Chapter 33. Clustering and EJB Passivation

Web clustering and EJB passivation share a common solution in Seam, so they are addressed together. This chapter focuses on the programming model and how it is affected by the use of clustering and EJB passivation. You will learn how Seam passivates component and entity instances, how passivation relates to clustering, and how to enable passivation. You will also learn how to deploy a Seam application into a cluster and verify that HTTP session replication is working correctly.
First, we will take you through some background information on clustering and show you an example deployment of a Seam application to an EAP cluster.

33.1. Clustering

Clustering, also known as web clustering, lets an application run on two or more parallel servers (nodes), while providing a client with a uniform view of the application. Load is distributed across servers such that if one or more servers fail, the application can still be accessed through any surviving nodes. This is crucial for scalable enterprise applications, since performance and availability can be improved just by adding nodes. However, this also provokes the question of what happens to state held on a server that fails.
So far, you know that Seam provides state management by including additional scopes and governing the life cycles of stateful (scoped) components. But Seam's state management goes beyond creating, storing, and destroying instances. Seam tracks changes to JavaBean components and stores those changes at strategic points during a request so that changes can be restored when the request shifts to a secondary node in the cluster. Monitoring and replicating stateful EJB components is already handled by the EJB server; Seam state management provides the same feature for stateful JavaBeans.
In addition to monitoring JavaBean components, Seam ensures that managed entity instances (that is, JPA and Hibernate entities) are not attached during replication. Seam keeps a record of entities to be loaded, and loads them automatically on the secondary node. You must be using a Seam-managed persistence context to use this feature. More information is provided later in this chapter.
Next we will take you through how to program for clustering.

33.1.1. Programming for clustering

Any session- or conversation-scoped mutable JavaBean component to be used in a clustered environment must implement the org.jboss.seam.core.Mutable interface from the Seam API. As part of the contract, the component must maintain a dirtyflag event, which indicates whether the user has made changes to the form that must be saved. This event is reported and reset by the clearDirty() method, which is called to determine whether the component must be replicated. This lets you avoid using the Servlet API to add and remove the session attribute for every change to an object.
You must also make sure that all session- and conversation-scoped JavaBean components are serializable. All fields of a stateful component (EJB or JavaBean) must be serializable, unless marked transient or set to null in a @PrePassivate method. You can restore the value of a transient or nullified field in a @PostActivate method.
One area that can be problematic is in using List.subList to create a list, because the list created is not serializable. A similar situation can occur for a number of methods that create objects automatically. If you encounter a java.io.NotSerializableException, you can place a breakpoint on this exception and run the application server in debug mode to find the problem method.

Note

Clustering does not work with components that are hot-deployed. Further, you should avoid using hot-deployable components in non-development environments.

33.1.2. Deploying a Seam application to a EAP cluster with session replication

The following procedure has been validated against a seam-gen application and the Seam Booking example.
This section assumes that the IP addresses of the master and slave servers are 192.168.1.2 and 192.168.1.3, respectively. The mod_jk load balancer was not use intentionally to make it easier to validate that both nodes are responding to requests and interchanging sessions.
The following log messages were generated out of the deployment of a WAR application, vehicles.war, and its corresponding datasource, vehiclesDatasource. The Booking example fully supports this process, and you can find instructions about deploying it to a cluster in the examples/booking/readme.txt file.
These instructions use the farm deployment method, but you can also deploy the application normally and let the two servers negotiate a master/slave relationship based on start order.
All timestamps in this tutorial have been replaced with zeroes to reduce noise.

Note

If your nodes are on different machines that run Red Hat Enterprise Linux or Fedora, they may not acknowledge each other automatically. EAP clustering relies on the UDP (User Datagram Protocol) multi-casting provided by jGroups. The SELinux configuration that ships with Red Hat Enterprise Linux and Fedora blocks these packets by default. To allow the packets, modify the iptables rules (as root). The following commands apply to an IP address that matches 192.168.1.x:
  /sbin/iptables -I RH-Firewall-1-INPUT 5 -p udp -d 224.0.0.0/4 -j ACCEPT
  /sbin/iptables -I RH-Firewall-1-INPUT 9 -p udp -s 192.168.1.0/24 -j ACCEPT
  /sbin/iptables -I RH-Firewall-1-INPUT 10 -p tcp -s 192.168.1.0/24 -j ACCEPT
  /etc/init.d/iptables save

Note

If you are deploying an application with stateful session beans and HTTP Session replication to a EAP cluster, your stateful session bean classes must be annotated with @Clustered (from the JBoss EJB 3.0 annotation API) or marked as clustered in the jboss.xml descriptor. For details, see the Booking example.

33.1.3. Tutorial

  1. Create two instances of JBoss Enterprise Application Platform. (To do so, just extract the zip twice.)
    Deploy the JDBC driver to server/all/lib/ on both instances if you are not using HSQLDB.
  2. Add <distributable/> as the first child element in WEB-INF/web.xml.
  3. Set the distributable property on org.jboss.seam.core.init to true to enable the ManagedEntityInterceptor (that is, <core:init distributable="true"> in WEB-INF/components.xml).
  4. Ensure that you have two IP addresses available (two computers, two network cards, or two IP addresses bound to the same interface). We assume that these two IP addresses are 192.168.1.2 and 192.168.1.3.
  5. Start the master JBoss Enterprise Application Platform instance on the first IP:
      ./bin/run.sh -c all -b 192.168.1.2
    The log should report one cluster member and zero other members.
  6. Verify that the server/all/farm directory in the slave JBoss Enterprise Application Platform instance is empty.
  7. Start the slave JBoss Enterprise Application Platform instance on the second IP:
      ./bin/run.sh -c all -b 192.168.1.3
    The log should report two cluster members and one other member. It should also show the state being retrieved from the master instance.
  8. Deploy the -ds.xml to the server/all/farm of the master instance.
    In the log of the master instance you should see acknowledgement of this deployment. You should see a corresponding message acknowledging deployment to the slave instance.
  9. Deploy the application to the server/all/farm directory. You should see acknowledgement of deployment in the log of the master instance after normal application start messages have finished. The slave instance log should show a corresponding message acknowledging deployment. (You may need to wait up to three minutes for the deployed archive to be transferred.)
Your application is now running in a cluster with HTTP Session replication. The next step is to validate that the clustering is working correctly.

33.1.4. Validating the distributable services of an application running in a EAP cluster

Your application now starts successfully on two different JBoss Enterprise Application Platform servers, but it is important to validate that the two instances are exchanging HTTP Sessions correctly, so that the application continues to operate with the slave instance if the master instance is stopped.
First, browse to the application on the master instance to start the first HTTP Session. On the same instance, open the JBoss Enterprise Application Platform JMX Console and navigate to the following managed bean:
  • Category: jboss.cache
  • Entry: config=standard-session-cache,service=Cache
  • Method: printDetails()
Invoke the printDetails() method. This will present you with a tree of active HTTP Sessions. Verify that the session used by your browser corresponds to one of the sessions on the tree.
Next, switch to the slave instance and invoke the same method in the JMX Console. You should see an identical tree under this application's context path.
That these trees are identical proves that both servers claim to have identical sessions. Next, we must test that the data is serializing and unserializing correctly.
Sign in via the URL of the master instance. Then, construct a URL for the second instance by placing the ;jsessionid=XXXX immediately after the Servlet path and changing the IP address. (You should see that the session has carried over to the other instance.)
Now, kill the master instance and check that you can continue to use the application from the slave instance. Then, remove the deployed applications from the server/all/farm directory and restart the instance.
Change the IP in the URL back to that of the master instance, and browse to the new URL — you should see that the original session ID is still being used.
You can watch objects passivate and activate by creating a session- or conversation-scoped Seam component and implementing the appropriate life-cycle methods. You can use methods from the HttpSessionActivationListener interface (which is automatically registered on all non-EJB components):
public void sessionWillPassivate(HttpSessionEvent e);
public void sessionDidActivate(HttpSessionEvent e);
Alternatively, you can mark two public void methods (without arguments) with @PrePassivate and @PostActivate respectively. Remember that passivation will occur at the end of every request, while activation will occur when a node is called.
In order to make replication transparent, Seam automatically keeps track of objects that have been changed. All that you need to do is maintain a dirtyflag on your session- or conversation-scoped component, and Seam will handle JPA entity instances for you.