13 Replies Latest reply on Jun 7, 2015 6:16 AM by Lars-Erik Helander

    Clustered stateful EJBs

    Lars-Erik Helander Newbie

      I want to know if the following is possible to do:

       

           A wildfly cluster with a single app (WAR) deployed on all nodes in the cluster where the app:

       

           1) contains a stateful session EJB where the state is shared among all nodes in the cluster

            2) a servlet that calls the stateful EJB

      The call from the servlet may call the EJB on the same cluster node or some other node (preferably the same).

      What I am looking for is being able to have a cluster wide managed state that is accessible from the servlet at any node in the cluster.


      I have tried to find examples that does this but found none. If someone could provide (or point me to) an example you know are working, I would be most grateful.


      There are examples of how you access stateful cluster beans from a remote client, but in my case the client is not "remote" it exists within the cluster.


        • 1. Re: Clustered stateful EJBs
          Paul Ferraro Master

          This sounds like a very straight-forward use case.  You simply need to use a @SessionScoped @Stateful EJB (so that the SFSB reference is stored in the web session), use <distributable/> in your web.xml (to enabled replication of web session state), and make sure that you start your servers using an "ha" profile.

          • 2. Re: Clustered stateful EJBs
            Lars-Erik Helander Newbie

            Thank you very much for the feedback. This sounds simple and I will try it out? However I am a bit "confused" about the combination of CDI and EJB annotations on the same class, is this "legal"? I guess that arbitrary combinations would lead to unrealisic semantics, so how to know what combinations are valid?

             

            How would the SFSB know that it needs to save its state cluster wide ( in a replicated Infinispan cache) ?

             

            Maybe I have misunderstood the semantics of an SFSB? I can see basically two interpretations of SFSB

             

            1. Capture state that has a lifecycle of the session
            2. Capture state that has a lifecycle longer than the session, but provide access to that state from the session

             

            The solution you suggest sounds more to me like solving 1) while I would like to achieve 2).

            Any information that would clarify things for me would be most helpful.

            • 3. Re: Clustered stateful EJBs
              Paul Ferraro Master

              This is absolutely legal.  By default, EJBs do not have a defined scope.  Mixing CDI and EJB allows you to scope your EJBs.  @SessionScoped allows you to seamlessly reference the same SFSB instance per user across multiple requests - which makes sense if your entry point is a servlet.  This also handles the lifecycle of the SFSB for you - so it will remove itself when the HttpSession is destroyed.  You certainly don't have to bind the lifecycle of your SFSB to a web session - that was only a suggestion - am assumption I made based on your description of your use case, since typically one wouldn't need a SFSB to live longer than the lifecycle of a given session.  Your use case might be different.  You can just as easily use one of the other CDI scopes, or manage the scope manually.

               

              WF uses the passivationCapable attribute of the SFSB (see Stateful (Java(TM) EE 7 Specification APIs)) to indicate whether or not the state can be passivated (the default is passivationCapable=true).  The semantics of passivation are dictated by your server profile's capability.  The default configuration for SFSBs when using an "ha" profile is to replicate the state, whereas the default configuration for SFSBs when using a non-ha profile is to support traditional passivation only (i.e. we use a local Infinispan cache using a file-based cache store).  Replication, in this sense, is really a passivation mechanism (imagine passivating state to a shared database as another means for "replicating" state).  Setting passivationCapable=false will tell WF not to passivate (or replicate) a SFSB.

              • 4. Re: Clustered stateful EJBs
                Lars-Erik Helander Newbie

                This sounds very cool, thank you so much.

                So if I use a ha profile and ApplicationScoped SFSB I should probably get what I want, so I will test it.

                Would this work on AS7/EAP6 as well ? Or do you need WF for this to work?

                • 5. Re: Clustered stateful EJBs
                  Lars-Erik Helander Newbie

                  After some testing, still no success. It looks like the combining of Stateful and ApplicationScoped does not "work". It seems like the ApplicationScoped is "ignored", since each call for the bean creates a new instance (I am logging from the bean's PostConstruct method and it gets called upon each invocation). Without the Stateful annotation the PostConstruct method is called only once. I am running the application in two clustered nodes and I can see no sign of state being shared.

                  • 6. Re: Clustered stateful EJBs
                    Paul Ferraro Master

                    Can you post the code in your servlet that references the SFSB?

                    • 7. Re: Clustered stateful EJBs
                      Lars-Erik Helander Newbie

                      I will post the code later ( on the road without access to it at the moment). Meanwhile I would like to know if calling the SFSB from an MDB or EJB timeout method will "work", i.e. with clustered application scoped semantics maintained?

                      • 8. Re: Clustered stateful EJBs
                        Lars-Erik Helander Newbie

                             Here is my code. I am running an application with these files in two cluster nodes.

                         

                        When connecting to the two nodes (to the servlet) from the same browser instance (i.e. the same clustered web session) the state is shared between the two nodes as expected. But when I connect from another browser instance the state is not shared. In my view this means that the shared state is not application scoped but session scoped. Maybe I am doing something wrong in my code?

                         

                         

                        StateBean.java

                         

                        @javax.ejb.Stateful

                        @javax.enterprise.context.ApplicationScoped

                        public class StateBean implements java.io.Serializable  {

                         

                          static Logger logger = Logger.getLogger(StateBean.class.getName());

                         

                         

                          int counter;

                         

                         

                          public int count() {

                          logger.info("count() start "+counter);

                          counter++;

                          logger.info("count() end "+counter);

                          return counter;

                          }

                         

                         

                          @PostConstruct

                          private void onStartup() {

                          logger.info("Startup with COUNTER "+counter);

                          }

                         

                          @PreDestroy

                          private void onShutdown() {

                          logger.info("Shutdown with counter "+counter);

                          }

                         

                        }

                         

                         

                        SessionData.java

                         

                        @javax.enterprise.context.SessionScoped

                        public class SessionData implements java.io.Serializable {

                         

                          static Logger logger = Logger.getLogger(SessionData.class.getName());

                         

                         

                          @javax.ejb.EJB

                          StateBean stateBean;

                         

                         

                         

                         

                        public StateBean getBean() {

                          return stateBean;

                          }

                         

                         

                          @PostConstruct

                          private void onStartup() {

                          logger.info("Startup");

                          }

                         

                          @PreDestroy

                          private void onShutdown() {

                          logger.info("Shutdown");

                          }

                         

                        }

                         

                        TestServlet.java


                        @WebServlet(name="test", urlPatterns={"/test"} )

                             

                        public class TestServlet extends HttpServlet {

                         

                         

                          @javax.inject.Inject

                            SessionData sessionData;

                               

                         

                         

                            protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

                                PrintWriter out = response.getWriter();

                                out.println("Counter "+sessionData.getBean().count());

                                out.close();

                            }

                         

                            protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

                                 doGet(request,response);

                            }

                         

                        }

                        • 9. Re: Clustered stateful EJBs
                          Lars-Erik Helander Newbie

                          I got it to work by changing

                          @javax.ejb.EJB

                          StateBean stateBean;

                          to

                          @javax.inject.Inject

                          StateBean stateBean;

                           

                           

                           

                          This means that as long as I have a set of web sessions I can get these session to share a common state, but what if I need to access that shared state from an MDB or an EJB timer method? In these cases there are no web session to "bind" to, so how can I get access to the shared state held by the stateful EJB?

                          • 10. Re: Clustered stateful EJBs
                            Paul Ferraro Master

                            I don't understand what you mean by "there are no web session to 'bind' to".

                            According to the spec (emphasis mine):

                            "The application scope is active:

                            • during the service() method of any servlet in the web application, during the doFilter() method of any servlet filter and when the container calls any ServletContextListener, HttpSessionListener, AsyncListener or ServletRequestListener,
                            • during any Java EE web service invocation,
                            • during any remote method invocation of any EJB, during any asynchronous method invocation of any EJB, during any call to an EJB timeout method and during message delivery to any EJB message-driven bean,
                            • during any message delivery to a MessageListener for a JMS topic or queue obtained from the Java EE component environment, and
                            • when the disposer method or @PreDestroy callback of any bean with any normal scope other than @ApplicationScoped is called."
                            • 11. Re: Clustered stateful EJBs
                              Lars-Erik Helander Newbie

                              I was a bit wrong about it working . If the SFSB is annotated with SessionScoped the state is shared between calls to the same clustered web session, but if the SFSB is annotated the state is shared between all sessions on a single node but not between nodes.

                               

                              What I meant with "no web session to bind to" was for example if you have a Singleton EJB that fires timers. These timers would fire independent on any web sessions that may be present and the main question was if it was possible to then call an SFSB that shared its state between the nodes in the cluster ???

                              • 12. Re: Clustered stateful EJBs
                                Lars-Erik Helander Newbie

                                lhelander skrev:

                                 

                                I was a bit wrong about it working . If the SFSB is annotated with SessionScoped the state is shared between calls to the same clustered web session, but if the SFSB is annotated the state is shared between all sessions on a single node but not between nodes.

                                 

                                What I meant with "no web session to bind to" was for example if you have a Singleton EJB that fires timers. These timers would fire independent on any web sessions that may be present and the main question was if it was possible to then call an SFSB that shared its state between the nodes in the cluster ???

                                Correction:

                                but if the SFSB is annotated with ApplicationScoped, the state is shared between all sessions on a single node, but not between nodes

                                • 13. Re: Clustered stateful EJBs
                                  Lars-Erik Helander Newbie

                                  I have looked into the "ejb"-cache when having my SFSB annotated with ApplicationScoped and the cache contains two separate "beans", one from each node in the cluster. This looks a bit "suspicious" if ApplicationScoped should result in a shared state for the SFSB over the cluster.

                                   

                                  What should be the expected behavior in a clustered Wildfly system, that an application scoped SFSB would give the same or different state on different cluster nodes?