How To use the Timer MBean and Scheduler Service on JBoss 3

Author: Andreas Schaefer <andreas@jboss.org>

Introduction

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.

Note

The Time Service only notifies the client that time is up and does not start any script or does anything else. It is up to the client implementation to perform whatever he/she has in mind. This is the big difference to other timely services like CRON etc.
Timer Service, Timer MBean

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.

Event Item

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.

Timer Notification Listener

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.

Important

Always deliver a Notification Filter to receive only the notifications you are interested into because the filter reduces the traffic to the client and the Notification Listener does not have to figure out which is "his" Notifications and which not.

Time Notification

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.

Preparation

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:

  • Create a MBeanServer

  • Find and select the appropriate MBeanServer

  • Connect to a remote MBeanServer

Create a MBeanServer

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();
            

Find and select a MBeanServer

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 );
            

Connect to a remote MBeanServer

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 );
            

Timer - Setup

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.

Note

Each client listening for notifications from a Timer MBean gets all notifications from all added time notifications. Therefore the client has to figure out which notification is for which Event Item.

Note

You only have to create a Timer MBean when there is no Timer MBean registered on the actual MBeanServer.
To create a Timer MBean you have 3 choices. The first two only work when used in conjunction with a JBoss application server 3. The last one works always.

  • Add it to jboss-service.xml

  • Use a Timer service.xml file

  • Create the Timer through the MBeanServer

Add it to jboss-service.xml

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"/>
            

Note

There is no need to add any classes to the classpath or classloader because the class is part of the JMX-Reference Implementation.

Use a Timer service.xml file

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.

Important

Ensure that the name of the service.xml is unique within all your deployment directories (by default "/deploy" and "/deploy/lib").
The timer-service.xml would look like this
<?xml version="1.0" encoding="UTF-8"?>

<server>

  <!-- ==================================================================== -->
  <!-- JMX Timer MBean                                                      -->
  <!-- ==================================================================== -->

   <mbean code="javax.management.timer.Timer" name="DefaultDomain:service=timer"/>

</server>
            

Note

Keep in mind that there will be no notification about the startup. To check use the JMX HTML-Adaptor on port 8082 by default. The timer is loaded from the jmxri.jar file in /lib directory.

Create the Timer through the MBeanServer

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.

Usage of a Timer MBean

Procedure 11.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.

  1. 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() 
            }
         );
                           
  2. 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
       );
                   

    Note

    Here both listener as well as filter are implemented as anonymous classes but this is not necessary. Have a look at the Scheduler to see how regular classes can be used.

Scheduler Preparation

The JBoss Scheduler service enables you to use the Timer Service easier and to call any instance implementing the Schedulable interface.

Note

One Scheduler Instance is to schedule one task. Therefore to schedule more than one task create more than one Scheduler MBeans.

Note

Every Scheduler Instance can be reused by reseting the MBean attributes but your old schedule is then lost.

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).

  1. 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.

  2. 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.

Using the Scheduler

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.

  1. 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>
                      

  2. 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.

Important

Do NOT mix these two types. The last call to either setSchedulableClass or setSchedulableMBean will determine if a Schedulable Class or MBean is used.

SchedulableClass

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.

InitArguments

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.

Note

Only basic data types, Strings and objects taking a String a single argument of the constructor are supported now. Objects are created with respect to the given argument types below.

Note

Arguments are separated by a comma and therefore you must not use commas in the attribute value (escaping is not supported yet).

InitTypes

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.

SchedulableMBean

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.

SchedulableMBeanMethod

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.

When you omit the method name "perform" will be set. If you omit the parameter list (second part) then the same signature as perform of the Schedulable instance will be assumed.

InitialStartDate

You have now 3 ways to set the Initial Start Date:

  • NOW: Current date when the Scheduler is started
  • Milliseconds since 1/1/1970: Date (in milliseconds since 1/1/1970) when the Timer sends the first Time Notification.
  • Date Pattern as String: Date as a String understood by the java.text.SimpleDateFormat (please check your environment). For US this looks like this: 2/5/02 11:15 am (Fith of February 2002, eleven fifeteen in the morning).
Note that when a date is set in the past the Scheduler will see if the Schedule would have a hit in the future with the given period and number of repetitions. If the number of repetitions is endless the Scheduler will start at the first scheduled time in the future with respect to the start date and period. Otherwise if a hit is in the future the Schedule will schedule this one but also reduce the number of hits which were in the past. If no hit is in the future the Schedule will be inactive even thought it is started.

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.

NrOfRepetitions

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.

StartAtStartup

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.

Note

The first version of the Scheduler passed these values as constructor arguments instead of using the named attributes here. In this new version it is not necessary but you can still pass the arguments to the constructor directly.

Note

The Scheduler can be reused when the original schedule is not used anymore or the number of repetitions felt to 0 meaning no Time Notifications are sent anyhow. But whenever you change the attributes of the Scheduler you have to restart the Scheduler thus the change becomes effective. To restart use the stopScheduler and startSchedule methods. The stopScheduler method needs a boolean argument indicating if Scheduler should be stopped immediately or wait until the next hit is performed. Whenever you stop the Scheduler with argument "false" the isRestartPending returns "true" until the Scheduler is stopped. The other stopService belongs to the life-cycle of the MBean and should only be used in this context.

Note

The Scheduler is showing this message when the Scheduler remaining repetitions went to 0 and became inActive:
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.

Conclusion

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.

Наши друзья