KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > varia > scheduler > Scheduler


1 /*
2  * JBoss, Home of Professional Open Source
3  * Copyright 2005, JBoss Inc., and individual contributors as indicated
4  * by the @authors tag. See the copyright.txt in the distribution for a
5  * full listing of individual contributors.
6  *
7  * This is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as
9  * published by the Free Software Foundation; either version 2.1 of
10  * the License, or (at your option) any later version.
11  *
12  * This software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this software; if not, write to the Free
19  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21  */

22 package org.jboss.varia.scheduler;
23
24 import java.lang.reflect.Constructor JavaDoc;
25 import java.security.InvalidParameterException JavaDoc;
26 import java.text.SimpleDateFormat JavaDoc;
27 import java.util.Date JavaDoc;
28 import java.util.StringTokenizer JavaDoc;
29 import java.util.Vector JavaDoc;
30 import java.util.Arrays JavaDoc;
31
32 import javax.management.InstanceNotFoundException JavaDoc;
33 import javax.management.MalformedObjectNameException JavaDoc;
34 import javax.management.Notification JavaDoc;
35 import javax.management.NotificationEmitter JavaDoc;
36 import javax.management.NotificationListener JavaDoc;
37 import javax.management.ObjectName JavaDoc;
38 import javax.management.MBeanServerInvocationHandler JavaDoc;
39 import javax.management.timer.Timer JavaDoc;
40 import javax.management.timer.TimerMBean JavaDoc;
41 import javax.management.timer.TimerNotification JavaDoc;
42
43 import org.jboss.logging.Logger;
44 import org.jboss.system.ServiceMBeanSupport;
45 import org.jboss.util.Classes;
46
47 /**
48  * Schedules a timer task that calls an MBean or Object instance.
49  * Any MBean operation can be called. Object instances must implement the
50  * {@link Schedulable} interface.
51  * <p />
52  * Create a separate Scheduler MBean for every MBean or Object you wish to call.
53  * One example naming strategy for calling an MBean named:
54  <code>example:type=HelloWorld</code>
55  * is to create a similarly named:
56  <code>example:type=Scheduler,call=HelloWorld</code> MBean.
57  * This way you should not run into a name conflict.
58  * <p>
59  * This MBean registers a notification listener with an
60  * javax.management.timer.Timer MBean. If the Timer does not exist, this MBean
61  * will create it. Each Timer can handle multiple Scheduler instances.
62  * </p>
63  *
64  * @author <a HREF="mailto:andreas@jboss.org">Andreas Schaefer</a>
65  * @author Cameron (camtabor)
66  * @version $Revision: 58158 $
67  */

68 public class Scheduler extends ServiceMBeanSupport
69    implements SchedulerMBean
70 {
71
72    // -------------------------------------------------------------------------
73
// Constants
74
// -------------------------------------------------------------------------
75

76    public static String JavaDoc JNDI_NAME = "scheduler:domain";
77    public static String JavaDoc JMX_NAME = "scheduler";
78    public static String JavaDoc DEFAULT_TIMER_NAME = ScheduleManager.DEFAULT_TIMER_NAME;
79
80    private static final int NOTIFICATION = 0;
81    private static final int DATE = 1;
82    private static final int REPETITIONS = 2;
83    private static final int SCHEDULER_NAME = 3;
84    private static final int NULL = 4;
85
86    // -------------------------------------------------------------------------
87
// Members
88
// -------------------------------------------------------------------------
89

90    private long mActualSchedulePeriod;
91    private long mRemainingRepetitions = 0;
92    private int mNotificationID = -1;
93    private String JavaDoc mTimerName = DEFAULT_TIMER_NAME;
94    private ObjectName JavaDoc mTimerObjectName;
95    private TimerMBean JavaDoc mTimer;
96    private NotificationEmitter JavaDoc mTimerEmitter;
97    private Schedulable mSchedulable;
98
99    private boolean mScheduleIsStarted = false;
100    private boolean mWaitForNextCallToStop = false;
101    private boolean mStartOnStart = false;
102    private boolean mIsRestartPending = true;
103
104    // Pending values which can be different to the actual ones
105
private boolean mUseMBean = false;
106
107    private Class JavaDoc mSchedulableClass;
108    private String JavaDoc mSchedulableArguments;
109    private String JavaDoc[] mSchedulableArgumentList = new String JavaDoc[0];
110    private String JavaDoc mSchedulableArgumentTypes;
111    private Class JavaDoc[] mSchedulableArgumentTypeList = new Class JavaDoc[0];
112
113    private ObjectName JavaDoc mSchedulableMBean;
114    private String JavaDoc mSchedulableMBeanMethod;
115    private String JavaDoc mSchedulableMBeanMethodName;
116    private int[] mSchedulableMBeanArguments = new int[0];
117    private String JavaDoc[] mSchedulableMBeanArgumentTypes = new String JavaDoc[0];
118
119    private SimpleDateFormat JavaDoc mDateFormatter;
120    private Date JavaDoc mStartDate;
121    private String JavaDoc mStartDateString;
122    private boolean mStartDateIsNow;
123    private long mSchedulePeriod;
124    private long mInitialRepetitions;
125    private boolean mFixedRate = false;
126    
127    private NotificationListener JavaDoc mListener;
128
129    // -------------------------------------------------------------------------
130
// Constructors
131
// -------------------------------------------------------------------------
132

133    /**
134     * Constructs a new Scheduler instance.
135     **/

136    public Scheduler()
137    {
138    }
139
140    /**
141     * Constructs a new Scheduler instance.
142     * @param pSchedulableClass
143     * @param pSchedulePeriod
144     */

145    public Scheduler(String JavaDoc pSchedulableClass,
146       long pSchedulePeriod)
147    {
148       setStartAtStartup(true);
149       setSchedulableClass(pSchedulableClass);
150       setSchedulePeriod(pSchedulePeriod);
151    }
152
153    /**
154     * Constructs a new Scheduler instance.
155     * @param pSchedulableClass
156     * @param pInitArguments
157     * @param pInitTypes
158     * @param pInitialStartDate
159     * @param pSchedulePeriod
160     * @param pNumberOfRepetitions
161     */

162    public Scheduler(String JavaDoc pSchedulableClass,
163       String JavaDoc pInitArguments,
164       String JavaDoc pInitTypes,
165       String JavaDoc pInitialStartDate,
166       long pSchedulePeriod,
167       long pNumberOfRepetitions
168       )
169    {
170       setStartAtStartup(true);
171       setSchedulableClass(pSchedulableClass);
172       setSchedulableArguments(pInitArguments);
173       setSchedulableArgumentTypes(pInitTypes);
174       setInitialStartDate(pInitialStartDate);
175       setSchedulePeriod(pSchedulePeriod);
176       setInitialRepetitions(pNumberOfRepetitions);
177    }
178
179    /**
180     * Constructs a new Scheduler instance.
181     * @param pSchedulableClass
182     * @param pInitArguments
183     * @param pInitTypes
184     * @param pDateFormat
185     * @param pInitialStartDate
186     * @param pSchedulePeriod
187     * @param pNumberOfRepetitions
188     */

189    public Scheduler(
190       String JavaDoc pSchedulableClass,
191       String JavaDoc pInitArguments,
192       String JavaDoc pInitTypes,
193       String JavaDoc pDateFormat,
194       String JavaDoc pInitialStartDate,
195       long pSchedulePeriod,
196       long pNumberOfRepetitions
197       )
198    {
199       setStartAtStartup(true);
200       setSchedulableClass(pSchedulableClass);
201       setSchedulableArguments(pInitArguments);
202       setSchedulableArgumentTypes(pInitTypes);
203       setDateFormat(pDateFormat);
204       setInitialStartDate(pInitialStartDate);
205       setSchedulePeriod(pSchedulePeriod);
206       setInitialRepetitions(pNumberOfRepetitions);
207    }
208
209    // -------------------------------------------------------------------------
210
// SchedulerMBean Methods
211
// -------------------------------------------------------------------------
212
//
213

214    private void checkMBean() {
215       if (mSchedulableMBean == null)
216       {
217          log.debug("Schedulable MBean Object Name is not set");
218          throw new InvalidParameterException JavaDoc(
219             "Schedulable MBean must be set"
220          );
221       }
222       if (mSchedulableMBeanMethodName == null)
223       {
224          mSchedulableMBeanMethodName = "perform";
225          mSchedulableMBeanArguments = new int[]{DATE, REPETITIONS};
226          mSchedulableMBeanArgumentTypes = new String JavaDoc[]{
227             Date JavaDoc.class.getName(),
228             Integer.TYPE.getName()
229          };
230       }
231    }
232
233    private void createSchedulable() {
234       if (mSchedulableClass == null)
235       {
236          throw new InvalidParameterException JavaDoc("Schedulable Class not set");
237       }
238       if (mSchedulableArgumentList.length != mSchedulableArgumentTypeList.length)
239       {
240          throw new InvalidParameterException JavaDoc(
241             "Schedulable Class Arguments and Types do not match in length"
242          );
243       }
244       // Create all the Objects for the Constructor to be called
245
Object JavaDoc[] lArgumentList = new Object JavaDoc[mSchedulableArgumentTypeList.length];
246       try
247       {
248          for (int i = 0; i < mSchedulableArgumentTypeList.length; i++)
249          {
250             Class JavaDoc lClass = mSchedulableArgumentTypeList[i];
251             if (lClass == Boolean.TYPE)
252             {
253                lArgumentList[i] = new Boolean JavaDoc(mSchedulableArgumentList[i]);
254             }
255             else if (lClass == Integer.TYPE)
256             {
257                lArgumentList[i] = new Integer JavaDoc(mSchedulableArgumentList[i]);
258             }
259             else if (lClass == Long.TYPE)
260             {
261                lArgumentList[i] = new Long JavaDoc(mSchedulableArgumentList[i]);
262             }
263             else if (lClass == Short.TYPE)
264             {
265                lArgumentList[i] = new Short JavaDoc(mSchedulableArgumentList[i]);
266             }
267             else if (lClass == Float.TYPE)
268             {
269                lArgumentList[i] = new Float JavaDoc(mSchedulableArgumentList[i]);
270             }
271             else if (lClass == Double.TYPE)
272             {
273                lArgumentList[i] = new Double JavaDoc(mSchedulableArgumentList[i]);
274             }
275             else if (lClass == Byte.TYPE)
276             {
277                lArgumentList[i] = new Byte JavaDoc(mSchedulableArgumentList[i]);
278             }
279             else if (lClass == Character.TYPE)
280             {
281                lArgumentList[i] = new Character JavaDoc(mSchedulableArgumentList[i].charAt(0));
282             }
283             else
284             {
285                Constructor JavaDoc lConstructor = lClass.getConstructor(new Class JavaDoc[]{String JavaDoc.class});
286                lArgumentList[i] = lConstructor.newInstance(new Object JavaDoc[]{mSchedulableArgumentList[i]});
287             }
288          }
289       }
290       catch (Exception JavaDoc e)
291       {
292          log.error("Could not load or create constructor argument", e);
293          throw new InvalidParameterException JavaDoc("Could not load or create a constructor argument");
294       }
295       try
296       {
297          // Check if constructor is found
298
Constructor JavaDoc lSchedulableConstructor = mSchedulableClass.getConstructor(mSchedulableArgumentTypeList);
299          // Create an instance of it
300
mSchedulable = (Schedulable) lSchedulableConstructor.newInstance(lArgumentList);
301       }
302       catch (Exception JavaDoc e)
303       {
304          log.error("Could not find the constructor or create Schedulable instance", e);
305          throw new InvalidParameterException JavaDoc("Could not find the constructor or create the Schedulable Instance");
306       }
307    }
308
309    private Date JavaDoc getNow() {
310       long now = System.currentTimeMillis();
311       return new Date JavaDoc(now + 1000);
312    }
313
314    private void initStartDate() {
315       // Register the Schedule at the Timer
316
// If start date is NOW then take the current date
317
if (mStartDateIsNow)
318       {
319          mStartDate = getNow();
320       }
321       else
322       {
323          // Check if initial start date is in the past
324
if (mStartDate.before(new Date JavaDoc()))
325          {
326             // If then first check if a repetition is in the future
327
long lNow = new Date JavaDoc().getTime() + 100;
328             long lSkipRepeats = ((lNow - mStartDate.getTime()) / mActualSchedulePeriod) + 1;
329             log.debug("Old start date: " + mStartDate + ", now: " + new Date JavaDoc(lNow) + ", Skip repeats: " + lSkipRepeats);
330             if (mRemainingRepetitions > 0)
331             {
332                // If not infinit loop
333
if (lSkipRepeats >= mRemainingRepetitions)
334                {
335                   // No repetition left -> exit
336
log.info("No repetitions left because start date is in the past and could " +
337                      "not be reached by Initial Repetitions * Schedule Period");
338                   return;
339                }
340                else
341                {
342                   // Reduce the missed hits
343
mRemainingRepetitions -= lSkipRepeats;
344                }
345             }
346             mStartDate = new Date JavaDoc(mStartDate.getTime() + (lSkipRepeats * mActualSchedulePeriod));
347          }
348       }
349    }
350
351    /**
352     * Starts the schedule if the schedule is stopped otherwise nothing will happen.
353     * The Schedule is immediately set to started even the first call is in the
354     * future.
355     *
356     * @jmx:managed-operation
357     *
358     * @throws InvalidParameterException If any of the necessary values are not set
359     * or invalid (especially for the Schedulable
360     * class attributes).
361     */

362    public void startSchedule()
363    {
364       if (isStarted())
365       {
366          log.debug("already started");
367          return;
368       }
369
370       if (mUseMBean)
371       {
372          checkMBean();
373       }
374       else
375       {
376          createSchedulable();
377       }
378
379       mRemainingRepetitions = mInitialRepetitions;
380       mActualSchedulePeriod = mSchedulePeriod;
381       initStartDate();
382
383       log.debug("Schedule initial call to: " + mStartDate + ", remaining repetitions: " + mRemainingRepetitions);
384       mNotificationID = mTimer.addNotification(
385             "Schedule", "Scheduler Notification",
386             null, // new Integer(getID()), // User Object
387
mStartDate,
388             new Long JavaDoc(mActualSchedulePeriod),
389             mRemainingRepetitions < 0 ? new Long JavaDoc(0) : new Long JavaDoc(mRemainingRepetitions),
390             Boolean.valueOf(mFixedRate)
391       );
392       mListener = mUseMBean ? new MBeanListener() : new PojoScheduler();
393       mTimerEmitter.addNotificationListener(
394             mListener,
395             new ScheduleManager.IdNotificationFilter(mNotificationID),
396             null
397       );
398       mScheduleIsStarted = true;
399       mIsRestartPending = false;
400    }
401
402    /**
403     * Stops the schedule immediately.
404     * @jmx:managed-operation
405     */

406    public void stopSchedule()
407    {
408       stopSchedule(true);
409    }
410
411    /**
412     * Stops the schedule because it is either not used anymore or to restart it with
413     * new values.
414     *
415     * @jmx:managed-operation
416     *
417     * @param pDoItNow If true the schedule will be stopped without waiting for the next
418     * scheduled call otherwise the next call will be performed before
419     * the schedule is stopped.
420     */

421    public void stopSchedule(boolean pDoItNow)
422    {
423       log.debug("stopSchedule(" + pDoItNow + ")");
424       try
425       {
426          if (mNotificationID < 0)
427          {
428             mScheduleIsStarted = false;
429             mWaitForNextCallToStop = false;
430             return;
431          }
432          if (pDoItNow)
433          {
434             log.debug("stopSchedule(), removing schedule id: " + mNotificationID);
435             mWaitForNextCallToStop = false;
436             if (mListener != null)
437             {
438                mTimerEmitter.removeNotificationListener(mListener);
439                try
440                {
441                   mTimer.removeNotification(mNotificationID);
442                }
443                catch (InstanceNotFoundException JavaDoc e)
444                {
445                   log.trace(e);
446                }
447                mListener = null;
448             }
449             mNotificationID = -1;
450             mScheduleIsStarted = false;
451          }
452          else
453          {
454             mWaitForNextCallToStop = true;
455          }
456       }
457       catch (Exception JavaDoc e)
458       {
459          log.error("stopSchedule failed", e);
460       }
461    }
462
463    /**
464     * Stops the server right now and starts it right now.
465     *
466     * @jmx:managed-operation
467     */

468    public void restartSchedule()
469    {
470       stopSchedule();
471       startSchedule();
472    }
473
474    /**
475     * @jmx:managed-attribute
476     *
477     * @return Full qualified Class name of the schedulable class called by the schedule or
478     * null if not set.
479     */

480    public String JavaDoc getSchedulableClass()
481    {
482       if (mSchedulableClass == null)
483       {
484          return null;
485       }
486       return mSchedulableClass.getName();
487    }
488
489    /**
490     * Sets the fully qualified Class name of the Schedulable Class being called by the
491     * Scheduler. Must be set before the Schedule is started. Please also set the
492     * {@link #setSchedulableArguments} and {@link #setSchedulableArgumentTypes}.
493     *
494     * @jmx:managed-attribute
495     *
496     * @param pSchedulableClass Fully Qualified Schedulable Class.
497     *
498     * @throws InvalidParameterException If the given value is not a valid class or cannot
499     * be loaded by the Scheduler or is not of instance
500     * Schedulable.
501     */

502    public void setSchedulableClass(String JavaDoc pSchedulableClass)
503       throws InvalidParameterException JavaDoc
504    {
505       if (pSchedulableClass == null || pSchedulableClass.equals(""))
506       {
507          throw new InvalidParameterException JavaDoc("Schedulable Class cannot be empty or undefined");
508       }
509       try
510       {
511          // Try to load the Schedulable Class
512
ClassLoader JavaDoc loader = TCLActions.getContextClassLoader();
513          mSchedulableClass = loader.loadClass(pSchedulableClass);
514          // Check if instance of Schedulable
515
if (!isSchedulable(mSchedulableClass))
516          {
517             String JavaDoc msg = "Given class " + pSchedulableClass + " is not instance of Schedulable";
518             StringBuffer JavaDoc info = new StringBuffer JavaDoc(msg);
519             info.append("\nThe SchedulableClass info:");
520             Classes.displayClassInfo(mSchedulableClass, info);
521             info.append("\nSchedulable.class info:");
522             Classes.displayClassInfo(Schedulable.class, info);
523             log.debug(info.toString());
524             throw new InvalidParameterException JavaDoc(msg);
525          }
526       }
527       catch (ClassNotFoundException JavaDoc e)
528       {
529          log.info("Failed to find: "+pSchedulableClass, e);
530          throw new InvalidParameterException JavaDoc(
531             "Given class " + pSchedulableClass + " is not not found"
532          );
533       }
534       mIsRestartPending = true;
535       mUseMBean = false;
536    }
537
538    /**
539     * @jmx:managed-attribute
540     *
541     * @return Comma seperated list of Constructor Arguments used to instantiate the
542     * Schedulable class instance. Right now only basic data types, String and
543     * Classes with a Constructor with a String as only argument are supported.
544     */

545    public String JavaDoc getSchedulableArguments()
546    {
547       return mSchedulableArguments;
548    }
549
550    /**
551     * @jmx:managed-attribute
552     *
553     * Sets the comma seperated list of arguments for the Schedulable class. Note that
554     * this list must have as many elements as the Schedulable Argument Type list otherwise
555     * the start of the Scheduler will fail. Right now only basic data types, String and
556     * Classes with a Constructor with a String as only argument are supported.
557     *
558     * @param pArgumentList List of arguments used to create the Schedulable intance. If
559     * the list is null or empty then the no-args constructor is used.
560     */

561    public void setSchedulableArguments(String JavaDoc pArgumentList)
562    {
563       if (pArgumentList == null || pArgumentList.equals(""))
564       {
565          mSchedulableArgumentList = new String JavaDoc[0];
566       }
567       else
568       {
569          StringTokenizer JavaDoc lTokenizer = new StringTokenizer JavaDoc(pArgumentList, ",");
570          Vector JavaDoc lList = new Vector JavaDoc();
571          while (lTokenizer.hasMoreTokens())
572          {
573             String JavaDoc lToken = lTokenizer.nextToken().trim();
574             if (lToken.equals(""))
575             {
576                lList.add("null");
577             }
578             else
579             {
580                lList.add(lToken);
581             }
582          }
583          mSchedulableArgumentList = (String JavaDoc[]) lList.toArray(new String JavaDoc[0]);
584       }
585       mSchedulableArguments = pArgumentList;
586       mIsRestartPending = true;
587    }
588
589    /**
590     * @jmx:managed-attribute
591     *
592     * @return A comma seperated list of Argument Types which should match the list of
593     * arguments.
594     */

595    public String JavaDoc getSchedulableArgumentTypes()
596    {
597       return mSchedulableArgumentTypes;
598    }
599
600    /**
601     * Sets the comma seperated list of argument types for the Schedulable class. This will
602     * be used to find the right constructor and to created the right instances to call the
603     * constructor with. This list must have as many elements as the Schedulable Arguments
604     * list otherwise the start of the Scheduler will fail. Right now only basic data types,
605     * String and Classes with a Constructor with a String as only argument are supported.
606     *
607     * @jmx:managed-attribute
608     *
609     * @param pTypeList List of arguments used to create the Schedulable intance. If
610     * the list is null or empty then the no-args constructor is used.
611     *
612     * @throws InvalidParameterException If the given list contains a unknow datat type.
613     */

614    public void setSchedulableArgumentTypes(String JavaDoc pTypeList)
615       throws InvalidParameterException JavaDoc
616    {
617       if (pTypeList == null || pTypeList.equals(""))
618       {
619          mSchedulableArgumentTypeList = new Class JavaDoc[0];
620       }
621       else
622       {
623          StringTokenizer JavaDoc lTokenizer = new StringTokenizer JavaDoc(pTypeList, ",");
624          Vector JavaDoc lList = new Vector JavaDoc();
625          while (lTokenizer.hasMoreTokens())
626          {
627             String JavaDoc lToken = lTokenizer.nextToken().trim();
628             // Get the class
629
Class JavaDoc lClass = null;
630             if (lToken.equals("short"))
631             {
632                lClass = Short.TYPE;
633             }
634             else if (lToken.equals("int"))
635             {
636                lClass = Integer.TYPE;
637             }
638             else if (lToken.equals("long"))
639             {
640                lClass = Long.TYPE;
641             }
642             else if (lToken.equals("byte"))
643             {
644                lClass = Byte.TYPE;
645             }
646             else if (lToken.equals("char"))
647             {
648                lClass = Character.TYPE;
649             }
650             else if (lToken.equals("float"))
651             {
652                lClass = Float.TYPE;
653             }
654             else if (lToken.equals("double"))
655             {
656                lClass = Double.TYPE;
657             }
658             else if (lToken.equals("boolean"))
659             {
660                lClass = Boolean.TYPE;
661             }
662             if (lClass == null)
663             {
664                try
665                {
666                   // Load class to check if available
667
ClassLoader JavaDoc loader = TCLActions.getContextClassLoader();
668                   lClass = loader.loadClass(lToken);
669                }
670                catch (ClassNotFoundException JavaDoc cnfe)
671                {
672                   throw new InvalidParameterException JavaDoc(
673                      "The argument type: " + lToken + " is not a valid class or could not be found"
674                   );
675                }
676             }
677             lList.add(lClass);
678          }
679          mSchedulableArgumentTypeList = (Class JavaDoc[]) lList.toArray(new Class JavaDoc[0]);
680       }
681       mSchedulableArgumentTypes = pTypeList;
682       mIsRestartPending = true;
683    }
684
685    /**
686     * @jmx:managed-attribute
687     *
688     * @return Object Name if a Schedulable MBean is set
689     */

690    public String JavaDoc getSchedulableMBean()
691    {
692       return mSchedulableMBean == null ?
693          null :
694          mSchedulableMBean.toString();
695    }
696
697    /**
698     * Sets the fully qualified JMX MBean name of the Schedulable MBean to be called.
699     * <b>Attention: </b>if set the all values set by {@link #setSchedulableClass},
700     * {@link #setSchedulableArguments} and {@link #setSchedulableArgumentTypes} are
701     * cleared and not used anymore. Therefore only use either Schedulable Class or
702     * Schedulable MBean. If {@link #setSchedulableMBeanMethod} is not set then the
703     * schedule method as in the {@link Schedulable#perform} will be called with the
704     * same arguments. Also note that the Object Name will not be checked if the
705     * MBean is available. If the MBean is not available it will not be called but
706     * the remaining repetitions will be decreased.
707     *
708     * @jmx:managed-attribute
709     *
710     * @param pSchedulableMBean JMX MBean Object Name which should be called.
711     *
712     * @throws InvalidParameterException If the given value is an valid Object Name.
713     */

714    public void setSchedulableMBean(String JavaDoc pSchedulableMBean)
715       throws InvalidParameterException JavaDoc
716    {
717       if (pSchedulableMBean == null)
718       {
719          throw new InvalidParameterException JavaDoc("Schedulable MBean must be specified");
720       }
721       try
722       {
723          mSchedulableMBean = new ObjectName JavaDoc(pSchedulableMBean);
724          mUseMBean = true;
725       }
726       catch (MalformedObjectNameException JavaDoc e)
727       {
728          throw new InvalidParameterException JavaDoc("Schedulable MBean name invalid " + pSchedulableMBean);
729       }
730    }
731
732    /**
733     * @return Schedulable MBean Method description if set
734     **/

735    public String JavaDoc getSchedulableMBeanMethod()
736    {
737       return mSchedulableMBeanMethod;
738    }
739
740    /**
741     * Sets the method name to be called on the Schedulable MBean. It can optionally be
742     * followed by an opening bracket, list of attributes (see below) and a closing bracket.
743     * The list of attributes can contain:
744     * <ul>
745     * <li>NOTIFICATION which will be replaced by the timers notification instance
746     * (javax.management.Notification)</li>
747     * <li>DATE which will be replaced by the date of the notification call
748     * (java.util.Date)</li>
749     * <li>REPETITIONS which will be replaced by the number of remaining repetitions
750     * (long)</li>
751     * <li>SCHEDULER_NAME which will be replaced by the Object Name of the Scheduler
752     * (javax.management.ObjectName)</li>
753     * <li>any full qualified Class name which the Scheduler will be set a "null" value
754     * for it</li>
755     * </ul>
756     * <br>
757     * An example could be: "doSomething( NOTIFICATION, REPETITIONS, java.lang.String )"
758     * where the Scheduler will pass the timer's notification instance, the remaining
759     * repetitions as int and a null to the MBean's doSomething() method which must
760     * have the following signature: doSomething( javax.management.Notification, long,
761     * java.lang.String ).
762     *
763     * @jmx:managed-attribute
764     *
765     * @param pSchedulableMBeanMethod Name of the method to be called optional followed
766     * by method arguments (see above).
767     *
768     * @throws InvalidParameterException If the given value is not of the right
769     * format
770     */

771    public void setSchedulableMBeanMethod(String JavaDoc pSchedulableMBeanMethod)
772       throws InvalidParameterException JavaDoc
773    {
774       if (pSchedulableMBeanMethod == null)
775       {
776          mSchedulableMBeanMethod = null;
777          return;
778       }
779       int lIndex = pSchedulableMBeanMethod.indexOf('(');
780       String JavaDoc lMethodName;
781       if (lIndex == -1)
782       {
783          lMethodName = pSchedulableMBeanMethod.trim();
784          mSchedulableMBeanArguments = new int[0];
785          mSchedulableMBeanArgumentTypes = new String JavaDoc[0];
786       }
787       else
788       {
789          lMethodName = pSchedulableMBeanMethod.substring(0, lIndex).trim();
790       }
791       if (lMethodName.equals(""))
792       {
793          lMethodName = "perform";
794       }
795       if (lIndex >= 0)
796       {
797          int lIndex2 = pSchedulableMBeanMethod.indexOf(')');
798          if (lIndex2 < lIndex)
799          {
800             throw new InvalidParameterException JavaDoc("Schedulable MBean Method: closing bracket must be after opening bracket");
801          }
802          if (lIndex2 < pSchedulableMBeanMethod.length() - 1)
803          {
804             String JavaDoc lRest = pSchedulableMBeanMethod.substring(lIndex2 + 1).trim();
805             if (lRest.length() > 0)
806             {
807                throw new InvalidParameterException JavaDoc("Schedulable MBean Method: nothing should be after closing bracket");
808             }
809          }
810          String JavaDoc lArguments = pSchedulableMBeanMethod.substring(lIndex + 1, lIndex2).trim();
811          if (lArguments.equals(""))
812          {
813             mSchedulableMBeanArguments = new int[0];
814             mSchedulableMBeanArgumentTypes = new String JavaDoc[0];
815          }
816          else
817          {
818             StringTokenizer JavaDoc lTokenizer = new StringTokenizer JavaDoc(lArguments, ", ");
819             mSchedulableMBeanArguments = new int[lTokenizer.countTokens()];
820             mSchedulableMBeanArgumentTypes = new String