-
1. Re: Clustered stateful EJBs
pferraro May 27, 2015 5:42 PM (in response to lhelander)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
lhelander May 27, 2015 6:31 PM (in response to pferraro)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
- Capture state that has a lifecycle of the session
- 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
pferraro May 27, 2015 10:49 PM (in response to lhelander)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
lhelander May 27, 2015 11:18 PM (in response to pferraro)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
lhelander May 28, 2015 1:27 AM (in response to lhelander)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
pferraro May 28, 2015 8:36 AM (in response to lhelander)Can you post the code in your servlet that references the SFSB?
-
7. Re: Clustered stateful EJBs
lhelander May 28, 2015 1:37 PM (in response to pferraro)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
lhelander Jun 1, 2015 8:31 AM (in response to pferraro)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
lhelander Jun 3, 2015 1:20 AM (in response to lhelander)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
pferraro Jun 4, 2015 3:08 PM (in response to lhelander)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
lhelander Jun 4, 2015 3:42 PM (in response to pferraro)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
lhelander Jun 4, 2015 3:44 PM (in response to lhelander)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
lhelander Jun 7, 2015 6:16 AM (in response to lhelander)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?