11 Replies Latest reply on Jun 7, 2013 12:56 PM by jfuerth

    Errai Newbie - Quick Q

    thewebman2002

      Hi All,

       

      Errai seems like a really neat project. We just started working with it to port some of the GWT stack to errai.

       

      With the Bus (I guess the most famous part of Errai), I had an extremely quick Q. We still havent brought in CDI.. will do at some point after some ofthe core refactoring is done.

       

      Here goes the Q:

       

      For our sample app - I have a client side code (bus listenining snippet below):

       

      bus = ErraiBus.get();

       

      public void onModuleLoad() {

           bus = ErraiBus.get();

           initEBus();

       

           /.... then create the login panel with login button etc./

      }

       



         private void initEBus() {




      // TODO Auto-generated method stub



         System.out.println("initbus");




      bus.subscribe("Login", new MessageCallback() {






       






      public void callback(CommandMessage message) {




           /**




            * When a message arrives, extract the "text" field and




            * do something with it




            */






      System.out.println( message.get(String.class, "text"));




          }

       






      @Override





      public void callback(Message message) {






      // TODO Auto-generated method stub












      }






      });



      }

       

       

      On the Server Side (the call happens via RPC when a login button is pressed):

       

      private MessageBus msgBus = ErraiBus.get();

       


      @Override

      public Boolean doLogin(String username, String password) {


      // TODO Auto-generated method stub


      System.out.println("dispatching message");


      msgBus.send(MessageBuilder


          .createMessage()


          .toSubject("Login")


          .with("text", "Hi There").done().getMessage());


      return true;

      }

       

       

      When I run this, the server seems to be sending a message (I can only assume as there are no errors and the method does return true successfully), but the client never gets the message and doesnt print anything.

       

      I am assuming we are doing something extremly silly or missing something .... Can anyone tell me what we are doing wrong?

       

      Thanks much,

       

      Mike,

        • 1. Re: Errai Newbie - Quick Q
          jfuerth

          Hi Mike,

           

          I see a few things you might want to look at:

           

          First, you're mixing levels of abstraction in this example. Although it's totally fine to write an app that uses the low-level MessageBuilder and MessageCallback API in some places, Errai RPC in other places, and CDI events and observers in other places yet, I can't recommend mixing and matching these levels of abstraction within the same use case (for example, responding to an RPC call using the MessageBuilder).

           

          So in the case, above, I'd recommend that you choose either to return a value from your Errai RPC method, and receive that value in a RemoteCallback on the client:

           

          @Inject Caller<MyRpcService> myRpcService;
          
          public void doRpcCall() {
            myRpcService.call(new RemoteCallback<String>() {
              @Override public void callback(String response) {
                System.out.println("Got RPC response from server: " + response);
              }
            }).doLogin(username, password);
          }
          

           

          Or if you do prefer to work directly with the low-level bus API, do something like this on the server:

           

          @Service
          public void doLogin(Message message) {
          
              // dig out message parts for username and password ...
          
              MessageBuilder.createConversation(message)
                  .toSubject("Login")
                  .with("text", "Hello, World!")
                  .done().reply();
          }
          

           

          One final note: in your example server-side code, the message you're sending would be a broadcast message to the Login topic. Note that the code above sends a private reply just to the one client who invoked your service method.

           

          Hope that helps!

          Jonathan

          1 of 1 people found this helpful
          • 2. Re: Errai Newbie - Quick Q
            thewebman2002

            Thanks Jonathan - I appreciate the feedback on not mixing - makes sense. We will probably go down the route of the low-level bus api to start with as that would help us refactor in pieces amongst several developers. If I can bug you with a follow-up question.

             

            When you put the @Service annotation, what jars do I need to bring in and do I need to initialize any provider or injector for it to be picked up?

             

            Thanks much,

             

            Mike

            • 3. Re: Errai Newbie - Quick Q
              jfuerth

              The helloworld bus demo has this dependency tree:

               

              org.jboss.errai:errai-bus-demos-helloworld:war:2.3.1.Final
              +- org.jboss.errai:errai-common:jar:2.3.1.Final:compile
              |  \- org.jboss.errai.reflections:reflections:jar:2.3.1.Final:compile
              |     \- dom4j:dom4j:jar:1.6.1:compile
              |        \- xml-apis:xml-apis:jar:1.0.b2:compile
              +- org.jboss.errai:errai-bus:jar:2.3.1.Final:compile
              |  +- org.jboss.errai:errai-config:jar:2.3.1.Final:compile
              |  +- org.jboss.errai:errai-marshalling:jar:2.3.1.Final:compile
              |  |  \- org.jboss.errai:errai-codegen-gwt:jar:2.3.1.Final:compile
              |  +- javax.validation:validation-api:jar:1.0.0.GA:compile
              |  +- javax.validation:validation-api:jar:sources:1.0.0.GA:compile
              |  +- com.google.inject:guice:jar:3.0:compile
              |  |  \- aopalliance:aopalliance:jar:1.0:compile
              |  +- javax.inject:javax.inject:jar:1:compile
              |  +- org.mvel:mvel2:jar:2.1.Beta8:compile
              |  +- org.slf4j:slf4j-api:jar:1.6.1:compile
              |  +- org.javassist:javassist:jar:3.15.0-GA:compile
              |  +- org.jboss.errai.io.netty:netty:jar:4.0.0.Alpha1.errai.r1:compile
              |  \- com.google.guava:guava:jar:12.0:compile
              |     \- com.google.code.findbugs:jsr305:jar:1.3.9:compile
              +- org.jboss.errai:errai-ioc:jar:2.3.1.Final:compile
              |  +- org.jboss.errai:errai-codegen:jar:2.3.1.Final:compile
              |  +- org.jboss.errai:errai-javax-enterprise:jar:2.3.1.Final:compile
              |  +- javax.annotation:jsr250-api:jar:1.0:compile
              |  \- javax.enterprise:cdi-api:jar:1.0-SP4:compile
              |     \- org.jboss.spec.javax.interceptor:jboss-interceptors-api_1.1_spec:jar:1.0.0.Beta1:compile
              +- org.jboss.errai:errai-tools:jar:2.3.1.Final:compile
              |  \- hsqldb:hsqldb:jar:1.8.0.7:compile
              +- com.google.gwt:gwt-user:jar:2.5.1:provided
              |  \- org.json:json:jar:20090211:provided
              \- junit:junit:jar:4.8.1:test (scope not updated to compile)
              

               

              For those not familiar with Maven scopes, "provided" means that jar is on the classpath at compile time and when running GWT Dev Mode, but the "provided" jars are not bundled in the .war file that's deployed to the server.

               

              "compile" scope, somewhat counterintuitively, is always on the classpath and is deployed to the server.

               

              "test" scope is on the classpath only when compiling and running the tests.

               

              On the GWT side, you need to depend on ErraiBus and Errai's IOC container:

               

              <module rename-to="MyApp">
                  <inherits name="org.jboss.errai.bus.ErraiBus"/>
                  <inherits name="org.jboss.errai.ioc.Container"/>
              </module>
              

               

              The dependency structure for Errai 3.0 is a bit different (we fixed some dependencies we felt were pointing the wrong way). Let me know if you're using the 3.0 snapshots and I can produce that dependency tree for you as it stands today.

              1 of 1 people found this helpful
              • 4. Re: Errai Newbie - Quick Q
                thewebman2002

                Great Jonathan!! This will definitely help in our process. We use maven extensively and that should help us as well. We are not using 3.0 snapshot at the moment.

                 

                And I am assuming that the developer doesnt need to specifically involve guice and bind - that should be taken care of by the Errai IOC etc.

                 

                Btw - can I ask a fundamental question: how does Errai ensure that the Client Bus talks to the correct Server Bus (I believe it uses socket underneath - could be mistaken; but since it has been encapsulated, what things need to be taken care of from a developer standpoint to ensure that the client bus and the server bus can communicate with each other).

                 

                Thanks much!

                 

                Mike

                • 5. Re: Errai Newbie - Quick Q
                  thewebman2002

                  Hey Jonathan - another quick Q:

                   

                  When I am using and trying the HelloWorldService, I get an exception:

                   

                  no subscribers to deliver to for subject: HelloWorldService -- Additional Details: none

                  org.jboss.errai.bus.client.api.base.NoSubscribersToDeliverTo: no subscribers to deliver to for subject: HelloWorldService
                    org.jboss.errai.bus.client.framework.ClientMessageBusImpl.send(ClientMessageBusImpl.java:485)
                    org.jboss.errai.bus.client.api.base.CommandMessage.sendNowWith(CommandMessage.java:328)
                    org.jboss.errai.bus.client.api.base.DefaultMessageBuilder$1.sendNowWith(DefaultMessageBuilder.java:78)

                   

                  Do I need to follow any structural pattern - such as it has to reside in certain package etc.

                   

                  in the client package (com.mytest.client) I have the EBus class (a client class with @Entrypoint annotation):

                   

                  import javax.annotation.PostConstruct;

                  import javax.inject.Inject;

                   

                  import org.jboss.errai.bus.client.api.Message;

                  import org.jboss.errai.bus.client.api.MessageCallback;

                  import org.jboss.errai.bus.client.api.base.MessageBuilder;

                  import org.jboss.errai.bus.client.framework.MessageBus;

                  import org.jboss.errai.common.client.protocols.MessageParts;

                  import org.jboss.errai.ioc.client.api.EntryPoint;

                   

                  import com.google.gwt.event.dom.client.ClickEvent;

                  import com.google.gwt.event.dom.client.ClickHandler;

                  import com.google.gwt.user.client.ui.Button;

                  import com.google.gwt.user.client.ui.HorizontalPanel;

                  import com.google.gwt.user.client.ui.Label;

                  import com.google.gwt.user.client.ui.RootPanel;

                  import com.google.gwt.user.client.ui.TextBox;

                   

                   

                  @EntryPoint

                  public class EBus {

                   

                        @Inject

                        private MessageBus bus;

                   

                        private final Label responseLabel = new Label();

                        private final Button button = new Button("Send");

                        private final TextBox message = new TextBox();

                   

                        @PostConstruct

                        public void buildUI() {

                   

                            button.addClickHandler(new ClickHandler() {

                                public void onClick(ClickEvent event) {

                                    System.out.println("Handling click event!");

                                    sendMessage();

                                }

                            });

                   

                            HorizontalPanel horizontalPanel = new HorizontalPanel();

                            horizontalPanel.add(message);

                            horizontalPanel.add(button);

                            horizontalPanel.add(responseLabel);

                   

                            RootPanel.get().add(horizontalPanel);

                           

                            System.out.println("UI Constructed!");

                        }

                       

                       

                       

                        void sendMessage() {

                              MessageBuilder.createMessage()

                                  .toSubject("HelloWorldService")

                                  .withValue("Hello, There!")

                                  .done()

                                  .repliesTo(new MessageCallback() {

                                    public void callback(Message message) {

                                      System.out.println("Got a Response!");

                                      responseLabel.setText("Message from Server: " + message.get(String.class, MessageParts.Value));

                                    }

                                  })

                                  .sendNowWith(bus);

                            }     

                   

                  }

                   

                  in the server package (com.mytest.server), I have a HelloWorldService (with @Service annotation) :

                   

                  import java.util.Date;

                   

                  import org.jboss.errai.bus.client.api.Message;

                  import org.jboss.errai.bus.client.api.MessageCallback;

                  import org.jboss.errai.bus.client.api.base.MessageBuilder;

                  import org.jboss.errai.bus.server.annotations.Service;

                   

                  @Service

                  public class HelloWorldService implements MessageCallback {

                   

                    public void callback(Message message) {

                      MessageBuilder.createConversation(message)

                        .subjectProvided()

                        .withValue("Hello, World! The server's time is now " + new Date() + ".")

                        .done().reply();

                    }

                  }

                   

                  Based on the documentation, I have placed the ErraiApp and ErraiService.properties file in src/main/resources.

                   

                  Do I need to do anything else?

                   

                  Thanks,

                   

                  Mike.

                  • 6. Re: Errai Newbie - Quick Q
                    jfuerth

                    And I am assuming that the developer doesnt need to specifically involve guice and bind - that should be taken care of by the Errai IOC etc.

                     

                    That's right, server-side Errai will find your services on its own. You don't have to write any code to do this, but you do have to be somewhat careful to configure it properly. The mechanism to use depends on whether or not Errai's CDI integration jar is deployed on the server. You configure this setting in ErraiService.properties. From the reference guide:

                     

                    • errai.auto_discover_services A boolean indicating whether or not the Errai bootstrapper should automatically scan for services. This property must be set to true if and only if Errai CDI is not on the classpath. The default value is false.

                     

                     

                    Btw - can I ask a fundamental question: how does Errai ensure that the Client Bus talks to the correct Server Bus (I believe it uses socket underneath - could be mistaken; but since it has been encapsulated, what things need to be taken care of from a developer standpoint to ensure that the client bus and the server bus can communicate with each other).

                     

                    The client bus automatically attempts to establish an HTTP connection to the server bus when the ErraiBus module loads. It also handles upgrade to Server-Sent Events and Web Sockets if the browser is capable and the server reports it is also capable.

                     

                    -Jonathan

                    • 7. Re: Errai Newbie - Quick Q
                      jfuerth

                      Based on the documentation, I have placed the ErraiApp and ErraiService.properties file in src/main/resources.

                       

                      Do I need to do anything else?

                       

                      If you're not using Errai's CDI integration on the server side, you will need to set errai.auto_discover_services=true in the ErraiService.properties file. Otherwise, the code you've posted looks good to me on first inspection.

                       

                      -Jonathan

                      • 8. Re: Errai Newbie - Quick Q
                        thewebman2002

                        Figured it Jonathan - since I am not using CDI, I need to explicitly set auto-discover services in the web.xml. Hope that helps anyone stuck ...

                         

                        thanks!

                         

                        Mike.

                        • 9. Re: Errai Newbie - Quick Q
                          thewebman2002

                          Hey Jonathan - The HelloWorldService is now working and sending back a response to the conversation message.

                           

                          When I try not to respond as a conversation message but send it to a specific subject (as you mentioned - broadcast), it doesnt appear to be coming to the client.... my code below...

                           

                          I have

                           

                          initListener() mehod in my

                           

                          @PostConstruct

                                public void buildUI() method

                           

                          and in initListener ...

                          bus.subscribe("HelloWorldClient", new MessageCallback() {

                                               public void callback(CommandMessage message) {

                                                  String messageText = message.get(String.class, "text");

                                                  System.out.println("Message Text: " + messageText);

                                                }

                           

                                              @Override

                                              public void callback(Message message) {

                                                  // TODO Auto-generated method stub

                                                  String messageText = message.get(String.class, "text");

                                                  System.out.println("Message Text: " + messageText);

                                              }

                                           });

                           

                           

                          The button click still sends a message to the server which I receive .. it prints that it received a message...

                           

                          @Inject
                          MessageBus msgBus;

                            public void callback(Message message) {

                           

                            System.out.println("Received message from client");
                          MessageBuilder.createMessage()
                            .toSubject("HelloWorldClient") // (1)
                            .signalling()              // (2)
                            .with("text", "Hi There - This is from HelloWorldService")  // (3)
                            .noErrorHandling()         // (4)
                            .sendNowWith(msgBus);

                            }

                           

                          but the client never receives the message - do I need to do something differently than whats done for the conversational interaction - I didnt see any difference in the docs.

                           

                          Thanks much!

                           

                          Mike

                          • 10. Re: Errai Newbie - Quick Q
                            thewebman2002

                            Hey Jonathan etal - I think I figured it. For some reason, when you are running errai via eclipse, things are not cleaned up properly - I could see some processes running that were probably tied to the original conversation context. The moment I cleaned up everything, restarted eclipse and killed zombie errai java processes, it picked up the boradcast callback and the client was able to get the new message.

                             

                            Hope that helps anyone else who may have faced this issue.

                             

                            Thanks,

                             

                            Mike

                            • 11. Re: Errai Newbie - Quick Q
                              jfuerth

                              A followup for anyone who runs into a similar problem with Errai 2.3.x or the 3.0 snapshots before today: we've just found and fixed a slippery bug in the way the bus expires abandoned queues on the server side. If you observe an HTTP "401 Unauthorized" error in the browser's network monitor right around the time the client misses a broadcast from the server, then you have been affected by this bug.

                               

                              The fix is in the 3.0.0.20130604-M1 release ("3.0 Milestone 1") that came out yesterday.

                               

                              -Jonathan