Tuesday, March 20, 2012

WebLogic Clusters and the Singleton Service


Ever need to have exactly one object (a single object method invocation) in a cluster of many Oracle WebLogic application servers that supports failover?  The EJB 3.1 @Singleton annotation only guarantees an EJB singleton per JVM, and creating a singleton in the traditional Java SE-fashions (see Joshua Bloch’s article at Dr Dobbs) only guarantees a singleton per class loader.
WebLogic Server provides support for such a cluster-wide singleton (here, scroll down to “Implementing the Singleton Service Interface”), which I had the chance to experiment with during the past week.  The documentation on this feature is adequate to get it running for the first time, but I thought some additional detail around it might be useful.


What is the SingletonService, and what does it provide?

weblogic.cluster.singleton.SingletonService is an interface that you can implement in your Plain Old Java Object.  It’s not applicable to EJBs, MDBs, or other objects whose lifecycles are managed by the application server.
SingletonService provides the two abstract methods activate() and deactivate().  activate() is invoked when the class becomes the designated cluster-wide singleton (i.e., server startup, failover and migration, or if the application is re-deployed).  deactivate() is more or less invoked on the inverse side of those operations: Server shutdown, failover and migration, and application un-deployment.
This is really all the Singleton Service interface provides: The invocation of the two implemented methods at the appropriate times, the guarantee that only one instance is active, and the behavior of starting activate() on another server in the cluster.  This seemingly basic functionality can be very powerful in the right use case, however.

What are some valid use cases for Singleton Service?

Singletons, even in the Java SE usage, usually require some additional consideration and planning. It shouldn’t be surprising that a singleton construct outside of the Java SE and EE APIs would require additional care as well.  The first step in correct implementation is developing an understanding for what use cases the cluster-wide singleton is (and isn’t) appropriate for.
This is not, by any means, a comprehensive list.  Feel free to add use cases you have used it for as well in the comments.

Timers / job schedulers in a cluster

The basic use case is getting a single server to fire off timed events within a cluster and support failover in the event that the server is turned off / unplugged / exploded.  You only want a single server firing off these timed events (e.g., JMS messages sent to a topic that prompts subscribers to report some kind of status).  This would be along the lines of a cluster-aware cron job. This is ordinarily not easy to achieve unless you configure a heterogeneous cluster or an external cron job– and that makes failover a concern.
SingletonService makes this use case relatively simple to implement, since you only have it active on one server at a time and failover is taken care of for you.  James Bayer wrote about this back in 2009, so you should be able to follow his example for implementation.  You may not want to have the scheduler be the singleton service, but you can use the singleton service to construct and start the timer.

Use the SingletonService to handle other un-clusterable services

What if you wanted to run a service that cannot be clustered in a meaningful way?  How about a Java-based email server?  Perhaps you need a file, FTP, or email client poller?
Using the activate() and deactivate() methods, you can create the service within the cluster exactly once, and ensure that the service will migrate over to another cluster member on server shutdown.

Single Source for State or Properties for Clustered Applications

Usually, using a database or an in-memory cache like Coherence are more desirable options to store application properties, due to both reduced complexity and ease of implementation. Using the database might not be an option, however, since it’s possible that the connection to the database may be transient or the information may be needed prior to connecting to the database.  It’s also possible that an in-memory cache is not currently in the environment.
Given that you want a single place to store and update the information, and not have to redeploy the application or restart the server to get the change to take effect, you could use the Singleton Service to store state / properties.  In this case, I’ve provided a sample.

My Example

My example is composed into two main parts: the Singleton Service POJO (and its interface), and the application that invokes methods from the “JEE world” – in this case, an Enterprise Java Bean.  I’ve got a couple of basic requirements: I need to reference the POJO from anywhere in the cluster via JNDI, and I want it to carry some kind of state.
Because I need to bind the object to JNDI, and access its methods, I need to start with an interface that extends Remote (i.e., I am going to use Remote Method Invocation).  Nothing profound, I just need modifiers to a private integer.
 package com.darrel.samples;  
 import java.rmi.Remote;  
 import java.rmi.RemoteException;  
 public interface MySingletonServiceInterface extends Remote {  
    public void setMyValue(int value) throws RemoteException;  
    public int getMyValue() throws RemoteException;  
 }  
Now I need to create the implementation:
 package com.darrel.samples;  
   
 import java.io.Serializable;  
 import javax.naming.Context;  
 import javax.naming.InitialContext;  
 import javax.naming.NamingException;  
 import weblogic.cluster.singleton.SingletonService;  
   
 public class MySingletonServiceClass implements 
    SingletonService, Serializable, MySingletonServiceInterface {  
   
    private static final long serialVersionUID = 3966807367110330202L;  
    private static final String jndiName = "MySingletonServiceClass";  
    private int myValue;  
      
    public int getMyValue() {  
       return myValue;  
    }  
      
    public synchronized void setMyValue(int myValue) {  
       this.myValue = myValue;  
    }  
   
    @Override  
    public void activate() {  
       System.out.println("activate triggered");  
       Context ic = null;  
       try {  
          ic = new InitialContext();  
          ic.bind(jndiName, this);  
          System.out.println("Object now bound in JNDI at " + jndiName);  
          myValue = 5;  
       } catch (NamingException e) {  
          myValue = -1;  
          e.printStackTrace();  
       }finally{  
          try {  
             if(ic != null) ic.close();  
          } catch (NamingException e) {  
             e.printStackTrace();  
          }  
       }     
    }  
   
    @Override  
    public void deactivate() {  
       System.out.println("deactivate triggered");  
       Context ic = null;  
       try {  
          ic = new InitialContext();  
          ic.unbind(jndiName);  
          System.out.println("Context unbound successfully");  
       }catch (NamingException e){  
          e.printStackTrace();  
       }  
    }  
 }  
The basics are there – I implement the abstract methods from Singleton Service.  I use activate() to initialize a value for myValue.    I also bind (and unbind upon deactivation) the object in JNDI as “MySingletonService” (creative, I know).  Concurrency is definitely an issue, so the synchronized modifier on setMyValue() is very, very necessary.
I created an EJB to access the POJO, and added web service annotations for testing purposes.
 package com.darrel.samples;  
   
 import javax.ejb.Stateless;  
 import javax.jws.WebMethod;  
 import javax.jws.WebService;  
 import javax.naming.Context;  
 import javax.naming.InitialContext;  
   
 @WebService  
 @Stateless(mappedName="com.darrel.samples.SingletonTestingBean")  
 public class SingletonTestingBean   
 implements SingletonTestingBeanRemote,   
 SingletonTestingBeanLocal   
 {  
    int myValue;  
   
    public SingletonTestingBean() {}  
   
    @Override  
    @WebMethod  
    public String sayHelloInternalValue(String firstname) throws Exception {  
       System.out.println("sayHelloInternalValue invoked");  
       Context ctx = new InitialContext();  
       MySingletonServiceInterface mssc = (MySingletonServiceInterface)   
             ctx.lookup("MySingletonServiceClass");  
       myValue = mssc.getMyValue();  
       return "Hello " + firstname + ", my value is " + myValue;           
    }  
   
    @Override  
    @WebMethod  
    public int addInternalValue(int myInt) throws Exception {  
       Context ctx = new InitialContext();  
       MySingletonServiceInterface mssc = (MySingletonServiceInterface)   
             ctx.lookup("MySingletonServiceClass");  
       mssc.setMyValue(mssc.getMyValue() + myInt);  
       myValue = mssc.getMyValue();  
       return myValue;  
    }  
 }  
   
Not much new here – a context lookup to the object, and simple setters and getters.  I’ve excluded the remote and local interfaces for brevity.
To build and bundle our new Singleton Service and its interface into a JAR, you will need to add weblogic.jar to your class path at build time.  Since I used a plain Java project, I had to add weblogic.jar as an external JAR to the project build path.
I added the resulting JAR to my WebLogic Domain’s /lib folder.  The EJB project can be built with the JAR in the build class path.  In Eclipse, you could do this via the “Required projects on the build path” dialog:


Now we need to configure the cluster for the Singleton Service. In the WebLogic Administration Console, navigate to your cluster, and then to the “Migration” tab.  You will need to have migration set up in some way, I used “Consensus” to avoid using a database for this example, but your production model may have different needs entirely.

Now you need to navigate to the cluster’s “Singleton Services” tab, and create a new Singleton Service.
You will want to use the fully qualified class name for the singleton:

Set your preferred server and we are ready for deployment and testing.  Deploy the EJB project to the cluster. You can use the WebLogic Test Client to verify functionality, as the EJB Web Service will provide web test points for you to use. 
In my test, I used the addInternalValue() method on the EJB hosted on server1 to add 8, returning the total value of 13.

Then, I used the sayHelloInternalValue() method from server2, using the argument “World” – note that the value displayed is 13, which implies that server2 is indeed invoking methods to the same object as server1.

This is also a good time to look at the console output – take a look at your server.out for the preferred server, you should see the System.out.println() from the activate() method.
 <Mar 2, 2012 5:25:20 PM CST> <Notice> <WebLogicServer> <BEA-000360> <The server started in RUNNING mode.>   
 activate triggered  
 Object now bound in JNDI at MySingletonServiceClass  
   
To verify migration, try shutting down the preferred server and you will then see the activate() method’s print statements in the console output of one of the other servers in your cluster.  If you shut down each server as the singleton becomes active on that server, you should see this output in the console output in every server in the cluster. 

Alternate methods of building and deploying

My initial attempt at deploying this Singleton Service was by bundling it in a JEE utility JAR that was inside of the EJB EAR, and using the weblogic-application.xml deployment descriptor of the EAR to register it as an app-scoped singleton service.  The xml snippet regarding the singleton service would look like this:
   <wls:singleton-service>  
     <wls:class-name>com.darrel.samples.MySingletonServiceClass</wls:class-name>  
     <wls:name>Appscoped_Singleton_Service</wls:name>  
   </wls:singleton-service>  
This approach has the merit of not requiring a server restart to get class changes to take effect after updating the Singleton Service – you only need to redeploy the EAR.  It also simplifies the maintenance of your build path, since the WebLogic System Libraries should already be there – unlike in my example, where I had to add the weblogic.jar file externally.  Further, this eliminates the step of modifying the cluster configuration in the Administration Console to account for the singleton.  Finally, since you are no longer adding your singleton class to the $DOMAIN_HOME/lib, administrators will not have to directly modify the class path to transition your application to production.

Implications for Use

Individually, the singleton service doesn’t benefit from the linear performance scaling of the cluster, just the failover capabilities.   This doesn’t mean that you can use the singleton service to create scalable and performance-driven services, merely that you can’t directly leverage the cluster to do so.  For example: JMS servers exist as form of singleton services within the cluster, but address scaling by hosting distributed destinations.  The constituent, physical destinations of the logical distributed destination are individually hosted by the singleton JMS servers.  In this case, however, quite a bit more is happening than simply registering a POJO in JNDI.
Concurrency is a consideration when providing access to class members in the singleton that are not thread-safe.  In the above example, I use a synchronized method to address concurrent access to the data primitive class member.  This is an observation that should be readily apparent (certainly, it would also be true in the case of any other type of singleton), but is worth mentioning as a warning.
“What are other people using this for?” you might ask.  Of the customers I have encountered, a majority have used the SingletonService as a means to create a High Availability service out of a service that is fundamentally unable to be clustered (like an FTP destination poller).  The SingletonService capability enabled them to avoid deploying the application to a stand-alone managed server, and the failover capability allows them to ensure that the service stays up as long as the cluster does.

Final Thoughts

I’d be curious to find out what you are using the Singleton Service for – please leave your use case in the comments, if you can share.

13 comments:

  1. Still it is unclear, the usage of singleton on cluster-ware environments?

    ReplyDelete
    Replies
    1. Hi Satya- Primarily, the focus is to trigger an "exactly once" per cluster functionality. Maybe this is because, for example, I want a single instance timer to fire off a message that would trigger occasional batch processing - I don't want every server to fire off such a message, in this case. I could achieve the exactly once functionality by doing partial cluster deployment, but I wouldn't get the failover functionality that the Cluster Wide Singleton gives me.

      Delete
  2. Hi Darrel.

    Will we see the JNDI MySingletonServiceClass bound to all the server s in the cluster or should we see it on only one server on which the service is active.

    As I have created a similar startup class I am just not using myValue however on startup I can see both the servers have the JNDi name bound.

    ReplyDelete
    Replies
    1. You should only see it bound on the server where it is active.

      Delete
  3. Hi,
    I am trying to develop a Singleton Service as a simple cache server and noticed that I am not able to get it activiated when I tried to use EJB EAR.

    Other than the server log that indicate that it is registered, I do not see any other things that indicate it is started. Are you able to help in anyway?

    ReplyDelete
    Replies
    1. I think solving the problem comes down to proving out two pieces, based on what you have said: 1) Verifying that the singleton service is actually coming up (verify through adding debug to the initialize() method), and 2) verifying what you are doing for communication to the object is correct. In my example, I used JNDI for lookup and RMI for communications, but I do not know what interface you are planning on using. When you say the logs indicate it's registered, it implies (to me) that your object is actually getting created - verify that, but your likely culprit is that you have something not quite working when you go to access the object.

      Delete
    2. Hi, I noticed what happen is it takes a random period of time before the Singleton Service get activated. Does this happened to yours as well?

      Delete
    3. No. If you're deploying your Singleton as a part of a larger EAR, perhaps it's somewhat due to the rest of your deployment lifecycle. You might try deploying your Singleton in an empty EAR and see how that affects your time-to-activation.

      Delete
  4. Instead of jndi look up of a singleton service from a stateless session bean method, can I inject the seingleton service to the session bean using @Resource

    ReplyDelete
  5. Hi ,

    we have uniform dustributed topic in weblogic clustered environment, intially there were two instances created for every single message , later we have added a singleton property into our compoisted and redeployed , now we can see one instances getting created for each message , but , messages in one of the JMS topic getting dissapeared after some time , have any idea on this.

    Thanks,
    Srinidhi

    ReplyDelete
  6. be careful on the deployment phase. You cannot deploy the same EAR with SingletonService on two different cluster on the same domain. You have to deployer the same EAR with two different name independently on each cluster of the domain.

    ReplyDelete