Preparation
As always: Get you GlassFish 3.1 RC4 up an running. Create a cluster (e.g. demoCluster) and add two instances (instance1 and instance2). Now start your domain and cluster. Write a simple EJB (e.g. MySessionBean) and make it implement a RemoteInterface (e.g. MySessionBeanRemote). My example has a simple String showInstance() business method which prints out the actual running instance:
Logger.getLogger(MySessionBean.class.getName())
.log(Level.INFO, "Running on: {0}",
System.getProperty("com.sun.aas.instanceName"));
Wrap your ejb-jar into an ear and deploy it. Make sure to check the "Availability" during deployment. This enables in-memory replication from one instance to the other.
Go and check if the EJB is registered with your instances:
%AS_INSTALL%/bin/asadmin list-jndi-entries demoCluster
You should see something like this:
node2:
java:global: com.sun.enterprise.naming.impl.TransientContext
[...]net.eisele.cluster.MySessionBeanRemote#
net.eisele.cluster.MySessionBeanRemote: javax.naming.Reference
[...]
node1:
java:global: com.sun.enterprise.naming.impl.TransientContext
[...]
net.eisele.cluster.MySessionBeanRemote#
net.eisele.cluster.MySessionBeanRemote: javax.naming.Reference
[...]
Up to now, this hardly was any problem. It's a simple EJB without anything else to tweak. Now let's go and write some clients.
Standalone Client
Coming from the old-school I am starting with a standalone client without any "magic". Create a Java project (e.g. StandaloneClient), add a Main class (StandaloneClient) and add the %AS_INSTALL%/glassfish/lib/gf-client.jar as a dependency. Make sure to reference the RemoteInterface (MySessionBeanRemote) somehow (Classpath or copying it to your projects source).
// get a simple no argument constructor InitialContext
InitialContext ic = new InitialContext();
// use the global jndi name as lookup string
String lookup = "java:global/ClusterTestEar/ClusterTestEar-ejb/MySessionBean!net.eisele.cluster.MySessionBeanRemote";
//lookup the jndi name
MySessionBeanRemote myEjb = (MySessionBeanRemote) ic.lookup(lookup);
//call showInstance
myEjb .showInstance();
Everything straight forward. No magic here. Compared with most of the other application servers it's unusual that you don't either have to specify any InitialContext properties or even add any cluster magic to the lookup string. But how does the client know about my instances and ports? That's simple. you have to specify them as VM Options:
-Dcom.sun.appserv.iiop.endpoints=
127.0.0.1:23701,127.0.0.1:23700
Application Client Container
The youth is probably unhappy about the standalone client. They are used to some kind of injection and those fancy annotation stuff. Ok. Here we go. You are calling for the ACC. The easiest way of creating one is to let your IDE do this. NetBeans generates a great acc project for you. It also has a main class and you can simply:
// inject you ejb ...
@EJB
private static MySessionBeanRemote myEjb;
[...]
// and use it
myEjb .showInstance();
Great. But here it goes. NetBeans itself does not have any kind of cluster deployment support, but the ACC project was designed to be deployed. So, even if you are not in need doing so, the "Run File" NetBeans command fails. So you have to do this manually. First part is to Tell the ACC where to find the remote EJB.
If you are not going to write a webstart based scenario (which is exactly what we are NOT going to do :)), you can simply pick a copy of the %AS_INSTALL%\domains\clusterdomain\config\sun-acc.xml and put it to your projects folder. You need to edit it and make it reflect your instances:
<target-server name="MYNODE" address="localhost" port="23701"/>
<target-server name="MYNODE" address="localhost" port="23700"/>
The %AS_INSTALL%\bin\appclient.bat/sh script is there to help you executing the ACC. Build a jar from your appclient project and run it:
appclient -client %YOUR_DIST_FOLDER%\ClusterAppClient.jar -xml %YOUR_DIST_FOLDER%\sun-acc.xml
Done. Now you have everything working. The new-school and the old-school way of accessing remote EJBs.
Clustering / Failover
Now let's test the failover capabilities. Add a second business method call to your clients and do anything to make the client pause between them. Either to automatically continue after a few seconds or on key-press.
Now let's start the client. It prints out to which instance this client is connected. Assume it tells you something like this:
Running on: instance1
Now switch over to your asadmin console and enter the needed stop-instance command to shutdown the instance the client is connected to. In this case:
asadmin> stop-instance instance1
If it is stopped, press a key or wait for the client script to continue. and see, that the second call is redirected to the second node:
Running on: instance2
Failover works. Perfect :) Thanks to Oracle's Cheng and Tim for answering my questions during research!
Further Reading
Oracle GlassFish Server 3.0.1 Application Development Guide Chapter 11 Developing Java Clients
GlassFish Community EJB FAQ
Hello,
ReplyDeletemaybe a dumb question, but why is the EJB called using a Remote interface? Wasn't the point of JEE 6 that we can get rid of these?
Or differently spoken: if I cluster my Glassfish, why does my (application) design have to take care about this?
Hi,
ReplyDeleteThanks for the comment, there aren't any dumb questions. You are right. You can get rid of most of the interfaces in general: as long as you stay inside the container and do not cross boundaries. Remote access between different JVMs still is a classic case for remote interfaces, stubs, skeletons and all those good old RMI stuff ;)
Clustering is one of those areas left to take care for if you design your application.
M.