Please note: This tutorial has been written for JBoss AS 7 / EAP 6 If you want to read the latest articles about JBoss / WildFly HA please check the following: Configuring High Availability with WildFly JBoss Clustering a Web Application Clustering EJB 3 with JBoss AS
In this tutorial we will show how to achieve High Availability with your Enterprise Java Beans using a simple Stateful clustered EJB and a remote Client.
Clustering stateful session beans requires JBoss AS to manage the state information. The component that is in charge to manage state information is Infinispan, which by default uses Replication to keep the session synchronized between components, each time the state of a bean changes.
In this tutorial we will set up and test a standalone cluster made up of two nodes and we will deploy our Stateful EJB on both nodes, by simply dropping the artifact on the deployments folder. Here’s a picture of our cluster definition:
Now let’s code a Simple SFSB with a counter variable instance that will be used to keep track of the session:
package com.sample.ejb; import javax.annotation.PostConstruct; import javax.ejb.Remote; import javax.ejb.Stateful; import org.jboss.ejb3.annotation.Clustered; @Stateful @Clustered @Remote(SampleBeanRemote.class) public class SampleBeanRemoteImpl implements SampleBeanRemote { int counter=0; @PostConstruct public void init() { System.out.println("EJB inited!"); } @Override public int sum() { counter++; System.out.println("Value of the counter:"+counter); return counter; } }
And here’s the corresponding SampleBeanRemote interface:
package com.sample.ejb; public interface SampleBeanRemote { public int sum(); }
On the other hand, if you are using Maven to build your project, you need to add the following dependency in order to compile your Clustered EJB:
<dependency> <groupId>org.jboss.ejb3</groupId> <artifactId>jboss-ejb3-ext-api</artifactId> <version>2.0.0</version> </dependency>
Fine, now start your cluster with a cluster-aware configuration as in the following example:
NodeA
standalone -c standalone-ha.xml -Djboss.node.name=nodeA
NodeB
standalone -c standalone-ha.xml -Djboss.socket.binding.port-offset=200 -Djboss.node.name=nodeB
And deploy your EJB application to both nodes of your cluster (simply copy them in the deployments folder, if you are using a standalone cluster)
Now it’s time to code your client application which will invoke the sum() method and display the value for that field.
package com.sample.client; import javax.naming.*; import com.sample.ejb.SampleBeanRemote; import com.sample.ejb.SampleBeanRemoteImpl; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.*; public class RemoteEJBClient { public static void main(String[] args) throws Exception { testRemoteEJB(); } private static void testRemoteEJB() throws NamingException { final SampleBeanRemote ejb = lookupRemoteEJB(); int s = ejb.sum(); System.out.println("Value of Counter " +s); s = ejb.sum(); System.out.println("Value of Counter " +s); System.out.println("Shut down the pinned JBoss AS 7 node and press ENTER"); pressAKey(); s = ejb.sum(); System.out.println("Value of Counter " +s); } private static SampleBeanRemote lookupRemoteEJB() throws NamingException { final Hashtable jndiProperties = new Hashtable(); jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming"); final Context context = new InitialContext(jndiProperties); final String appName = ""; final String moduleName = "jboss-as-ejb-remote-app"; final String distinctName = ""; final String beanName = SampleBeanRemoteImpl.class.getSimpleName(); final String viewClassName = SampleBeanRemote.class.getName(); System.out.println("Looking EJB via JNDI "); System.out.println("ejb:" + appName + "/" + moduleName + "/" + distinctName + "/" + beanName + "!" + viewClassName); return (SampleBeanRemote) context.lookup("ejb:" + appName + "/" + moduleName + "/" + distinctName + "/" + beanName + "!" + viewClassName+"?stateful"); } // Compatible with Eclipse Environment public static void pressAKey() { InputStreamReader istream=null; BufferedReader bufRead=null; istream = new InputStreamReader(System.in) ; bufRead = new BufferedReader(istream) ; String returnval = null; try { returnval = bufRead.readLine(); bufRead.close(); istream.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
As you can see, after the second invocation, you need to press the ENTER key, so you are allowed to shut down the JBoss AS 7 instance which is pinned to our client and see if the session is correctly moved to the other node.
Last thing we need adding is the jboss-ejb-client.properties which will contain the list of nodes which will be used by the client application. (Since the second node is running with a port-offset of 200m the remoting node port will be 4647)
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false remote.connections=node1,node2 remote.connection.node1.host=localhost remote.connection.node1.port = 4447 remote.connection.node1.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false remote.connection.node1.username=userejb remote.connection.node1.password=userejb123 remote.connection.node2.host=localhost remote.connection.node2.port = 4647 remote.connection.node2.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false remote.connection.node2.username=userejb remote.connection.node2.password=userejb123
Ready to run !
Launch the client application and verify on the console that the EJB correctly prints out the total:
Now kill this server instance (Control-C on the window will suffice), and press the ENTER key in your Client shell.
Et voilà ! The clustered session has been recovered by the other server node. In the next tutorial we will check high availability using a clustered Web application.