The documentation for the singleton server is here:
http://docs.oracle.com/cd/E24329_01/web.1211/e24425/service_migration.htm#i1051668
With this capability, a question arises from time-to-time whether the Singleton Service feature in WebLogic Server can be used to provide a cluster wide singleton service for applications to use
The answer to this is yes, but it requires you to do a few things to expose the singleton instance to applications running in the cluster so that it can be looked up and used.
Since a singleton service is basically a POJO that implements the weblogic.cluster.singleton.SingletonService interface, which gets instantiated and managed by WebLogic Server, it doesn't have a surface area that allows it to be seen by applications running in the cluster.
To allow the singleton service to be used by applications, in addition to implementing the basic logic that is required, it also needs to perform the following tasks:
- Provide an RMI Remote interface that the singleton service additionally implements, which then allows it to be accessed and invoked from any server in the cluster
- When the singleton service is activated, it binds itself into the cluster-wide JNDI tree
- When the singleton service is deactivated, unbinds itself from the cluster-wide JNDI tree
package sab.demo.wlssingletoninstance; import java.rmi.Remote; import java.rmi.RemoteException; /** * * @author sbutton */ public interface DemoSingleton extends Remote { public final String JNDI_NAME="DemoSingleton"; public String singletonLocation(); public String getWebLogicServerName(); public String getHostName(); public int increment(); public int value(); }DemoSingletonImpl
package sab.demo.wlssingletoninstance; import java.net.InetAddress; import java.net.UnknownHostException; import java.rmi.RemoteException; import java.util.logging.Logger; import javax.naming.Context; import javax.naming.NamingException; import javax.rmi.PortableRemoteObject; import weblogic.cluster.singleton.SingletonService; import weblogic.jndi.Environment; /** * Example of a WebLogic Server Singleton Service that can be accessed from * other deployed components. * * @author sbutton */ public class DemoSingletonImpl implements DemoSingleton, SingletonService { private Integer counter = 0; final private Logger logger = new Logger().getLogger("DemoSingletonImpl"); public DemoSingletonImpl() { super(); logger.info("Constructor"); } // ================================================================ // // ===================== Singleton Service Methods ================ // // ================================================================ // @Override public void activate() { logger.info("Activate on " + singletonLocation()); counter = 0; try { Environment env = new Environment(); Context ctx = env.getInitialContext(); ctx.rebind(JNDI_NAME, this); logger.info("Executed bind for: " + JNDI_NAME); logger.info(String.format("Looking up JNDI_NAME returns: %s", ctx.lookup(JNDI_NAME).getClass().getName())); //quickTest(); } catch (NamingException e) { logger.warning("Unable to bind: " + this.getClass().getName() + " into JNDI as:" + this.getClass().getName() + ", Error: " + e.getMessage()); } } @Override public void deactivate() { logger.info("Deactivate on " + singletonLocation()); try { Environment env = new Environment(); Context ctx = env.getInitialContext(); ctx.unbind(JNDI_NAME); logger.info("Executed unbind for: " + JNDI_NAME); } catch (NamingException e) { System.out.println("\tUnable to unbind from JNDI as: " + JNDI_NAME + ", Error: " + e.getMessage()); } } // ================================================================ // // ========================= Remotable Methods ==================== // // ================================================================ // @Override public String singletonLocation() { return String.format("%s %s", getHostName(), getWebLogicServerName()); } @Override public String getWebLogicServerName() { return Utilities.getWebLogicServerName(); } @Override public String getHostName() { return Utilities.getHostName(); } @Override public synchronized int increment() { counter++; return counter.intValue(); } @Override public int value() { return counter.intValue(); } // ================================================================ // // ========================== Private Methods ===================== // // ================================================================ // /** * Called on activate to verify binding is available */ private void quickTest() { try { Environment env = new Environment(); Context ctx = env.getContext(); DemoSingleton singleton = (DemoSingleton) PortableRemoteObject.narrow(ctx.lookup(DemoSingleton.JNDI_NAME), DemoSingleton.class); logger.info("quickTest: " + singleton.singletonLocation()); } catch (Exception ne) { logger.log(Logger.SEVERE, ne); logger.severe("Error: %s" + ne.getMessage()); } } }
Deploying and Configuring the Singleton
To deploy the singleton in standalone mode, compile and package the classes into a JAR file and put the JAR file into the WebLogic Server classpath. Note this needs to be done on each physical machine where the managed servers in the cluster run.
You configure a singleton service instance for cluster using the WebLogic Server console.
This ultimately writes an entry in the domain's config.xml as such:
<singleton-service> <name>DemoSingleton</name> <user-preferred-server>Mike</user-preferred-server> <class-name>sab.demo.wlssingletoninstance.DemoSingletonImpl</class-name> <cluster>Tingles</cluster> </singleton-service>When the preferred managed server starts, the singleton service will be instantiated.
<Mar 2, 2012 3:25:32 PM CST> <Notice> <Cluster> <BEA-000102> <Joining cluster Tingles on 239.192.0.0:7001> <Mar 2, 2012 3:25:33 PM CST> <Notice> <WebLogicServer> <BEA-000365> <Server state changed to RUNNING.> <Mar 2, 2012 3:25:33 PM CST> <Notice> <WebLogicServer> <BEA-000360> <The server started in RUNNING mode.> [] INFO Activate on Steve-Button-MacBook-Pro.local Mike [] INFO Executed bind for: DemoSingleton [] INFO Looking up JNDI_NAME returns: sab.demo.wlssingletoninstance.DemoSingletonImpl [] INFO quickTest: Steve-Button-MacBook-Pro.local MikePerforming a manual migration using the console, the singleton gets deactivated on the current server and activated on another.
[] INFO Deactivate on Steve-Button-MacBook-Pro.local Mike [] INFO Executed unbind for: DemoSingleton <Mar 2, 2012 3:25:34 PM CST> <Notice> <WebLogicServer> <BEA-000360> <The server started in RUNNING mode.> [] INFO Activate on Steve-Button-MacBook-Pro.local James [] INFO Executed bind for: DemoSingleton [] INFO Looking up JNDI_NAME returns: sab.demo.wlssingletoninstance.DemoSingletonImpl [] INFO quickTest: Steve-Button-MacBook-Pro.local JamesUsing the Singleton
Applications wishing to use the singleton service then need to look it up from the cluster-wide JNDI tree, narrow it to singleton service type and invoke it's methods as needed.
private DemoSingleton getSingleton() throws ClassCastException, NamingException { Context ctx = getInitialContext() DemoSingleton singleton = (DemoSingleton) PortableRemoteObject.narrow(ctx.lookup(DemoSingleton.JNDI_NAME), DemoSingleton.class); return singleton; }Using this in a servlet for example, you can see how the servlet running on the managed server Mike is able to access the singleton service running on managed server James.
When the singleton service is migrated, the servlet then seamlessly accesses it on the other managed server, where it is now co-located in this specific example below.
Summary
This is obviously a fairly simple example but it should highlight the basic requirements and method to create an cluster wide singleton that can be used by applications.