Author: andreas@jboss.org>
<As part of the JMX specification each JMX compliant agent must provide a timer service to trigger notifications sent at a certain time, in a certain interval and/or number of occurrences. Therefore you can check for mails, check for changes on a target (auto deployer) or notify the client about a date.
The Timer Service in a JMX Agent is implemented by the Timer MBean. In this HowTo it means the same whereas Timer Service more stresses the fact of being an Agent Service and Timer MBean more stresses the fact of being an MBean.
An Event-Item is an added "Notification" on the Timer MBean. Please note that this is not the same as the adding of a Notification Listener on the JMX Agent which is also the reason to name it differently here. Whenever a client creates a new Event-Item through the various addNotification methods it will send one or more notifications to the registered notification listener of the Timer MBean.
Every client who wants to receive notification about an "Event Item" must register a Notification Listener. The Notification Listener can belong to another client than the one adding the Event Item(s). Each Notification Listener will without filtering receiver all the notifications triggered by all added Event Items on this Timer MBean. As example two clients added one Event Item and one Notification Listener. Now both Notification Listeners will receive all the notifications triggered by the two Event Items and not only the notifications trigger from their Event Item.
A notification triggered by an added Event Item and without filtering sent to all Notification Listener of this Timer MBean.
Just to make it perfectly clear: Each Timer MBean manages a list of Event Items which forces the Timer MBean to send Notifications at the given time and as many times as specified to all the Notification Listener registered to listen for Notification from this Timer MBean instance. Please use a Notification Filter to reduce the Timer Notification to what you are interested into especially when you are on a remote server.
To use the Timer Service we have to prepare the environment. Whenever you are using the JBoss application server you can skip this section and jump to Timer Setup. If not then you have to do one of the following steps:
Whenever your application does not create a MBeanServer beforehand you have to create one now. Note that you can have more than one MBeanServer running in the same JVM at the same time but normally you only need one MBeanServer instance. Therefore when you are sure that you don't have one now or you need a new one create a MBeanServer this way:
MBeanServer lServer = MBeanServerFactory.createMBeanServer();
Whenever there is already a MBeanServer running to find and select a MBeanServer by:
// Get a list of all locally registered MBeanServers List lServers = MBeanServerFactory.findMBeanServer( null ); // Select one. Here it is just the first one MBeanServer lServer = (MBeanServer) lServers.get( 0 );
You can also use the Timer on a remote site but then you have to connect to the remote MBeanServer by using a Connector. Using the RMI-Connector would look like this:
// Connect to a remote MBeanServer by using the RMI-Connector (RMI-Adaptor must be looked up beforehand) RemoteMBeanServer lServer = new RMIConnectorImpl( lRMIAdaptor );Using the EJB-Connector would look like this:
// Connect to a remote MBeanServer by using the EJB-Connector (EJB-Adaptor must be looked up beforehand) RemoteMBeanServer lServer = new EJBConnector( lEJBAdaptor );
To use the Timer service a Timer MBean must be created, registered and activated. There is no need to create a Timer MBean each time you want to use the Timer Service but in order to receive the Timer Notifications you have to register a Notification Listner on this Timer MBean which in turn sends you all the Timer Notifications triggered by any Event Item. But you can supply a Notification Filter to receive only Time Notifications you want to receive.
The Timer MBean should always be started when JBoss is started then add it to the jboss-service.mxl file in the current configuration directory (under /conf). Please add this line but keep in mind that you can change the value of attribute "name" whenever you like but it must be unique on the current MBeanServer:
<mbean code="javax.management.timer.Timer" name="DefaultDomain:service=timer"/>
The other approach with JBoss is to create your own service.xml giving you the chance to deploy it at runtime and don't have the burden to change the root jboss-service.xml file.
<?xml version="1.0" encoding="UTF-8"?> <server> <!-- ==================================================================== --> <!-- JMX Timer MBean --> <!-- ==================================================================== --> <mbean code="javax.management.timer.Timer" name="DefaultDomain:service=timer"/> </server>
When you don't have the JBoss application server running then you have to create, register and start the Timer MBean by your own using the MBeanServer or RemoteMBeanSErver interface. To create and register the Timer MBean:
ObjectInstance lTimer = lServer.createMBean( "javax.management.timer.Timer", new ObjectName( "DefaultDomain", "service", "timer" ) );To start the Timer MBean which is always necessary here (JBoss is doing this for you):
lServer.invoke( lTimer.getObjectName(), "start", new Object[] {}, new String[] {} );Of course you can also use an Adaptor like the HTML-Adaptor to create and setup the Timer MBean.
Procedure 13.2. Procedure to use the Timer Service
Now we are ready to configure the Timer Service and to finally use it. Both steps could be done without an order but for the Notification Filter we need the ID number of the Event Item. Therefore the Event Item should be added first. If you don't use the Notification Filter or filter on the client side then you can perform the second step as the first step, too.
First add the Event Item to the Timer Service. Keep the number of the Event Item because it is necessary for the Notification Filter. Because there are different ways to register an Event Item let us go through them one by one.
This code just adds an Event Item which sends a Time Notification once after one minute from now.
Date lNext = new Date( new Date().getTime() + Timer.ONE_MINUTE ); Integer lOneMinuteTimer = (Integer) lServer.invoke( lTimer.getObjectName(), "addNotification", new Object[] { "IDoNotKnowWhatTypeIs", "I call you with this timer once", // No user object null, // In one minute from now lNext, }, new String[] { "".getClass().getName(), "".getClass().getName(), "java.lang.Object", Date.class.getName() } );
This code sets up an Event Item which sends ten Time Notifications one minute apart starting after one hour from now.
Date lNext = new Date( new Date().getTime() + Timer.ONE_HOUR ); Integer lOneHourTimer = (Integer) lServer.invoke( lTimer.getObjectName(), "addNotification", new Object[] { "IDoNotKnowWhatTypeIs", "I call you with this timer ten times", // No user object null, // In one hour from now lNext, new Long( Timer.ONE_MINUTE ), new Long( 10 ) }, new String[] { "".getClass().getName(), "".getClass().getName(), "java.lang.Object", Date.class.getName(), Long.TYPE.getName(), Long.TYPE.getName() } );
This will remove an Event Item by its ID
lServer.invoke( lTimer.getObjectName(), "removeNotification", new Object[] { // You could also use the type: "IDoNotKnowWhatTypeIs" lOneHourTimer }, new String[] { // If you remove by type: String.getClass().getName() Integer.TYPE.getName() } );
Now we have to register a Notification Listener at the JMX Agent to retrieve the Time Notifications. The Notification Filter should be used whenever you have more than one Event Item added to the Timer Service. The filter should only pass Time Notification the client is interested into (it is assumed here that the Id of the Event Item (above it would be "lOneMinuteTimer" or "lOneHourTimer") is stored in the class variable "mTimerId").
lServer.addNotificationListener( lTimer.getObjectName(), // Notification Listener new NotificationListener() { public void handleNotification( Notification pNotification, Object pHandback ) { // Add here you call any code or application or perform whatever you want to System.out.println( "You got a Notification: " + pNotification ); } }, // Filter Listener new NotificationFilter() { public boolean isNotificationEnabled( Notification pNotification ) { if( pNotification instanceof TimerNotification ) { TimerNotification lTimerNotification = (TimerNotification) pNotification; return lTimerNotification.getNotificationID().equals( mTimerId ); } return false; } }, // No object handback necessary null );
The JBoss Scheduler service enables you to use the Timer Service easier and to call any instance implementing the Schedulable interface.
To use the Scheduler you need a target which the Scheduler will call when the Timer sends a notification based on your settings. This can be either an instance of a class implementing the Schedulable interface (which will be created by the Scheduler) or another MBean available in the JMX MBeanServer of JBoss. From now on we will call this a Schedulable (class / instance or MBean).
One way to use the Scheduler service is to provide a class that implements the Schedulable interface. When the Scheduler receives a Notification from the Timer it will call the Schedulable's perform() method. This method takes 2 paramaters: the first is the time that the notification was sent; the second is the number of the remain occurences(where -1 means no limit).
The example coming with the Scheduler what a Schedulable class should look like is the internal class SchedulableExample. It just takes a name and a value used to identify the log when a timed call comes in. To create a Schedulable class just build a Constructor taking basic data types, String or any other class having a constructor taking a single String as parameter. Then implement the perform method where you can use the given date of call, number of remaining repetitions and the value set by the constructor. The perform method can start backups, nightly data transfers, purge old data from the DB etc. The default example looks like this:
public static class SchedulableExample implements Schedulable { private String mName; private int mValue; public SchedulableExample( String pName, int pValue ) { mName = pName; mValue = pValue; } /** * Just log the call **/ public void perform( Date pTimeOfCall, long pRemainingRepetitions ) { System.out.println( "Schedulable Examples is called at: " + pTimeOfCall + ", remaining repetitions: " + pRemainingRepetitions + ", test, name: " + mName + ", value: " + mValue ); } }
The last step of the preparation is that all the new classes coming along with your Schedulable must be made available to the JMX agent. Therefore you have to add it to the classpath or with JBoss 3 reference the JAR file in the service.xml file.
The other way is to tell the Scheduler which method on which MBean schould be called by the Scheduler service when it receives a Notification from the Timer. The MBean must be loaded, created and started when the Scheduler calls the MBean for the first time. If not available the Scheduler will count this call (decrease the remaining repetitions if not endless) but the call is not performed.
The Scheduler is programmed in a way that you only have to configure and start it to use it. The next code snippet can be used to add it to jboss-service.xml file or create your own scheduler-service.xml file. When you use a JMX Connector then you can create the Scheduler MBean, set the Attributes and then invoke the init and start methods (please have a look at the timer-usage above for more informations). The Scheduler can be setup in two ways.
To setup a Scheduler in a service.xml file using a Schedulable class add this code snippet and adjust it to your needs:
<mbean code="org.jboss.util.Scheduler" name=":service=Scheduler"> <attribute name="SchedulableClass">org.jboss.util.Scheduler$SchedulableExample</attribute> <attribute name="SchedulableArguments">Schedulabe Test,12345</attribute> <attribute name="SchedulableArgumentTypes">java.lang.String,int</attribute> <attribute name="InitialStartDate">0</attribute> <attribute name="SchedulePeriod">10000</attribute> <attribute name="InitialRepetitions">-1</attribute> <attribute name="StartAtStartup">true</attribute> </mbean>
To setup a Scheduler in a service.xml file using a Schedulable MBean add this code snippet and adjust it to your needs:
<mbean code="org.jboss.util.SchedulableExample" name="jboss:type=example,name=schedulable"> </mbean> <mbean code="org.jboss.util.Scheduler" name="jboss:service=Scheduler"> <attribute name="StartAtStartup">true</attribute> <attribute name="SchedulableMBean">jboss:type=example,name=schedulable</attribute> <attribute name="SchedulableMBeanMethod">hit( NOTIFICATION, DATE, REPETITIONS, SCHEDULER_NAME, java.lang.String )</attribute> <attribute name="InitialStartDate">NOW</attribute> <attribute name="SchedulePeriod">10000</attribute> <attribute name="InitialRepetitions">10</attribute> </mbean>Note that the first MBean is the target (Schedulable) MBean which is created before the Scheduler. This is not necessary especially when you want to use JBoss MBeans but they should be available when the first hit (see initial start date) from the Timer is in.
Do NOT mix these two types. The last call to either setSchedulableClass or setSchedulableMBean will determine if a Schedulable Class or MBean is used.
Full qualified path to the class implementing the Schedulable interface. Whenever a Time Notification is sent to the Scheduler the Schedulable's perform method will be called. Your Schedulable instance can then peform any necessary steps you have to perform at a certain time.
Contains the text representation of the attributes to create the Schedulable instance. If the needs arise I will also add a way to create a Schedulable MBean and pass the JMX Object Name instead (see J2eeDeployer) instead.
Data types of the constructor you want to use to create the Schedulable instance. The number and type must match with the given InitArguments above. It contains Name of the basic data type (int, long, byte, char, float, double) or the fully qualified path of a class like java.lang.String which must have a constructor taking a single String argument.
Object Name of the MBean you want to be called when timer send a notification. Note that this must not be null and must be a valid JMX Object Name.
Can contain two parts. The first is the method name which must be in front of the opening bracket. The second is inside the opening and closing bracket. There must not be any other characters after the closing bracket except white spaces which will be trimmed away with java.lang.String.trim(). The second part must be a comma seperated list of the following items (if written in uppercase then this literal without the colon):
NOTIFICATION: Timer Notification object sent by the Timer. The signature is: javax.management.Notification.
DATE: Date instance when the timer sent the call. The signature is: java.util.Date.
REPETITIONS: Number of remaining repetitions. The signature is: long.
SCHEDULER_NAME: Object Name of the Scheduler calling the MBean. With this Object Name you can call back and investigate the Scheduler. The signature is: javax.management.ObjectName.
Anything else will be treated as fully qualified Class name where the Scheduler send a "null" as value but this Class as signature. This method is here to satisfy your method signature.
You have now 3 ways to set the Initial Start Date:
The reason to do so is to allow an administrator to restart the JBoss application server without adjusting the Scheduler every time. If for example you started a Scheduler on Monday to run every day at 12:00pm for a week (7 times) and you restart JBoss on Thursday after 12:00pm the same week then the Scheduler will run for Friday, Saturday and Sunday at 12:00pm.
You can define the number of repetitions of timed calls. If you specify -1 then there is no limit. When all the calls are sent the Scheduler falls into hibernation because when no Time Notification is sent nothing is going to happen. But then you can either reuse the Scheduler or remove the MBean from the JMX Agent.
Either "true" or "false" indicats if the Scheduler should be started after creation or it should wait until you are ready and start the Scheduler by hand (HTML-Adaptor), another JMX MBean or a client using JMX Connector/Adaptor.
02:12:00,908 ERROR [Default] java.lang.NullPointerException 02:12:00,908 ERROR [Default] at javax.management.timer.Timer.updateTimerTable(Timer.java:984) 02:12:00,908 ERROR [Default] at javax.management.timer.Timer.notifyAlarmClock(Timer.java:1093) 02:12:00,918 ERROR [Default] at javax.management.timer.TimerAlarmClock.run(Timer.java:1165)Please just ignore this message because it is catched and then reported inside JMX. It seems to be a bug from a bug fix of Sun.
The Timer MBean is not that difficult to use as soon as you understand what the differences are compared to other scheduling services like CRON. If you are just interested to get the hit after a certain time with a certain number of repetitions the Scheduler is an easy way to use the Timer MBean. The Scheduler hides all the details of the Timer and you can focus on the implementation of what you have to perform when time is up.
If anything is wrong or not correct please contact me at andreas@jboss.org. Also if you want to know more in detail or have a request for further infos.