Clustering JBoss AS 7

Important Notice: There is a more updated tutorial about Clustering with WildFly Application Server: Clustering WildFly Application Server


In this article we will show how to configure in a nutshell JBoss AS 7 for clustering. Then we will deploy a sample Java EE 6 application on the top of it.

Earlier JBoss users used to start a clustered server configuration by feeding the -c all parameter to the application server starter shell.

The AS 7 is pretty different from other server releases as it does not contain multiple server configuration directories (all, default, minimal ..). It just contains a set of configuration files each one including a different set of server modules.

The built-in cluster configuration file are standalone-ha.xml and standalone-full-ha.xml (the latter one including Corba support). So let’s start a demo cluster configuration composed of two nodes on the same machine.

So unzip two AS 7 distribution on a path of your choice, for example:

C:\jboss-as-7.1.1.Final.NodeA
C:\jboss-as-7.1.1.Final.NodeB

In order to start your cluster nodes you need to provide an unique node name and, since you are running multiple servers on the same machine specify a port offset so that you don’t have port conflicts between servers.

So we will start up NodeA with:

standalone -c standalone-ha.xml -Djboss.node.name=nodeA

and NodeB with:

standalone -c standalone-ha.xml -Djboss.socket.binding.port-offset=100 -Djboss.node.name=nodeB

Fine. Checkout the welcome page to see that everything works correctly:
http://localhost:8080 (nodeA) and http://localhost:8180 (nodeB)

Configuring Apache Web server to reach JBoss AS 7 cluster

There are more than one available choice to connect a Web server in front of JBoss AS 7 cluster. You can either choose the older mod_jk approach which provides a static view of your AS 7 cluster or, as we suggest, configure mod_cluster which provides additional advantages such as:

  • Dynamic configuration of httpd workers
  • Server-side load balance factor calculation
  • Fine grained web-app lifecycle control

Mod_cluster Java libraries are built-in into JBoss AS 7 distribution so you don’t need to hack your server release. On the other hand, you need to add to your Apache server the native libraries required to proxy calls to JBoss AS. Download the latest mod_cluster binaries at:
http://www.jboss.org/mod_cluster/downloads/1-2-0-Final

 The above modules can be found into the mod_cluster distribution under httpd/modules path. You have to place them into the Apache’s module folder.

Then we edit httpd.conf. We should add the modules:

LoadModule slotmem_module modules/mod_slotmem.so
LoadModule manager_module modules/mod_manager.so
LoadModule proxy_cluster_module modules/mod_proxy_cluster.so
LoadModule advertise_module modules/mod_advertise.so

Also, please note we should comment out:

#LoadModule proxy_balancer_module modules/mod_proxy_balancer.so

This one conflicts with cluster module. And then we need to make httpd to listen to public address so we could do the testing. Because we installed httpd on 192.168.1.0 host we will use the following available port:

Listen 192.168.1.0:6666

<VirtualHost 192.168.1.0:6666>   
    <Directory />     
       Order deny,allow     
       Deny from all     
       Allow from 192.168.1.  
   </Directory>

   KeepAliveTimeout 60
   MaxKeepAliveRequests 0  
   ManagerBalancerName mycluster
   AdvertiseFrequency 5
</VirtualHost>

Now verify that the Apache JBoss connection works properly by typing: http://192.168.1.0:6666


Now we will deploy a variant of ourJava EE 6 example application which is composed of the following components:

Application Components

An EJB Stateful Bean which is used to store a cache of data, by exposing a Local interface
A JSF Managed Bean which acts as a layer between the EJB and the GUI
A JSF view which is used to collect data

Let’s start with coding the JSF Managed Bean class:

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;

import com.sample.ejb.StatefulData;

@SessionScoped
@Named(value="manager")
public class PropertyManager implements Serializable {
 
    @Inject
    StatefulData ejb;

    ArrayList  cacheList  = new ArrayList ();

    private String key;
    private String value;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    public void save() {
        ejb.put(key, value);
    }

    public void clear() {
        ejb.delete();

    }
    public List getCacheList() {
        return ejb.getCache();
    }


}

And this is the Local Stateful EJB class which is annotated as @Stateful @Clustered @SessionScoped

package com.sample.ejb;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.PostConstruct;
import javax.ejb.LocalBean;

import javax.ejb.Stateful;
import javax.enterprise.context.SessionScoped;

import org.jboss.ejb3.annotation.Clustered;

import com.sample.model.Property;

@Stateful
@Clustered
@SessionScoped
@LocalBean
public class  LocalStatefulData implements StatefulData  {

    private  List<Property> cache;

    @Override
    @PostConstruct
    public void initCache(){
        this.cache = queryCache();
        if (cache == null) cache = new ArrayList<Property>();
    }


    @Override
    public void delete(){
        this.cache.clear();
    }

    @Override
    public void put(String key,String value){
        Property p = new Property();
        p.setKey(key);
        p.setValue(value);
        this.cache.add(p);
    }

    @Override
    public List<Property> getCache() {
        return cache;
    }

    @Override
    public List<Property>  queryCache(){

        return cache;          
    }

}
Important Notice

The current release of JBoss Tools (3.3) does not includes by default the libraries where the @Clustered annotation is contained. Until this issue is solved, you have to include the jboss-ejb3-ext-api-2.0.0.jar manually as shown by the following picture:

jboss cluster tutorial software

For the sake of completeness we include also the Stateful SB interface:

package com.sample.ejb;

import java.util.List;
import com.sample.model.Property;

public interface StatefulData {

    public abstract void delete();
    public abstract void put(String key, String value);
   public abstract List<Property> getCache();
    public abstract List<Property> queryCache();

}

Then, this is the model class which consists of a simple POJO:

                                                                                                         

package com.sample.model;

import java.io.Serializable;

public class Property implements Serializable{

    private String key;
    private String value;

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

}

Last piece of the application is the JSF home.xhtml page which is unchanghed from no clustered Java EE application:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head>
    
</h:head>
<h:body>
    <h2>Cluster demo on JBoss 7</h2>
    <h:form id="jsfexample">
        <h:panelGrid columns="2" styleClass="default">

            <h:outputText value="Enter key:" />
            <h:inputText value="#{manager.key}" />

            <h:outputText value="Enter value:" />
            <h:inputText value="#{manager.value}" />

            <h:commandButton actionListener="#{manager.save}"
                styleClass="buttons" value="Save key/value" />
            <h:commandButton actionListener="#{manager.clear}"
                styleClass="buttons" value="Clear cache" />

            <h:messages />

        </h:panelGrid>


        <h:dataTable value="#{manager.cacheList}" var="item"
            styleClass="table" headerClass="table-header"
            rowClasses="table-odd-row,table-even-row">
            <h:column>
                <f:facet name="header">Key</f:facet>
                <h:outputText value="#{item.key}" />
            </h:column>
            <h:column>
                <f:facet name="header">Value</f:facet>
                <h:outputText value="#{item.value}" />
            </h:column>
        </h:dataTable>
    </h:form>
</h:body>
</html>

That’s all. Remeber to add an empty beans.xml file under the WEB-INF folder so that the Weld dependencies are triggered automatically by AS 7.
Here’s how your project should look like:
jboss as 7 cluster tutorial
Now launch your browser and test your Web application by pointing to the mod_cluster port that we have formerly configured:
http://192.168.1.0:6666/as7clustersample/home.xhtml

jboss as 7 cluster tutorial