Clustering EJB 3 with JBoss AS

Please note: This tutorial has been written for JBoss EAP 5/6 and early versions of WildFly.

The current version of WildFly or JBoss EAP 7 no longer requires you to enable the clustering behavior. By default, if the server is started using the "ha" or "full-ha", the state of SFSBs will be replicated automatically and Stateless Session Beans will be load balanced.

If you want to disable this behavior for Stateful Session Beans check this tutorial: How to disable passivation for SFSB in WildFly

Clustering EJB 3 with JBoss EAP 6, JBoss EAP 5 and early WildFly versions

To cluster a stateless session bean in EJB 3 all you need to do is to annotate the bean class withe the @Cluster annotation. You can pass in the load balance policy and cluster partition as parameters to the annotation.

The default load balance policy is org.jboss.ha.framework.interfaces.RandomRobin and the default cluster is DefaultPartition.

Clustering a Stateless Session Bean

As we said earlier all you have to do is tagging the EJB with the @Stateless annotation before the class declaration. Here’s a sample:

package com.sample;

import java.io.Serializable;
import javax.ejb.*;
import org.jboss.annotation.ejb.Clustered;

@Stateless
@Clustered
public class HelloBean implements HelloBeanItf {
  int counter = 0;

  public String doSomething() {
     return "HelloBean Called"; 
  }

}

and here’s the interface:  

package com.sample;

public interface HelloBeanItf {

   public String doSomething(); 

}

Not very difficult, isn’t it ?? 

Clustering Stateful Session Beans

To cluster stateful session beans in EJB 3.0, you need to tag the bean implementation class with the @Cluster annotation, just as we did with the EJB 3.0 stateless session bean earlier.

@Stateful
@Clustered


public class HelloBean implements HelloBeanItf,Serializable {
int counter = 0;
  public void doSomething() {

     counter++;
     System.out.println("Value of counter is " + counter);

  }

So who takes care of session state replication ? the answer is JBoss Cache. JBoss Cache provides distributed cache and state replication services for the JBoss cluster. A JBoss cluster can have multiple JBoss Cache MBeans (known as the TreeCache MBean), one for HTTP session replication, one for stateful session beans, one for cached entity beans, etc.

The related MBean service is defined in the ejb3-clustered-sfsbcache-service.xml file in the deploy directory. The contents of the file are as follows.

<server>
   <mbean code="org.jboss.ejb3.cache.tree.PassivationTreeCache"
 name="jboss.cache:service=EJB3SFSBClusteredCache">
 
       <attribute name="IsolationLevel">READ_UNCOMMITTED</attribute>
       <attribute name="CacheMode">REPL_SYNC</attribute>
       <attribute name="ClusterName">SFSB-Cache</attribute>
       <attribute name="ClusterConfig">
           ... ...
       </attribute>

       <attribute name="SyncReplTimeout">10000</attribute>
       <attribute name="LockAcquisitionTimeout">15000</attribute>

       <attribute name="EvictionPolicyClass">org.jboss.ejb3.cache.tree.StatefulEvictionPolicy</attribute>

       <attribute name="EvictionPolicyConfig">
          <config>
             <attribute name="wakeUpIntervalSeconds">1</attribute>
            <name>statefulClustered</name>
            <region name="/_default_">
               <attribute name="maxNodes">1000000</attribute>
               <attribute name="timeToIdleSeconds">300</attribute>
            </region>
         </config>
      </attribute>

      <attribute name="CacheLoaderFetchPersistentState">false</attribute>
      <attribute name="CacheLoaderFetchTransientState">true</attribute>
      <attribute name="FetchStateOnStartup">true</attribute>
      <attribute name="CacheLoaderClass">org.jboss.ejb3.cache.tree.StatefulCacheLoader</attribute>
      <attribute name="CacheLoaderConfig">location=statefulClustered</attribute>
   </mbean>
</server>

JBoss AS 6 and 7 don’t use any more JBoss Cache for providing distributed and replicated cache. The new provider is Infinispan which exposes a data grid platform and exposes and a JSR-107 (JCACHE) compatible Cache interface

A local JBoss EJB client

A local client can be a simple jsp:

<%
   public void jspInit () {

   try {

     HelloBeanItf ejb = null;
     InitialContext ctx = new InitialContext();

     ejb = (HelloBeanItf)

       ctx.lookup("cluster/HelloBean/local");

   }

   catch (Exception exc) { 
      exc.printStackTrace();
   }
}

ejb.doSomething();
%>

Remember that in EJB 3, the JNDI of the EJB by default is made up of : 
package name( ear/jar) / Bean Name / local or remote

If you fail to find your EJB in JNDI tree take a look at the JNDI tree from the JMX console and you’ll solve the naming problem (There’s an how to in this site here : JBoss howtos )

How can a remote client access a clustered EJB ?

When using remote client the first thing you think about is JNDI.

How does JNDI works in a cluster ? JBoss service maintains a cluster-wide context tree called HA-JNDI (High Availability JNDI). The cluster wide tree is always available as long as there is one node left in the cluster.

In the previous JSP , new InitialContext(), will be bound to a local-only, non-cluster-wide JNDI Context (this is actually basic JNDI). So, all EJB homes and such will not be bound to the cluster-wide JNDI Context, but rather, each home will be bound into the local JNDI.


When a remote client does a lookup through HA-JNDI, HA-JNDI will delegate to the local JNDI Context when it cannot find the object within the global cluster-wide Context.

How the remote client is aware of the HA-JNDI cluster ? You can pass a list of JNDI servers (i.e., the nodes in the HA-JNDI cluster) to the java.naming.provider.url JNDI setting in the jndi.properties file. Each server node is identified by its IP address and the JNDI port number. The server nodes are separated by commas

Example:

java.naming.provider.url=server1:1100,server2:1100,server3:1100,server4:1100

you can pass the list of JNDI servers to the InitialContext also like here:

import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;

public class Client {

  public static void main(String[] args) throws Exception {


     Properties p = new Properties(); 
     p.put(Context.INITIAL_CONTEXT_FACTORY,"org.jnp.interfaces.NamingContextFactory"); 
     p.put(Context.URL_PKG_PREFIXES, "jboss.naming:org.jnp.interfaces"); 
     p.put(Context.PROVIDER_URL, "192.168.0.1:1100,192.168.0.2:1100");

     InitialContext ctx = new InitialContext(p);
 
     HelloBeanItf ejb = 
       (HelloBeanItf) ctx.lookup("cluster/HelloBean/remote");
 
     for (int ii=0;ii<10;ii++) {
        ejb.doSomething(); 
     }
     System.out.println("Terminated");
   }
}

Before running the sample remeber to put the @Remote annotation in the Bean class, other wise you will try to contact an EJB local interface from remote, and this is not allowed!

Found the article helpful? if so please follow us on Socials