Image by denis collette via FlickrThis example shows the creation of a timer for an
OpenESB application, created using Glassfish. A timed code fragment will execute every 30 seconds. This example uses an EJB implementing TimedObject as the timer itself. The timer is started from a lifecycle method in a servlet as the EJB timer can not be started from EJB lifecycle methods, in the current implementation.
Here's the tools:
Product Version: NetBeans IDE 6.1 (Build 200805300101)
Java: 1.6.0; Java HotSpot(TM) Client VM 1.6.0-b105
System: Windows XP version 5.1 running on x86; Cp1252; en_US (nb)
Userdir: E:\openesb\.netbeans\openesb
glassfish-v2-ur2-b04-patch-20080729
1) Create an
EJB with a local interface in an EJB project. Call the EJB EJBTimer.
2) Create a servlet in a web application project. Call it Watch.
3) create an enterprise application project and add both of the above
4) In the servlet code, right-click and use the wizard to add a reference to the EJB. It will generate this:
@EJB
private EJBTimerBean eJBTimerBean;
5) Add to the servlet code an init() lifecycle method, and include there a call to the EJB to start ticking (see the source below).
6) Modify the servlet's deployment descriptor to create the servlet on deploy. In Glassfish, it's web.xml editor calls this "Startup Order". Fill in a one. Examine the "load-on-startup" tag in the web.xml, to see what this does.
7) Deploy the enterprise application. The servlet will be created and will start the timer.
Here is the timer EJB:
package com.me.ebj;
import java.util.Date;
import javax.annotation.Resource;
import javax.ejb.Timer;
import javax.ejb.SessionContext;
import javax.ejb.Stateless;
import javax.ejb.TimedObject;
import javax.ejb.TimerHandle;
import javax.ejb.TimerService;
@Stateless
public class EJBTimerBean implements EJBTimerLocal, TimedObject {
private SessionContext sc;
private TimerHandle timerHandle = null;
public void ejbTimeout(javax.ejb.Timer arg0) {
java.util.logging.Logger.getLogger(getClass().getName()).log(
java.util.logging.Level.INFO,
"CMS0000: EJBTimerBean: Tick");
}
public void ejbPostCreate() {
// This does not work:
Date now = new Date();
initializeTimer(now, 30000, "CMS_TIMER");
}
public void initializeTimer(Date firstDate, long timeout, String timerName) {
try {
TimerService ts = sc.getTimerService();
Timer timer = ts.createTimer(firstDate, timeout, timerName);
timerHandle = timer.getHandle();
java.util.logging.Logger.getLogger(getClass().getName()).log(
java.util.logging.Level.INFO,
"Timer started.");
} catch (Exception e) {
e.printStackTrace();
}
}
@Resource
public void setSessionContext(SessionContext ctx) {
sc = ctx;
}
}
Be sure the timer includes the setSessionContext() methods, with the @Resource annotation. Also, initializeTimer() must be declared in the bean's interface.
Here is the servlet, whose only purpose in life is to start the timer (note that some basic servlet code has been left out, it's not important for the example at hand).
package com.me.servlets;
import com.iwsinc.cms.ebj.EJBTimerBean;
import com.iwsinc.cms.ebj.EJBTimerLocal;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import javax.ejb.EJB;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Watch extends HttpServlet {
@EJB
private EJBTimerLocal eJBTimerBean;
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
public void init(ServletConfig config) throws ServletException {
super.init(config);
// Start the timer
// to-do: MAKE SURE only one timer is started!!
try {
eJBTimerBean.initializeTimer(new Date(), 30000, "CMS_TIMER");
}
catch (Exception e) {
java.util.logging.Logger.getLogger(getClass().getName()).log(
java.util.logging.Level.SEVERE,
"Exception", e);
e.printStackTrace();
}
}
public void contextInitialized(ServletContextEvent sce) {
}
public void contextDestroyed(ServletContextEvent sce) {
try {
// to-do: Stop the timer
} catch (Exception e) {
e.printStackTrace();
}
}
}
It took me awhile to figure out that to do this (this way), the EJB and the servlet must end up in the same ear file, that the annotation in the servlet for the EJB needs to be the interface, not the bean itself, and that the EJB needs to get its SessionContext using a @Resource annotated method, just as shown above.
On deployment of the web application, an exception is thrown by the EJB. The cause is not known at this time. i found a handful of questions on the various forums asking about this. This exception does not seem to impact the timer in anyway, as far as I can tell.
java.lang.IllegalArgumentException: "timers"
com.sun.enterprise.management.support.OldTypesBase.oldObjectNameToJ2EEType(OldTypesBase.java:153)
com.sun.enterprise.management.support.oldconfig.OldProps.(OldProps.java:187)
com.sun.enterprise.management.support.LoaderOfOldMonitor.oldToNewObjectName(LoaderOfOldMonitor.java:265)
com.sun.enterprise.management.support.LoaderOfOld.syncWithOld(LoaderOfOld.java:415)
com.sun.enterprise.management.support.Loader._sync(Loader.java:548)
com.sun.enterprise.management.support.Loader.sync(Loader.java:522)
com.sun.enterprise.management.support.Loader.handleMBeanRegistered(Loader.java:209)
com.sun.enterprise.management.support.LoaderRegThread.processRegistration(LoaderRegThread.java:204)
com.sun.enterprise.management.support.LoaderRegThread.process(LoaderRegThread.java:253)
com.sun.enterprise.management.support.LoaderRegThread.run(LoaderRegThread.java:154)