1 2 Previous Next 21 Replies Latest reply: Sep 4, 2010 3:44 PM by Marcin Misiewicz RSS

Calling RPC doesn't work

Marcin Misiewicz Newbie

Hi

 

I've got another problem with errai, right know I wanted to test calling rpc in errai way, but unfortunately with no luck. Here is

the simple class which I'm using to test rpc communication :

 

 

@EntryPoint
public class Test {
private MessageBus bus;
/**
* @param bus
*/
@Inject
public Test(MessageBus bus) {
  super();
  this.bus = bus;
}
/*
* (non-Javadoc)
*
* @see com.google.gwt.core.client.EntryPoint#onModuleLoad()
*/
@PostConstruct
public void init() {
  final VerticalPanel panel = new VerticalPanel();
  final TextBox inputBox = new TextBox();
  final Button sendBroadcast = new Button("Broadcast!");
  sendBroadcast.addClickHandler(new ClickHandler() {
    public void onClick(ClickEvent event) {
     System.out.println("click");
     MessageBuilder.createMessage().toSubject("BroadcastService")
     .signalling().with("BroadcastText", inputBox.getText())
     .noErrorHandling().sendNowWith(bus);
    }
   });
     final Label broadcastReceive = new Label();
     bus.subscribe("BroadcastReceiver", new MessageCallback() {
       @Override
        public void callback(Message message) {
        String broadcastText = message.get(String.class,"BroadcastText");
        broadcastReceive.setText(broadcastText);
      }
   });
   panel.add(inputBox);
   panel.add(sendBroadcast);
   panel.add(broadcastReceive);
   final Button callRpcButton = new Button("Call rpc");
   final Label callRpcLabel = new Label();
   final UserAssignmentManager call;
   try {
       call = MessageBuilder.createCall(new RemoteCallback<Boolean>() {
             @Override
             public void callback(Boolean response) {
                if (response) {
                   callRpcLabel.setText("Yes");
                } else {
                  callRpcLabel.setText("No");
                }
             }
        }, UserAssignmentManager.class);
       callRpcButton.addClickHandler(new ClickHandler() {
             @Override
             public void onClick(ClickEvent event) {
                 call.isFirstRun();
             }
        });
     } catch (RuntimeException e) {
        e.printStackTrace();
     }
     panel.add(callRpcButton);
     panel.add(callRpcLabel);
     RootPanel.get().add(panel);
   }
}

 

As you can see this is very simple scenario, first to check if errai works at all I'm sending and receiving broadcast message and it works perfectly.

CDI integration on the server side works as expected.

 

Then I try to test rpc calls and unfortunately I get following RuntimeException with message :

No service definition for: java.lang.Class

 

This exception comes from line 52 of the  AbstractRemoteCallBuilder class.

 

This message should be :

No service definition for: pl.scentia.smartoffice.test.UserAssignmentManager

 

But that's not the case, the real problem for me is that it can't obtain proxy object for my class. Everything looks like in the rpc example included in the errai-weld distribution.

UserAssignmentManager has @Remote annotation, the service is properly configured, on the server side everything seems to be all right.

 

Errai and errai-weld has been build today, so its pretty fresh. The main difference betwen my project and errai-weld example is that I don't have errai-jboss.sar I have errai packaged inside ear. But as I sad sending messages works as expected.

 

Do you have any suggestions what am I doing wrong ?

 

Regards

Marcin Misiewicz

  • 1. Re: Calling RPC doesn't work
    Mike Brock Master

    I will look into this.  This stuff is still heavily under development, so I'm not surprised it's not perfect.

  • 2. Re: Calling RPC doesn't work
    Heiko Braun Master

    Can you post the server log when the EAR is deployed? There should be something like "Discovered RPC Endpoint ..."

    Maybr the UserAssignmentmanager interface and the implentation as well?

  • 3. Re: Calling RPC doesn't work
    Marcin Misiewicz Newbie

         Hi

     

    Here is the relevant part of the server log :

     

    12:56:27,201 INFO  [service] Removing bootstrap log handlers
    12:56:31,029 INFO  [org.jboss.errai.cdi.server.CDIExtensionPoints] Register MessageCallback: @Service @ApplicationScoped pl.scentia.smartoffice.server.services.BroadcastService
    12:56:31,068 INFO  [org.jboss.errai.cdi.server.CDIExtensionPoints] Register RPC Endpoint: @Service @ApplicationScoped pl.scentia.smartoffice.server.services.UserAssignmentService(interface pl.scentia.smartoffice.test.UserAssignmentManager)
    12:56:31,446 INFO  [org.jboss.web.tomcat.service.deployers.TomcatDeployment] deploy, ctxPath=/smartoffice
    12:56:31,973 INFO  [pl.scentia.smartoffice.server.guice.DispatchServletModule] Binding /login/dispatch to DispatchServlet.class
    12:56:31,985 INFO  [pl.scentia.smartoffice.server.guice.DispatchServletModule] Binding /basic/dispatch to DispatchServlet.class
    12:56:31,986 INFO  [pl.scentia.smartoffice.server.guice.DispatchServletModule] Binding /pro/dispatch to DispatchServlet.class
    12:56:31,986 INFO  [pl.scentia.smartoffice.server.guice.DispatchServletModule] Binding /login/gwt-log to RemoteLoggerServiceImpl.class
    12:56:31,987 INFO  [pl.scentia.smartoffice.server.guice.DispatchServletModule] Binding /basic/gwt-log to RemoteLoggerServiceImpl.class
    12:56:31,987 INFO  [pl.scentia.smartoffice.server.guice.DispatchServletModule] Binding /pro/gwt-log to RemoteLoggerServiceImpl.class
    12:56:32,138 INFO  [net.sf.gilead.core.PersistentBeanManager] Using persistence util : net.sf.gilead.core.hibernate.jboss.HibernateJBossUtil@1646aeb
    12:56:32,138 INFO  [net.sf.gilead.core.PersistentBeanManager] Using Proxy Store : net.sf.gilead.core.store.stateful.HttpSessionProxyStore@1a77f87
    12:56:32,139 INFO  [net.sf.gilead.core.PersistentBeanManager] Using class mapper : null
    12:56:32,345 INFO  [org.jboss.resteasy.cdi.CdiInjectorFactory] Found BeanManager at java:comp/BeanManager
    12:56:32,867 INFO  [org.apache.coyote.http11.Http11Protocol] Starting Coyote HTTP/1.1 on http-127.0.0.1-8080
    12:56:32,878 INFO  [org.apache.coyote.ajp.AjpProtocol] Starting Coyote AJP/1.3 on ajp-127.0.0.1-8009
    12:56:32,884 INFO  [org.jboss.bootstrap.impl.base.server.AbstractServer] JBossAS [6.0.0.20100721-M4 "Neo"] Started in 1m:45s:443ms
    
    
    

     

    Don't bother with guice and gilead logs we are in the process of the migration.

     

    As you can see RPC endpoint has been registered successfully obviously I can see it in the bus monitor tool.

    In my opinion the problem lies in the client part. After debugging I have found out that remoteProxies object in the RemoteServiceProxyFactory is empty. That's way It can't find the proxy.

     

    Here is the UserAssignmentBeanManager interface :

     

    package pl.scentia.smartoffice.test;
    
    import org.jboss.errai.bus.server.annotations.Remote;
    
    @Remote
    public interface UserAssignmentManager {
         Boolean isFirstRun();
    }
    
    

     

    And UserAssignmentService :

     

    package pl.scentia.smartoffice.server.services;
    
    import java.util.logging.Logger;
    
    import javax.enterprise.context.ApplicationScoped;
    import javax.inject.Inject;
    
    import org.jboss.errai.bus.server.annotations.Service;
    
    import pl.scentia.smartoffice.business.client.interfaces.UserAssignmentBeanLocal;
    import pl.scentia.smartoffice.test.UserAssignmentManager;
    
    @Service
    @ApplicationScoped
    public class UserAssignmentService implements UserAssignmentManager {
    
         @Inject
         private UserAssignmentBeanLocal userBean;
         @Inject
         protected Logger log;
         
         /* (non-Javadoc)
          * @see pl.scentia.smartoffice.shared.server.rpc.UserAssignmentManager#isFirstRun()
          */
         @Override
         public Boolean isFirstRun() {
              log.fine("Calling isFirstRun");
              return userBean.isFirstRun();
         }     
         
    }
    
  • 4. Re: Calling RPC doesn't work
    Marcin Misiewicz Newbie

    Well I guess I have found the solution to my problems.

     

    As I wrote before I thought that remoteProxies in class RemoteServiceProxyFactory is empty and it really is when I was calling MessageBuilder.createCall() method.

    So I thought that there is something wrong with my deployment but I wanted to find out when the problem lies, so I put the breakpoint on

    AbstractRemoteCallBuilder.setProxyFactory(ProxyProvider provider) method to see what happens there and whether this method is called at all.

     

    Suprisingly this method is called but after I call messagebuilder.createCall() and when its called it holds the reference to my proxy in proxyProvider so I think that problem lies no in my deployment but the MessageBuilder or MessageBus initialization and runtime conditions. When I call MessageBuilder.createCall after a while, everything is ok and calling rpc service works as expected.

     

    Here the code snippet which shows this scenario.

    When the application starts Call Rpc button is disabled, so you have to first enable this button by clicking Set rpc.

    In the click handler of this button I obtain the UserAssignmentManager object from MessageBuilder (this time without exceptions), set the click handler on Call rpc and finally enabling this Call rpc button.

    After that clicking on Call rpc works perfectly.

     

    @EntryPoint
    public class Test {
     
      private MessageBus bus;
      private UserAssignmentManager call;
     
      private final Button setRpc = new Button("Set rpc");
      private final Button callRpcButton = new Button("Call rpc");
      private final Label callRpcLabel = new Label();
     
      /**
      * @param bus
      */
      @Inject
      public Test(MessageBus bus) {
        super();
        this.bus = bus;
      }
     
     
      @PostConstruct
      public void init() {
     
     
        final VerticalPanel panel = new VerticalPanel();
     
     
        setRpc.addClickHandler(new ClickHandler() {
     
          @Override
            public void onClick(ClickEvent event) {
              setCallRpc();
     
            }
        });
     
        callRpcButton.setEnabled(false);
     
        panel.add(setRpc);
        panel.add(callRpcButton);
        panel.add(callRpcLabel);
     
     
        RootPanel.get().add(panel);
      }
     
     
      private void setCallRpc() {
        try {
          call = MessageBuilder.createCall(new RemoteCallback<Boolean>() {
     
            @Override
            public void callback(Boolean response) {
              if (response) {
                callRpcLabel.setText("Yes");
              } else {
                callRpcLabel.setText("No");
              }
            }
          }, UserAssignmentManager.class);
     
     
          callRpcButton.addClickHandler(new ClickHandler() {
     
            @Override
            public void onClick(ClickEvent event) {
              call.isFirstRun();
            }
          });
     
          callRpcButton.setEnabled(true);
        } catch (RuntimeException e) {
          e.printStackTrace();
        }
      }
    }
    
    

     

    I hope that this explanation will help you and of course I'm waiting for workaround because during the startup of my application I have to call rpc service, before user interface is showed to the user. I was trying to use DeferredCommand to call this service but it does not helps.

     

    Regards

    Marcin Misiewicz

  • 5. Re: Calling RPC doesn't work
    Heiko Braun Master

    Sounds like you depend on the initialization of the client message bus. In that case you can register a post-init-task:

     

    ClientMessageBus.addPostInitTask(Runnable run);

     

     

    Hope that helps.

  • 6. Re: Calling RPC doesn't work
    Marcin Misiewicz Newbie

    Hi

     

    Thank you for the quick response. It really works except addPostInitTask is not a static method of this class but when I map bus instance to ClientMessageBus it works.

     

    Thank you very much.

     

    So I have got this problems because I was calling rpc before the bus initialization ? Am I right, if yes I guess you should mention somewhere in docs about this.

     

    Anyway, thanks once again.

  • 7. Re: Calling RPC doesn't work
    Mike Brock Master

    This is almost certainly a feature of hosted mode. I'm guessing you wouldn't see the problem in production mode, because of the way the GWT defers compilation aggressively to improve the start-up time of the development mode runtime, you run into issues where the initialization of components don't occur until you actually access them.

     

    This is definitely a papercut we need to fix.

  • 8. Re: Calling RPC doesn't work
    Mike Brock Master

    So I can confirm that this has to do with the way GWT hosted model defers compilation.  I believe the best solution is not to use addPostInitTask() but rather to simply fully inline the calls like so:

     

     

    callRpcButton.addClickHandler(new ClickHandler() {
    
      @Override
      public void onClick(ClickEvent event) {
        MessageBuilder.createCall(new RemoteCallback<Boolean>() {
            @Override
            public void callback(Boolean response) {
              if (response) {
                callRpcLabel.setText("Yes");
              } else {
                callRpcLabel.setText("No");
              }
            }
          }, UserAssignmentManager.class).isFirstRun();
      }
    });
       
    
    
    

     

    It may not look as pretty, but this will avoid the issue you're running into in hosted mode -- negating the need to sloppily use a post-init task.  The way you're doing it now is making GWT think it has a late-binding opportunity.  But the GWT compiler is not smart enough to know that Errai has factory initialization code to be run prior to that call.  Writing your code like this will force the factory initialization code to be compiled and executed before the call.

  • 9. Re: Calling RPC doesn't work
    Mike Brock Master

    So just as a follow-up, I'll offer a tip -- that is good in any Java programming -- that will help avoid falling victim to GWT's aggressive deferred compile: Do not use expose components as class members unless absolutely necessary.

     

    Example: do not declare a Button component as a class field unless it really needs to be referenced/shared throughout the class.  If it's entire configuration/initialization can be done in a single local scope, it should be:

     

    A few examples.

     

    BAD:

    @EntryPoint
    public class MyClass {
       private SimplePanel panel;
       private Button myButton;
        ...
       @PostConstruct
       public void init() {
           panel = new SimplePanel();
           myButton = new Button("Hello!");
           panel.add(myButton);
     
           RootPanel.get().add(panel);
        }
    }
    
    
    

     

    There's really no need to expose panel or myButton.  No other components make use of it.  And if there's any deferred bindings referenced in any click handler, with a framework like Errai, you may run into uninitialized state problems when testing in development mode.  Or even in the browser if there is any code-splitting done by the compiler.

     

     

    GOOD:

     

    @EntryPoint
    public class MyClass {
        ...
    
        @PostConstruct
        public void init() {
            SimplePanel panel = new SimplePanel();
            Button myButton = new Button("Hello!");
    
            panel.add(myButton);
    
            RootPanel.get().add(panel);
        }
    }
    
    

    We make panel and myButton locals, so their value does not escape.  The GWT compiler cannot safely codesplit within a local scope -- and it doesn't.  It can only code-split between calls.  By writing our code this way, we avoid suffering any side-effects like the one you're experiencing. 

     

    This is of course encapsulation 101, but it's more important to follow, because you need to sort of think about the optimizing GWT compiler as a client to the code.  If certain values escape, the compiler might decide it's a good place to split the code.  But since Errai components bootstrap in separate, parallel modules, the GWT compiler cannot really determine whether or not it's safe to carry those optimizations out, and it may go ahead and do them -- as is happening with you.

  • 10. Re: Calling RPC doesn't work
    Marcin Misiewicz Newbie

     

    Thank you for that tips.

    I didn't realize till now that coding in this way could make some problem with gwt code splitting.

    But I must say that basically I follow the guidelines you have described, the code snippet I have included it's simple test case which I wrote in a hurry and didn't care a lot about beaty of the code.

     

    You have mention that this problem occurs only in hosted mode and I must disagree. I have been testing in the development mode and after compilation with and without -draftCompile flag. In every case I get the same exception.

     

    Once again thank you for the very detailed explanation.

     

    Regards

    Marcin Misiewicz

  • 11. Re: Calling RPC doesn't work
    Marcin Misiewicz Newbie

    I'm still playing with this Test sample.

     

    I followed your advice and I moved the variables to the init method, but in that case I still get the exception. Right know I also moved calling rpc service to the button callback. And of course it's not working in both modes hosted and production, so it's not related only to deferred binding during application startup in the hosted mode.

     

    Suprisingly when I remove subscribscription to the bus it starts working.

    This sample app does not working :

     

    @EntryPoint
    public class Test {
    
         private MessageBus bus;
              
         /**
          * @param bus
          */
         @Inject
         public Test(MessageBus bus) {
              super();
              this.bus = bus;
         }
    
         /*
          * (non-Javadoc)
          * 
          * @see com.google.gwt.core.client.EntryPoint#onModuleLoad()
          */
         @PostConstruct
         public void init() {
    
              final VerticalPanel panel = new VerticalPanel();
              
              final TextBox inputBox = new TextBox();
              final Button sendBroadcast = new Button("Broadcast!");
              
              final Button callRpcButton = new Button("Call rpc");
              final Label callRpcLabel = new Label();
    
              sendBroadcast.addClickHandler(new ClickHandler() {
                   public void onClick(ClickEvent event) {
                        /**
                         * Send a message to the BroadcastService with the contents of
                         * the inputBox as the "BroadcastText" field.
                         */
                        System.out.println("click");
                        MessageBuilder.createMessage().toSubject("BroadcastService")
                                  .signalling().with("BroadcastText", inputBox.getText())
                                  .noErrorHandling().sendNowWith(bus);
                   }
              });
    
              final Label broadcastReceive = new Label();
    
              /**
               * Declare a local service to receive messages on the subject
               * "BroadCastReceiver".
               */
              bus.subscribe("BroadcastReceiver", new MessageCallback() {
                   @Override
                   public void callback(Message message) {
                        /**
                         * When a message arrives, extract the "BroadcastText" field and
                         * update the broadcastReceive Label widget with the contents.
                         */
                        String broadcastText = message.get(String.class,
                                  "BroadcastText");
                        broadcastReceive.setText(broadcastText);
                   }
              });
    
              panel.add(inputBox);
              panel.add(sendBroadcast);
              panel.add(broadcastReceive);
    
              callRpcButton.addClickHandler(new ClickHandler() {
                   
                   @Override
                   public void onClick(ClickEvent event) {
                        MessageBuilder.createCall(new RemoteCallback<Boolean>() {
    
                             @Override
                             public void callback(Boolean response) {     
                                  if (response) {
                                       callRpcLabel.setText("Yes");
                                  } else {
                                       callRpcLabel.setText("No");
                                  }
                             }
                        }, UserAssignmentManager.class).isFirstRun();
                   }
              });
              panel.add(callRpcButton);
              panel.add(callRpcLabel);
    
              RootPanel.get().add(panel);
         }     
    }
    

     

    But when I remove subscription to the bus it starts working :

     

    @EntryPoint
    public class Test {
    
         private MessageBus bus;
              
         /**
          * @param bus
          */
         @Inject
         public Test(MessageBus bus) {
              super();
              this.bus = bus;
         }
    
         /*
          * (non-Javadoc)
          * 
          * @see com.google.gwt.core.client.EntryPoint#onModuleLoad()
          */
         @PostConstruct
         public void init() {
    
              final VerticalPanel panel = new VerticalPanel();
              
              final Button callRpcButton = new Button("Call rpc");
              final Label callRpcLabel = new Label();
    
              callRpcButton.addClickHandler(new ClickHandler() {
                   
                   @Override
                   public void onClick(ClickEvent event) {
                        MessageBuilder.createCall(new RemoteCallback<Boolean>() {
    
                             @Override
                             public void callback(Boolean response) {     
                                  if (response) {
                                       callRpcLabel.setText("Yes");
                                  } else {
                                       callRpcLabel.setText("No");
                                  }
                             }
                        }, UserAssignmentManager.class).isFirstRun();
                   }
              });
              panel.add(callRpcButton);
              panel.add(callRpcLabel);
    
              RootPanel.get().add(panel);
         }     
    }
     
    
    

     

    So, right know I'm bit confused (in one case it works, in the other not) and I guess to be sure that everything works perfectly I have to use ClientMessageBus.addPostInitTask, and I must stress that I checked it and when it's not working it doesn't work in both modes hosted and production.

     

    I think that you can easily replace the rpc service with your own, even simple hello world and you will get the same results, if not it wiil be very interesting why there are some differences.

     

    If it is interesting to you I'm compiling using goole eclipse plugin on the ubuntu 10.04, using sun jdk and gwt 2.0.4.

     

    My real application has three modules and in all this tree modules I have to make rpc call, then depends on the reponse I show the proper gui, so I call rpc service just after the application startup.

  • 12. Re: Calling RPC doesn't work
    Mike Brock Master

    Yeah, I'm trying once again to reproduce the problem as described. 

  • 13. Re: Calling RPC doesn't work
    Mike Brock Master

    Are you working off our trunk by any chance?

  • 14. Re: Calling RPC doesn't work
    Marcin Misiewicz Newbie

    Yes I'm compiling errai from trunk. i've tried to use errai m2 but I didn't worked with m4.

1 2 Previous Next