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 JavaDoc[lTokenizer.countTokens()];
821             for (int i = 0; lTokenizer.hasMoreTokens(); i++)
822             {
823                String JavaDoc lToken = lTokenizer.nextToken().trim();
824                if (lToken.equals("NOTIFICATION"))
825                {
826                   mSchedulableMBeanArguments[i] = NOTIFICATION;
827                   mSchedulableMBeanArgumentTypes[i] = Notification JavaDoc.class.getName();
828                }
829                else if (lToken.equals("DATE"))
830                {
831                   mSchedulableMBeanArguments[i] = DATE;
832                   mSchedulableMBeanArgumentTypes[i] = Date JavaDoc.class.getName();
833                }
834                else if (lToken.equals("REPETITIONS"))
835                {
836                   mSchedulableMBeanArguments[i] = REPETITIONS;
837                   mSchedulableMBeanArgumentTypes[i] = Long.TYPE.getName();
838                }
839                else if (lToken.equals("SCHEDULER_NAME"))
840                {
841                   mSchedulableMBeanArguments[i] = SCHEDULER_NAME;
842                   mSchedulableMBeanArgumentTypes[i] = ObjectName JavaDoc.class.getName();
843                }
844                else
845                {
846                   mSchedulableMBeanArguments[i] = NULL;
847                   //AS ToDo: maybe later to check if this class exists !
848
mSchedulableMBeanArgumentTypes[i] = lToken;
849                }
850             }
851          }
852       }
853       mSchedulableMBeanMethodName = lMethodName;
854       mSchedulableMBeanMethod = pSchedulableMBeanMethod;
855    }
856
857    /**
858     * @jmx:managed-attribute
859     *
860     * @return True if the Scheduler uses a Schedulable MBean, false if it uses a
861     * Schedulable class
862     */

863    public boolean isUsingMBean()
864    {
865       return mUseMBean;
866    }
867
868    /**
869     * @jmx:managed-attribute
870     *
871     * @return Schedule Period between two scheduled calls in Milliseconds. It will always
872     * be bigger than 0 except it returns -1 then the schedule is stopped.
873     */

874    public long getSchedulePeriod()
875    {
876       return mSchedulePeriod;
877    }
878
879    /**
880     * Sets the Schedule Period between two scheduled call.
881     *
882     * @jmx:managed-attribute
883     *
884     * @param pPeriod Time between to scheduled calls (after the initial call) in Milliseconds.
885     * This value must be bigger than 0.
886     *
887     * @throws InvalidParameterException If the given value is less or equal than 0
888     */

889    public void setSchedulePeriod(long pPeriod)
890    {
891       if (pPeriod <= 0)
892       {
893          throw new InvalidParameterException JavaDoc("Schedulable Period may be not less or equals than 0");
894       }
895       mSchedulePeriod = pPeriod;
896       mIsRestartPending = true;
897    }
898
899    /**
900     * @jmx:managed-attribute
901     *
902     * @return the date format
903     */

904    public String JavaDoc getDateFormat()
905    {
906       if (mDateFormatter == null)
907          mDateFormatter = new SimpleDateFormat JavaDoc();
908       return mDateFormatter.toPattern();
909    }
910
911    /**
912     * Sets the date format used to parse date/times
913     *
914     * @jmx:managed-attribute
915     *
916     * @param dateFormat The date format when empty or null the locale is used to parse dates
917     */

918    public void setDateFormat(String JavaDoc dateFormat)
919    {
920       if (dateFormat == null || dateFormat.trim().length() == 0)
921          mDateFormatter = new SimpleDateFormat JavaDoc();
922       else
923          mDateFormatter = new SimpleDateFormat JavaDoc(dateFormat);
924    }
925
926    /**
927     * @jmx:managed-attribute
928     *
929     * @return Date (and time) of the first scheduled. For value see {@link #setInitialStartDate}
930     * method.
931     */

932    public String JavaDoc getInitialStartDate()
933    {
934       return mStartDateString;
935    }
936
937    /**
938     * Sets the first scheduled call. If the date is in the past the scheduler tries to find the
939     * next available start date.
940     *
941     * @jmx:managed-attribute
942     *
943     * @param pStartDate Date when the initial call is scheduled. It can be either:
944     * <ul>
945     * <li>
946     * NOW: date will be the current date (new Date()) plus 1 seconds
947     * </li><li>
948     * Date as String able to be parsed by SimpleDateFormat with default format
949     * </li><li>
950     * Date as String parsed using the date format attribute
951     * </li><li>
952     * Milliseconds since 1/1/1970
953     * </li>
954     * </ul>
955     * If the date is in the past the Scheduler
956     * will search a start date in the future with respect to the initial repe-
957     * titions and the period between calls. This means that when you restart
958     * the MBean (restarting JBoss etc.) it will start at the next scheduled
959     * time. When no start date is available in the future the Scheduler will
960     * not start.<br>
961     * Example: if you start your Schedulable everyday at Noon and you restart
962     * your JBoss server then it will start at the next Noon (the same if started
963     * before Noon or the next day if start after Noon).
964     */

965    public void setInitialStartDate(String JavaDoc pStartDate)
966    {
967       mStartDateString = pStartDate == null ? "" : pStartDate.trim();
968       if (mStartDateString.equals(""))
969       {
970          mStartDate = new Date JavaDoc(0);
971       }
972       else if (mStartDateString.equals("NOW"))
973       {
974          mStartDate = getNow();
975          mStartDateIsNow = true;
976       }
977       else
978       {
979          try
980          {
981             long lDate = new Long JavaDoc(pStartDate).longValue();
982             mStartDate = new Date JavaDoc(lDate);
983             mStartDateIsNow = false;
984          }
985          catch (NumberFormatException JavaDoc e)
986          {
987             try
988             {
989                if (mDateFormatter == null)
990                {
991                   mDateFormatter = new SimpleDateFormat JavaDoc();
992                }
993                mStartDate = mDateFormatter.parse(mStartDateString);
994                mStartDateIsNow = false;
995             }
996             catch (Exception JavaDoc e2)
997             {
998                log.error("Could not parse given date string: " + mStartDateString, e2);
999                throw new InvalidParameterException JavaDoc("Schedulable Date is not of correct format: " + mStartDateString);
1000            }
1001         }
1002      }
1003      log.debug("Initial Start Date is set to: " + mStartDate);
1004   }
1005
1006   /**
1007    * @jmx:managed-attribute
1008    *
1009    * @return Number of scheduled calls initially. If -1 then there is not limit.
1010    */

1011   public long getInitialRepetitions()
1012   {
1013      return mInitialRepetitions;
1014   }
1015
1016   /**
1017    * Sets the initial number of scheduled calls.
1018    *
1019    * @jmx:managed-attribute
1020    *
1021    * @param pNumberOfCalls Initial Number of scheduled calls. If -1 then the number
1022    * is infinite
1023    *
1024    * @throws InvalidParameterException If the given value is less or equal than 0
1025    */

1026   public void setInitialRepetitions(long pNumberOfCalls)
1027   {
1028      if (pNumberOfCalls <= 0)
1029      {
1030         pNumberOfCalls = -1;
1031      }
1032      mInitialRepetitions = pNumberOfCalls;
1033      mIsRestartPending = true;
1034   }
1035
1036   /**
1037    * @jmx:managed-attribute
1038    *
1039    * @return Number of remaining repetitions. If -1 then there is no limit.
1040    */

1041   public long getRemainingRepetitions()
1042   {
1043      return mRemainingRepetitions;
1044   }
1045
1046   /**
1047    * @jmx:managed-attribute
1048    *
1049    * @return True if the schedule is up and running. If you want to start the schedule
1050    * with another values by using {@ #startSchedule} you have to stop the schedule
1051    * first with {@ #stopSchedule} and wait until this method returns false.
1052    */

1053   public boolean isStarted()
1054   {
1055      return mScheduleIsStarted;
1056   }
1057
1058   /**
1059    * @jmx:managed-attribute
1060    *
1061    * @return True if any attributes are changed but the Schedule is not restarted yet.
1062    */

1063   public boolean isRestartPending()
1064   {
1065      return mIsRestartPending;
1066   }
1067
1068   /**
1069    * @jmx:managed-attribute
1070    *
1071    * @return True if the Schedule when the Scheduler is started
1072    */

1073   public boolean isStartAtStartup()
1074   {
1075      return mStartOnStart;
1076   }
1077
1078   /**
1079    * Set the scheduler to start when MBean started or not. Note that this method only
1080    * affects when the {@link #startService startService()} gets called (normally at
1081    * startup time.
1082    *
1083    * @jmx:managed-attribute
1084    *
1085    * @param pStartAtStartup True if Schedule has to be started at startup time
1086    */

1087   public void setStartAtStartup(boolean pStartAtStartup)
1088   {
1089      mStartOnStart = pStartAtStartup;
1090   }
1091
1092   /**
1093    * @jmx:managed-attribute
1094    *
1095    * @return True if this Scheduler is active and will send notifications in the future
1096    */

1097   public boolean isActive()
1098   {
1099      return isStarted() && mRemainingRepetitions != 0;
1100   }
1101
1102   /**
1103    * @jmx:managed-attribute
1104    *
1105    * @return Name of the Timer MBean used in here
1106    */

1107   public String JavaDoc getTimerName()
1108   {
1109      return mTimerName;
1110   }
1111
1112   /**
1113    * @jmx:managed-attribute
1114    *
1115    * @param pTimerName Object Name of the Timer MBean to
1116    * be used. If null or not a valid ObjectName
1117    * the default will be used
1118    */

1119   public void setTimerName(String JavaDoc pTimerName)
1120   {
1121      mTimerName = pTimerName;
1122   }
1123
1124   /**
1125    * @jmx:managed-attribute
1126    *
1127    * @param fixedRate the default scheduling to use, fixed-rate or fixed-delay (false, default)
1128    */

1129   public void setFixedRate(boolean fixedRate)
1130   {
1131      mFixedRate = fixedRate;
1132   }
1133   
1134   /**
1135    * @jmx:managed-attribute
1136    *
1137    * @return the default scheduling to use
1138    */

1139   public boolean getFixedRate()
1140   {
1141      return mFixedRate;
1142   }
1143   
1144   // -------------------------------------------------------------------------
1145
// Methods
1146
// -------------------------------------------------------------------------
1147

1148   // -------------------------------------------------------------------------
1149
// ServiceMBean - Methods
1150
// -------------------------------------------------------------------------
1151

1152   protected void startService()
1153      throws Exception JavaDoc
1154   {
1155      mTimerObjectName = new ObjectName JavaDoc(mTimerName);
1156      if (!getServer().isRegistered(mTimerObjectName))
1157      {
1158         getServer().createMBean(Timer JavaDoc.class.getName(), mTimerObjectName);
1159      }
1160      mTimer = (TimerMBean JavaDoc)MBeanServerInvocationHandler.newProxyInstance(getServer(),
1161            mTimerObjectName, TimerMBean JavaDoc.class, true);
1162      mTimerEmitter = (NotificationEmitter JavaDoc)mTimer;
1163      if (!mTimer.isActive())
1164      {
1165         mTimer.start();
1166      }
1167      if (mStartOnStart)
1168      {
1169         log.debug("Start Scheduler on start up time");
1170         startSchedule();
1171      }
1172   }
1173
1174   protected void stopService()
1175   {
1176      stopSchedule();
1177   }
1178
1179   private static boolean isSchedulable(Class JavaDoc c)
1180   {
1181      boolean lFound = false;
1182      do
1183      {
1184         Class JavaDoc[] lInterfaces = c.getInterfaces();
1185         for (int i = 0; i < lInterfaces.length; i++)
1186         {
1187            if (lInterfaces[i] == Schedulable.class)
1188            {
1189               lFound = true;
1190               break;
1191            }
1192         }
1193         c = c.getSuperclass();
1194      }
1195      while (c != null && !lFound);
1196      return lFound;
1197   }
1198
1199   /**
1200    * Base class for listeners.
1201    */

1202   public abstract class BaseListener
1203      implements NotificationListener JavaDoc
1204   {
1205      final Logger log = Logger.getLogger(BaseListener.class);
1206
1207      public void handleNotification(
1208         Notification JavaDoc notification,
1209         Object JavaDoc handback
1210         )
1211      {
1212         boolean trace = log.isTraceEnabled();
1213         if (trace)
1214         {
1215            log.trace("handleNotification: " + notification);
1216         }
1217         if (!isStarted())
1218         {
1219            log.trace("Scheduler not started");
1220            stopSchedule();
1221            return;
1222         }
1223         if (mRemainingRepetitions == 0)
1224         {
1225            log.trace("No more repetitions");
1226            stopSchedule();
1227            return;
1228         }
1229         if (mRemainingRepetitions > 0)
1230         {
1231            mRemainingRepetitions--;
1232            if (trace)
1233               log.trace("Remaining repetitions: " + mRemainingRepetitions);
1234         }
1235         invoke(notification);
1236         if (mWaitForNextCallToStop)
1237         {
1238            stopSchedule();
1239         }
1240      }
1241
1242      /**
1243       * Invokes the scheduler method.
1244       */

1245      protected abstract void invoke(Notification JavaDoc notification);
1246
1247   }
1248
1249   // -------------------------------------------------------------------------
1250
// Inner Classes
1251
// -------------------------------------------------------------------------
1252

1253   /**
1254    * Calls {@link Schedulable#perform} on a plain Java Object.
1255    */

1256   public class PojoScheduler extends BaseListener
1257   {
1258
1259      protected void invoke(Notification JavaDoc notification)
1260      {
1261         ClassLoader JavaDoc currentTCL = TCLActions.getContextClassLoader();
1262         try
1263         {
1264            ClassLoader JavaDoc loader = TCLActions.getClassLoader(mSchedulable.getClass());
1265            TCLActions.setContextClassLoader(loader);
1266            Date JavaDoc lTimeStamp = new Date JavaDoc(notification.getTimeStamp());
1267            mSchedulable.perform(lTimeStamp, getRemainingRepetitions());
1268         }
1269         catch (Exception JavaDoc e)
1270         {
1271            log.error("Scheduler.perform call failed", e);
1272         }
1273         finally
1274         {
1275            TCLActions.setContextClassLoader(currentTCL);
1276         }
1277      }
1278   }
1279
1280   /**
1281    * Invokes an operation on an MBean.
1282    */

1283   public class MBeanListener extends BaseListener
1284   {
1285      protected void invoke(Notification JavaDoc notification)
1286      {
1287         Object JavaDoc[] lArguments = new Object JavaDoc[mSchedulableMBeanArguments.length];
1288         for (int i = 0; i < lArguments.length; i++)
1289         {
1290            switch (mSchedulableMBeanArguments[i])
1291            {
1292               case NOTIFICATION:
1293                  lArguments[i] = notification;
1294                  break;
1295               case DATE:
1296                  lArguments[i] = new Date JavaDoc(notification.getTimeStamp());
1297                  break;
1298               case REPETITIONS:
1299                  lArguments[i] = new Long JavaDoc(mRemainingRepetitions);
1300                  break;
1301               case SCHEDULER_NAME:
1302                  lArguments[i] = getServiceName();
1303                  break;
1304               default:
1305                  lArguments[i] = null;
1306            }
1307         }
1308         if (log.isTraceEnabled())
1309         {
1310            log.debug("invoke " + mSchedulableMBean + " " + mSchedulableMBeanMethodName);
1311            log.debug("arguments: " + Arrays.asList(lArguments));
1312            log.debug("argument types: " + Arrays.asList(mSchedulableMBeanArgumentTypes));
1313         }
1314         try
1315         {
1316            getServer().invoke(
1317               mSchedulableMBean,
1318               mSchedulableMBeanMethodName,
1319               lArguments,
1320               mSchedulableMBeanArgumentTypes
1321            );
1322         }
1323         catch (Exception JavaDoc e)
1324         {
1325            log.error("Invoke failed for " + mSchedulableMBean + " " + mSchedulableMBeanMethodName, e);
1326         }
1327      }
1328   }
1329
1330}
1331
Popular Tags