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.
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.
Still it is unclear, the usage of singleton on cluster-ware environments?
ReplyDeleteHi 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.
DeleteHi Darrel.
ReplyDeleteWill 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.
You should only see it bound on the server where it is active.
DeleteHi,
ReplyDeleteI 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?
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.
DeleteHi, 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?
DeleteNo. 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.
DeleteInstead 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
ReplyDeleteYes.
DeleteHi ,
ReplyDeletewe 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
Great article Darrel.
ReplyDeletebe 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.
ReplyDeleteWhy did you say "It’s not applicable to EJBs, MDBs, or other objects whose lifecycles are managed by the application server"? I am doing a project that requires both MDB and strict order of message. I see an article here for that:
ReplyDeletehttp://middlewaresnippets.blogspot.com/2011/02/singleton-message-driven-beans-in.html#!/2011/02/singleton-message-driven-beans-in.html
Nice topic
ReplyDeleteAs claimed by Stanford Medical, It is indeed the one and ONLY reason this country's women live 10 years longer and weigh an average of 42 pounds lighter than us.
ReplyDelete(And actually, it has totally NOTHING to do with genetics or some secret diet and absolutely EVERYTHING related to "how" they are eating.)
BTW, What I said is "HOW", and not "WHAT"...
TAP on this link to see if this little questionnaire can help you unlock your true weight loss possibilities
Hi,
ReplyDeleteNeed to configure singleton service for JMS consumer, the constructor is being called again once the call comes to onmessage, resulting in multiple instances. Can anyone help.
Thanks !!
شركة ترميمات
ReplyDeleteعزل اسطح
مكافحة حشرات
تسليك مجاري
كشف تسربات المياه
hl468 stadium goods es confiable,restocksromania,stockxsrbija,columbiaireland,kickscrew europe,klekt sell,klekt yeezy,dr martens québec,alexander mcqueen hrvatska dq473
ReplyDelete