/projects/jboss-5.1.0/varia/src/main/org/jboss/varia/scheduler/Scheduler.java
Java | 1330 lines | 852 code | 75 blank | 403 comment | 130 complexity | d15fcdefc32cb25abef87b9b93b858d6 MD5 | raw file
- /*
- * JBoss, Home of Professional Open Source.
- * Copyright 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
- package org.jboss.varia.scheduler;
- import java.lang.reflect.Constructor;
- import java.security.InvalidParameterException;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.StringTokenizer;
- import java.util.Vector;
- import java.util.Arrays;
- import javax.management.InstanceNotFoundException;
- import javax.management.MalformedObjectNameException;
- import javax.management.Notification;
- import javax.management.NotificationEmitter;
- import javax.management.NotificationListener;
- import javax.management.ObjectName;
- import javax.management.MBeanServerInvocationHandler;
- import javax.management.timer.Timer;
- import javax.management.timer.TimerMBean;
- import javax.management.timer.TimerNotification;
- import org.jboss.logging.Logger;
- import org.jboss.system.ServiceMBeanSupport;
- import org.jboss.util.Classes;
- /**
- * Schedules a timer task that calls an MBean or Object instance.
- * Any MBean operation can be called. Object instances must implement the
- * {@link Schedulable} interface.
- * <p />
- * Create a separate Scheduler MBean for every MBean or Object you wish to call.
- * One example naming strategy for calling an MBean named:
- <code>example:type=HelloWorld</code>
- * is to create a similarly named:
- <code>example:type=Scheduler,call=HelloWorld</code> MBean.
- * This way you should not run into a name conflict.
- * <p>
- * This MBean registers a notification listener with an
- * javax.management.timer.Timer MBean. If the Timer does not exist, this MBean
- * will create it. Each Timer can handle multiple Scheduler instances.
- * </p>
- *
- * @author <a href="mailto:andreas@jboss.org">Andreas Schaefer</a>
- * @author Cameron (camtabor)
- * @version $Revision: 81038 $
- */
- public class Scheduler extends ServiceMBeanSupport
- implements SchedulerMBean
- {
- // -------------------------------------------------------------------------
- // Constants
- // -------------------------------------------------------------------------
- public static String JNDI_NAME = "scheduler:domain";
- public static String JMX_NAME = "scheduler";
- public static String DEFAULT_TIMER_NAME = ScheduleManager.DEFAULT_TIMER_NAME;
- private static final int NOTIFICATION = 0;
- private static final int DATE = 1;
- private static final int REPETITIONS = 2;
- private static final int SCHEDULER_NAME = 3;
- private static final int NULL = 4;
- // -------------------------------------------------------------------------
- // Members
- // -------------------------------------------------------------------------
- private long mActualSchedulePeriod;
- private long mRemainingRepetitions = 0;
- private int mNotificationID = -1;
- private String mTimerName = DEFAULT_TIMER_NAME;
- private ObjectName mTimerObjectName;
- private TimerMBean mTimer;
- private NotificationEmitter mTimerEmitter;
- private Schedulable mSchedulable;
- private boolean mScheduleIsStarted = false;
- private boolean mWaitForNextCallToStop = false;
- private boolean mStartOnStart = false;
- private boolean mIsRestartPending = true;
- // Pending values which can be different to the actual ones
- private boolean mUseMBean = false;
- private Class mSchedulableClass;
- private String mSchedulableArguments;
- private String[] mSchedulableArgumentList = new String[0];
- private String mSchedulableArgumentTypes;
- private Class[] mSchedulableArgumentTypeList = new Class[0];
- private ObjectName mSchedulableMBean;
- private String mSchedulableMBeanMethod;
- private String mSchedulableMBeanMethodName;
- private int[] mSchedulableMBeanArguments = new int[0];
- private String[] mSchedulableMBeanArgumentTypes = new String[0];
- private SimpleDateFormat mDateFormatter;
- private Date mStartDate;
- private String mStartDateString;
- private boolean mStartDateIsNow;
- private long mSchedulePeriod;
- private long mInitialRepetitions;
- private boolean mFixedRate = false;
-
- private NotificationListener mListener;
- // -------------------------------------------------------------------------
- // Constructors
- // -------------------------------------------------------------------------
- /**
- * Constructs a new Scheduler instance.
- **/
- public Scheduler()
- {
- }
- /**
- * Constructs a new Scheduler instance.
- * @param pSchedulableClass
- * @param pSchedulePeriod
- */
- public Scheduler(String pSchedulableClass,
- long pSchedulePeriod)
- {
- setStartAtStartup(true);
- setSchedulableClass(pSchedulableClass);
- setSchedulePeriod(pSchedulePeriod);
- }
- /**
- * Constructs a new Scheduler instance.
- * @param pSchedulableClass
- * @param pInitArguments
- * @param pInitTypes
- * @param pInitialStartDate
- * @param pSchedulePeriod
- * @param pNumberOfRepetitions
- */
- public Scheduler(String pSchedulableClass,
- String pInitArguments,
- String pInitTypes,
- String pInitialStartDate,
- long pSchedulePeriod,
- long pNumberOfRepetitions
- )
- {
- setStartAtStartup(true);
- setSchedulableClass(pSchedulableClass);
- setSchedulableArguments(pInitArguments);
- setSchedulableArgumentTypes(pInitTypes);
- setInitialStartDate(pInitialStartDate);
- setSchedulePeriod(pSchedulePeriod);
- setInitialRepetitions(pNumberOfRepetitions);
- }
- /**
- * Constructs a new Scheduler instance.
- * @param pSchedulableClass
- * @param pInitArguments
- * @param pInitTypes
- * @param pDateFormat
- * @param pInitialStartDate
- * @param pSchedulePeriod
- * @param pNumberOfRepetitions
- */
- public Scheduler(
- String pSchedulableClass,
- String pInitArguments,
- String pInitTypes,
- String pDateFormat,
- String pInitialStartDate,
- long pSchedulePeriod,
- long pNumberOfRepetitions
- )
- {
- setStartAtStartup(true);
- setSchedulableClass(pSchedulableClass);
- setSchedulableArguments(pInitArguments);
- setSchedulableArgumentTypes(pInitTypes);
- setDateFormat(pDateFormat);
- setInitialStartDate(pInitialStartDate);
- setSchedulePeriod(pSchedulePeriod);
- setInitialRepetitions(pNumberOfRepetitions);
- }
- // -------------------------------------------------------------------------
- // SchedulerMBean Methods
- // -------------------------------------------------------------------------
- //
- private void checkMBean() {
- if (mSchedulableMBean == null)
- {
- log.debug("Schedulable MBean Object Name is not set");
- throw new InvalidParameterException(
- "Schedulable MBean must be set"
- );
- }
- if (mSchedulableMBeanMethodName == null)
- {
- mSchedulableMBeanMethodName = "perform";
- mSchedulableMBeanArguments = new int[]{DATE, REPETITIONS};
- mSchedulableMBeanArgumentTypes = new String[]{
- Date.class.getName(),
- Integer.TYPE.getName()
- };
- }
- }
- private void createSchedulable() {
- if (mSchedulableClass == null)
- {
- throw new InvalidParameterException("Schedulable Class not set");
- }
- if (mSchedulableArgumentList.length != mSchedulableArgumentTypeList.length)
- {
- throw new InvalidParameterException(
- "Schedulable Class Arguments and Types do not match in length"
- );
- }
- // Create all the Objects for the Constructor to be called
- Object[] lArgumentList = new Object[mSchedulableArgumentTypeList.length];
- try
- {
- for (int i = 0; i < mSchedulableArgumentTypeList.length; i++)
- {
- Class lClass = mSchedulableArgumentTypeList[i];
- if (lClass == Boolean.TYPE)
- {
- lArgumentList[i] = new Boolean(mSchedulableArgumentList[i]);
- }
- else if (lClass == Integer.TYPE)
- {
- lArgumentList[i] = new Integer(mSchedulableArgumentList[i]);
- }
- else if (lClass == Long.TYPE)
- {
- lArgumentList[i] = new Long(mSchedulableArgumentList[i]);
- }
- else if (lClass == Short.TYPE)
- {
- lArgumentList[i] = new Short(mSchedulableArgumentList[i]);
- }
- else if (lClass == Float.TYPE)
- {
- lArgumentList[i] = new Float(mSchedulableArgumentList[i]);
- }
- else if (lClass == Double.TYPE)
- {
- lArgumentList[i] = new Double(mSchedulableArgumentList[i]);
- }
- else if (lClass == Byte.TYPE)
- {
- lArgumentList[i] = new Byte(mSchedulableArgumentList[i]);
- }
- else if (lClass == Character.TYPE)
- {
- lArgumentList[i] = new Character(mSchedulableArgumentList[i].charAt(0));
- }
- else
- {
- Constructor lConstructor = lClass.getConstructor(new Class[]{String.class});
- lArgumentList[i] = lConstructor.newInstance(new Object[]{mSchedulableArgumentList[i]});
- }
- }
- }
- catch (Exception e)
- {
- log.error("Could not load or create constructor argument", e);
- throw new InvalidParameterException("Could not load or create a constructor argument");
- }
- try
- {
- // Check if constructor is found
- Constructor lSchedulableConstructor = mSchedulableClass.getConstructor(mSchedulableArgumentTypeList);
- // Create an instance of it
- mSchedulable = (Schedulable) lSchedulableConstructor.newInstance(lArgumentList);
- }
- catch (Exception e)
- {
- log.error("Could not find the constructor or create Schedulable instance", e);
- throw new InvalidParameterException("Could not find the constructor or create the Schedulable Instance");
- }
- }
- private Date getNow() {
- long now = System.currentTimeMillis();
- return new Date(now + 1000);
- }
- private void initStartDate() {
- // Register the Schedule at the Timer
- // If start date is NOW then take the current date
- if (mStartDateIsNow)
- {
- mStartDate = getNow();
- }
- else
- {
- // Check if initial start date is in the past
- if (mStartDate.before(new Date()))
- {
- // If then first check if a repetition is in the future
- long lNow = new Date().getTime() + 100;
- long lSkipRepeats = ((lNow - mStartDate.getTime()) / mActualSchedulePeriod) + 1;
- log.debug("Old start date: " + mStartDate + ", now: " + new Date(lNow) + ", Skip repeats: " + lSkipRepeats);
- if (mRemainingRepetitions > 0)
- {
- // If not infinit loop
- if (lSkipRepeats >= mRemainingRepetitions)
- {
- // No repetition left -> exit
- log.info("No repetitions left because start date is in the past and could " +
- "not be reached by Initial Repetitions * Schedule Period");
- return;
- }
- else
- {
- // Reduce the missed hits
- mRemainingRepetitions -= lSkipRepeats;
- }
- }
- mStartDate = new Date(mStartDate.getTime() + (lSkipRepeats * mActualSchedulePeriod));
- }
- }
- }
- /**
- * Starts the schedule if the schedule is stopped otherwise nothing will happen.
- * The Schedule is immediately set to started even the first call is in the
- * future.
- *
- * @jmx:managed-operation
- *
- * @throws InvalidParameterException If any of the necessary values are not set
- * or invalid (especially for the Schedulable
- * class attributes).
- */
- public void startSchedule()
- {
- if (isStarted())
- {
- log.debug("already started");
- return;
- }
- if (mUseMBean)
- {
- checkMBean();
- }
- else
- {
- createSchedulable();
- }
- mRemainingRepetitions = mInitialRepetitions;
- mActualSchedulePeriod = mSchedulePeriod;
- initStartDate();
- log.debug("Schedule initial call to: " + mStartDate + ", remaining repetitions: " + mRemainingRepetitions);
- mNotificationID = mTimer.addNotification(
- "Schedule", "Scheduler Notification",
- null, // new Integer(getID()), // User Object
- mStartDate,
- new Long(mActualSchedulePeriod),
- mRemainingRepetitions < 0 ? new Long(0) : new Long(mRemainingRepetitions),
- Boolean.valueOf(mFixedRate)
- );
- mListener = mUseMBean ? new MBeanListener() : new PojoScheduler();
- mTimerEmitter.addNotificationListener(
- mListener,
- new ScheduleManager.IdNotificationFilter(mNotificationID),
- null
- );
- mScheduleIsStarted = true;
- mIsRestartPending = false;
- }
- /**
- * Stops the schedule immediately.
- * @jmx:managed-operation
- */
- public void stopSchedule()
- {
- stopSchedule(true);
- }
- /**
- * Stops the schedule because it is either not used anymore or to restart it with
- * new values.
- *
- * @jmx:managed-operation
- *
- * @param pDoItNow If true the schedule will be stopped without waiting for the next
- * scheduled call otherwise the next call will be performed before
- * the schedule is stopped.
- */
- public void stopSchedule(boolean pDoItNow)
- {
- log.debug("stopSchedule(" + pDoItNow + ")");
- try
- {
- if (mNotificationID < 0)
- {
- mScheduleIsStarted = false;
- mWaitForNextCallToStop = false;
- return;
- }
- if (pDoItNow)
- {
- log.debug("stopSchedule(), removing schedule id: " + mNotificationID);
- mWaitForNextCallToStop = false;
- if (mListener != null)
- {
- mTimerEmitter.removeNotificationListener(mListener);
- try
- {
- mTimer.removeNotification(mNotificationID);
- }
- catch (InstanceNotFoundException e)
- {
- log.trace(e);
- }
- mListener = null;
- }
- mNotificationID = -1;
- mScheduleIsStarted = false;
- }
- else
- {
- mWaitForNextCallToStop = true;
- }
- }
- catch (Exception e)
- {
- log.error("stopSchedule failed", e);
- }
- }
- /**
- * Stops the server right now and starts it right now.
- *
- * @jmx:managed-operation
- */
- public void restartSchedule()
- {
- stopSchedule();
- startSchedule();
- }
- /**
- * @jmx:managed-attribute
- *
- * @return Full qualified Class name of the schedulable class called by the schedule or
- * null if not set.
- */
- public String getSchedulableClass()
- {
- if (mSchedulableClass == null)
- {
- return null;
- }
- return mSchedulableClass.getName();
- }
- /**
- * Sets the fully qualified Class name of the Schedulable Class being called by the
- * Scheduler. Must be set before the Schedule is started. Please also set the
- * {@link #setSchedulableArguments} and {@link #setSchedulableArgumentTypes}.
- *
- * @jmx:managed-attribute
- *
- * @param pSchedulableClass Fully Qualified Schedulable Class.
- *
- * @throws InvalidParameterException If the given value is not a valid class or cannot
- * be loaded by the Scheduler or is not of instance
- * Schedulable.
- */
- public void setSchedulableClass(String pSchedulableClass)
- throws InvalidParameterException
- {
- if (pSchedulableClass == null || pSchedulableClass.equals(""))
- {
- throw new InvalidParameterException("Schedulable Class cannot be empty or undefined");
- }
- try
- {
- // Try to load the Schedulable Class
- ClassLoader loader = TCLActions.getContextClassLoader();
- mSchedulableClass = loader.loadClass(pSchedulableClass);
- // Check if instance of Schedulable
- if (!isSchedulable(mSchedulableClass))
- {
- String msg = "Given class " + pSchedulableClass + " is not instance of Schedulable";
- StringBuffer info = new StringBuffer(msg);
- info.append("\nThe SchedulableClass info:");
- Classes.displayClassInfo(mSchedulableClass, info);
- info.append("\nSchedulable.class info:");
- Classes.displayClassInfo(Schedulable.class, info);
- log.debug(info.toString());
- throw new InvalidParameterException(msg);
- }
- }
- catch (ClassNotFoundException e)
- {
- log.info("Failed to find: "+pSchedulableClass, e);
- throw new InvalidParameterException(
- "Given class " + pSchedulableClass + " is not not found"
- );
- }
- mIsRestartPending = true;
- mUseMBean = false;
- }
- /**
- * @jmx:managed-attribute
- *
- * @return Comma seperated list of Constructor Arguments used to instantiate the
- * Schedulable class instance. Right now only basic data types, String and
- * Classes with a Constructor with a String as only argument are supported.
- */
- public String getSchedulableArguments()
- {
- return mSchedulableArguments;
- }
- /**
- * @jmx:managed-attribute
- *
- * Sets the comma seperated list of arguments for the Schedulable class. Note that
- * this list must have as many elements as the Schedulable Argument Type list otherwise
- * the start of the Scheduler will fail. Right now only basic data types, String and
- * Classes with a Constructor with a String as only argument are supported.
- *
- * @param pArgumentList List of arguments used to create the Schedulable intance. If
- * the list is null or empty then the no-args constructor is used.
- */
- public void setSchedulableArguments(String pArgumentList)
- {
- if (pArgumentList == null || pArgumentList.equals(""))
- {
- mSchedulableArgumentList = new String[0];
- }
- else
- {
- StringTokenizer lTokenizer = new StringTokenizer(pArgumentList, ",");
- Vector lList = new Vector();
- while (lTokenizer.hasMoreTokens())
- {
- String lToken = lTokenizer.nextToken().trim();
- if (lToken.equals(""))
- {
- lList.add("null");
- }
- else
- {
- lList.add(lToken);
- }
- }
- mSchedulableArgumentList = (String[]) lList.toArray(new String[0]);
- }
- mSchedulableArguments = pArgumentList;
- mIsRestartPending = true;
- }
- /**
- * @jmx:managed-attribute
- *
- * @return A comma seperated list of Argument Types which should match the list of
- * arguments.
- */
- public String getSchedulableArgumentTypes()
- {
- return mSchedulableArgumentTypes;
- }
- /**
- * Sets the comma seperated list of argument types for the Schedulable class. This will
- * be used to find the right constructor and to created the right instances to call the
- * constructor with. This list must have as many elements as the Schedulable Arguments
- * list otherwise the start of the Scheduler will fail. Right now only basic data types,
- * String and Classes with a Constructor with a String as only argument are supported.
- *
- * @jmx:managed-attribute
- *
- * @param pTypeList List of arguments used to create the Schedulable intance. If
- * the list is null or empty then the no-args constructor is used.
- *
- * @throws InvalidParameterException If the given list contains a unknow datat type.
- */
- public void setSchedulableArgumentTypes(String pTypeList)
- throws InvalidParameterException
- {
- if (pTypeList == null || pTypeList.equals(""))
- {
- mSchedulableArgumentTypeList = new Class[0];
- }
- else
- {
- StringTokenizer lTokenizer = new StringTokenizer(pTypeList, ",");
- Vector lList = new Vector();
- while (lTokenizer.hasMoreTokens())
- {
- String lToken = lTokenizer.nextToken().trim();
- // Get the class
- Class lClass = null;
- if (lToken.equals("short"))
- {
- lClass = Short.TYPE;
- }
- else if (lToken.equals("int"))
- {
- lClass = Integer.TYPE;
- }
- else if (lToken.equals("long"))
- {
- lClass = Long.TYPE;
- }
- else if (lToken.equals("byte"))
- {
- lClass = Byte.TYPE;
- }
- else if (lToken.equals("char"))
- {
- lClass = Character.TYPE;
- }
- else if (lToken.equals("float"))
- {
- lClass = Float.TYPE;
- }
- else if (lToken.equals("double"))
- {
- lClass = Double.TYPE;
- }
- else if (lToken.equals("boolean"))
- {
- lClass = Boolean.TYPE;
- }
- if (lClass == null)
- {
- try
- {
- // Load class to check if available
- ClassLoader loader = TCLActions.getContextClassLoader();
- lClass = loader.loadClass(lToken);
- }
- catch (ClassNotFoundException cnfe)
- {
- throw new InvalidParameterException(
- "The argument type: " + lToken + " is not a valid class or could not be found"
- );
- }
- }
- lList.add(lClass);
- }
- mSchedulableArgumentTypeList = (Class[]) lList.toArray(new Class[0]);
- }
- mSchedulableArgumentTypes = pTypeList;
- mIsRestartPending = true;
- }
- /**
- * @jmx:managed-attribute
- *
- * @return Object Name if a Schedulable MBean is set
- */
- public String getSchedulableMBean()
- {
- return mSchedulableMBean == null ?
- null :
- mSchedulableMBean.toString();
- }
- /**
- * Sets the fully qualified JMX MBean name of the Schedulable MBean to be called.
- * <b>Attention: </b>if set the all values set by {@link #setSchedulableClass},
- * {@link #setSchedulableArguments} and {@link #setSchedulableArgumentTypes} are
- * cleared and not used anymore. Therefore only use either Schedulable Class or
- * Schedulable MBean. If {@link #setSchedulableMBeanMethod} is not set then the
- * schedule method as in the {@link Schedulable#perform} will be called with the
- * same arguments. Also note that the Object Name will not be checked if the
- * MBean is available. If the MBean is not available it will not be called but
- * the remaining repetitions will be decreased.
- *
- * @jmx:managed-attribute
- *
- * @param pSchedulableMBean JMX MBean Object Name which should be called.
- *
- * @throws InvalidParameterException If the given value is an valid Object Name.
- */
- public void setSchedulableMBean(String pSchedulableMBean)
- throws InvalidParameterException
- {
- if (pSchedulableMBean == null)
- {
- throw new InvalidParameterException("Schedulable MBean must be specified");
- }
- try
- {
- mSchedulableMBean = new ObjectName(pSchedulableMBean);
- mUseMBean = true;
- }
- catch (MalformedObjectNameException e)
- {
- throw new InvalidParameterException("Schedulable MBean name invalid " + pSchedulableMBean);
- }
- }
- /**
- * @return Schedulable MBean Method description if set
- **/
- public String getSchedulableMBeanMethod()
- {
- return mSchedulableMBeanMethod;
- }
- /**
- * Sets the method name to be called on the Schedulable MBean. It can optionally be
- * followed by an opening bracket, list of attributes (see below) and a closing bracket.
- * The list of attributes can contain:
- * <ul>
- * <li>NOTIFICATION which will be replaced by the timers notification instance
- * (javax.management.Notification)</li>
- * <li>DATE which will be replaced by the date of the notification call
- * (java.util.Date)</li>
- * <li>REPETITIONS which will be replaced by the number of remaining repetitions
- * (long)</li>
- * <li>SCHEDULER_NAME which will be replaced by the Object Name of the Scheduler
- * (javax.management.ObjectName)</li>
- * <li>any full qualified Class name which the Scheduler will be set a "null" value
- * for it</li>
- * </ul>
- * <br>
- * An example could be: "doSomething( NOTIFICATION, REPETITIONS, java.lang.String )"
- * where the Scheduler will pass the timer's notification instance, the remaining
- * repetitions as int and a null to the MBean's doSomething() method which must
- * have the following signature: doSomething( javax.management.Notification, long,
- * java.lang.String ).
- *
- * @jmx:managed-attribute
- *
- * @param pSchedulableMBeanMethod Name of the method to be called optional followed
- * by method arguments (see above).
- *
- * @throws InvalidParameterException If the given value is not of the right
- * format
- */
- public void setSchedulableMBeanMethod(String pSchedulableMBeanMethod)
- throws InvalidParameterException
- {
- if (pSchedulableMBeanMethod == null)
- {
- mSchedulableMBeanMethod = null;
- return;
- }
- int lIndex = pSchedulableMBeanMethod.indexOf('(');
- String lMethodName;
- if (lIndex == -1)
- {
- lMethodName = pSchedulableMBeanMethod.trim();
- mSchedulableMBeanArguments = new int[0];
- mSchedulableMBeanArgumentTypes = new String[0];
- }
- else
- {
- lMethodName = pSchedulableMBeanMethod.substring(0, lIndex).trim();
- }
- if (lMethodName.equals(""))
- {
- lMethodName = "perform";
- }
- if (lIndex >= 0)
- {
- int lIndex2 = pSchedulableMBeanMethod.indexOf(')');
- if (lIndex2 < lIndex)
- {
- throw new InvalidParameterException("Schedulable MBean Method: closing bracket must be after opening bracket");
- }
- if (lIndex2 < pSchedulableMBeanMethod.length() - 1)
- {
- String lRest = pSchedulableMBeanMethod.substring(lIndex2 + 1).trim();
- if (lRest.length() > 0)
- {
- throw new InvalidParameterException("Schedulable MBean Method: nothing should be after closing bracket");
- }
- }
- String lArguments = pSchedulableMBeanMethod.substring(lIndex + 1, lIndex2).trim();
- if (lArguments.equals(""))
- {
- mSchedulableMBeanArguments = new int[0];
- mSchedulableMBeanArgumentTypes = new String[0];
- }
- else
- {
- StringTokenizer lTokenizer = new StringTokenizer(lArguments, ", ");
- mSchedulableMBeanArguments = new int[lTokenizer.countTokens()];
- mSchedulableMBeanArgumentTypes = new String[lTokenizer.countTokens()];
- for (int i = 0; lTokenizer.hasMoreTokens(); i++)
- {
- String lToken = lTokenizer.nextToken().trim();
- if (lToken.equals("NOTIFICATION"))
- {
- mSchedulableMBeanArguments[i] = NOTIFICATION;
- mSchedulableMBeanArgumentTypes[i] = Notification.class.getName();
- }
- else if (lToken.equals("DATE"))
- {
- mSchedulableMBeanArguments[i] = DATE;
- mSchedulableMBeanArgumentTypes[i] = Date.class.getName();
- }
- else if (lToken.equals("REPETITIONS"))
- {
- mSchedulableMBeanArguments[i] = REPETITIONS;
- mSchedulableMBeanArgumentTypes[i] = Long.TYPE.getName();
- }
- else if (lToken.equals("SCHEDULER_NAME"))
- {
- mSchedulableMBeanArguments[i] = SCHEDULER_NAME;
- mSchedulableMBeanArgumentTypes[i] = ObjectName.class.getName();
- }
- else
- {
- mSchedulableMBeanArguments[i] = NULL;
- //AS ToDo: maybe later to check if this class exists !
- mSchedulableMBeanArgumentTypes[i] = lToken;
- }
- }
- }
- }
- mSchedulableMBeanMethodName = lMethodName;
- mSchedulableMBeanMethod = pSchedulableMBeanMethod;
- }
- /**
- * @jmx:managed-attribute
- *
- * @return True if the Scheduler uses a Schedulable MBean, false if it uses a
- * Schedulable class
- */
- public boolean isUsingMBean()
- {
- return mUseMBean;
- }
- /**
- * @jmx:managed-attribute
- *
- * @return Schedule Period between two scheduled calls in Milliseconds. It will always
- * be bigger than 0 except it returns -1 then the schedule is stopped.
- */
- public long getSchedulePeriod()
- {
- return mSchedulePeriod;
- }
- /**
- * Sets the Schedule Period between two scheduled call.
- *
- * @jmx:managed-attribute
- *
- * @param pPeriod Time between to scheduled calls (after the initial call) in Milliseconds.
- * This value must be bigger than 0.
- *
- * @throws InvalidParameterException If the given value is less or equal than 0
- */
- public void setSchedulePeriod(long pPeriod)
- {
- if (pPeriod <= 0)
- {
- throw new InvalidParameterException("Schedulable Period may be not less or equals than 0");
- }
- mSchedulePeriod = pPeriod;
- mIsRestartPending = true;
- }
- /**
- * @jmx:managed-attribute
- *
- * @return the date format
- */
- public String getDateFormat()
- {
- if (mDateFormatter == null)
- mDateFormatter = new SimpleDateFormat();
- return mDateFormatter.toPattern();
- }
- /**
- * Sets the date format used to parse date/times
- *
- * @jmx:managed-attribute
- *
- * @param dateFormat The date format when empty or null the locale is used to parse dates
- */
- public void setDateFormat(String dateFormat)
- {
- if (dateFormat == null || dateFormat.trim().length() == 0)
- mDateFormatter = new SimpleDateFormat();
- else
- mDateFormatter = new SimpleDateFormat(dateFormat);
- }
- /**
- * @jmx:managed-attribute
- *
- * @return Date (and time) of the first scheduled. For value see {@link #setInitialStartDate}
- * method.
- */
- public String getInitialStartDate()
- {
- return mStartDateString;
- }
- /**
- * Sets the first scheduled call. If the date is in the past the scheduler tries to find the
- * next available start date.
- *
- * @jmx:managed-attribute
- *
- * @param pStartDate Date when the initial call is scheduled. It can be either:
- * <ul>
- * <li>
- * NOW: date will be the current date (new Date()) plus 1 seconds
- * </li><li>
- * Date as String able to be parsed by SimpleDateFormat with default format
- * </li><li>
- * Date as String parsed using the date format attribute
- * </li><li>
- * Milliseconds since 1/1/1970
- * </li>
- * </ul>
- * If the date is in the past the Scheduler
- * will search a start date in the future with respect to the initial repe-
- * titions and the period between calls. This means that when you restart
- * the MBean (restarting JBoss etc.) it will start at the next scheduled
- * time. When no start date is available in the future the Scheduler will
- * not start.<br>
- * Example: if you start your Schedulable everyday at Noon and you restart
- * your JBoss server then it will start at the next Noon (the same if started
- * before Noon or the next day if start after Noon).
- */
- public void setInitialStartDate(String pStartDate)
- {
- mStartDateString = pStartDate == null ? "" : pStartDate.trim();
- if (mStartDateString.equals(""))
- {
- mStartDate = new Date(0);
- }
- else if (mStartDateString.equals("NOW"))
- {
- mStartDate = getNow();
- mStartDateIsNow = true;
- }
- else
- {
- try
- {
- long lDate = new Long(pStartDate).longValue();
- mStartDate = new Date(lDate);
- mStartDateIsNow = false;
- }
- catch (NumberFormatException e)
- {
- try
- {
- if (mDateFormatter == null)
- {
- mDateFormatter = new SimpleDateFormat();
- }
- mStartDate = mDateFormatter.parse(mStartDateString);
- mStartDateIsNow = false;
- }
- catch (Exception e2)
- {
- log.error("Could not parse given date string: " + mStartDateString, e2);
- throw new InvalidParameterException("Schedulable Date is not of correct format: " + mStartDateString);
- }
- }
- }
- log.debug("Initial Start Date is set to: " + mStartDate);
- }
- /**
- * @jmx:managed-attribute
- *
- * @return Number of scheduled calls initially. If -1 then there is not limit.
- */
- public long getInitialRepetitions()
- {
- return mInitialRepetitions;
- }
- /**
- * Sets the initial number of scheduled calls.
- *
- * @jmx:managed-attribute
- *
- * @param pNumberOfCalls Initial Number of scheduled calls. If -1 then the number
- * is infinite
- *
- * @throws InvalidParameterException If the given value is less or equal than 0
- */
- public void setInitialRepetitions(long pNumberOfCalls)
- {
- if (pNumberOfCalls <= 0)
- {
- pNumberOfCalls = -1;
- }
- mInitialRepetitions = pNumberOfCalls;
- mIsRestartPending = true;
- }
- /**
- * @jmx:managed-attribute
- *
- * @return Number of remaining repetitions. If -1 then there is no limit.
- */
- public long getRemainingRepetitions()
- {
- return mRemainingRepetitions;
- }
- /**
- * @jmx:managed-attribute
- *
- * @return True if the schedule is up and running. If you want to start the schedule
- * with another values by using {@ #startSchedule} you have to stop the schedule
- * first with {@ #stopSchedule} and wait until this method returns false.
- */
- public boolean isStarted()
- {
- return mScheduleIsStarted;
- }
- /**
- * @jmx:managed-attribute
- *
- * @return True if any attributes are changed but the Schedule is not restarted yet.
- */
- public boolean isRestartPending()
- {
- return mIsRestartPending;
- }
- /**
- * @jmx:managed-attribute
- *
- * @return True if the Schedule when the Scheduler is started
- */
- public boolean isStartAtStartup()
- {
- return mStartOnStart;
- }
- /**
- * Set the scheduler to start when MBean started or not. Note that this method only
- * affects when the {@link #startService startService()} gets called (normally at
- * startup time.
- *
- * @jmx:managed-attribute
- *
- * @param pStartAtStartup True if Schedule has to be started at startup time
- */
- public void setStartAtStartup(boolean pStartAtStartup)
- {
- mStartOnStart = pStartAtStartup;
- }
- /**
- * @jmx:managed-attribute
- *
- * @return True if this Scheduler is active and will send notifications in the future
- */
- public boolean isActive()
- {
- return isStarted() && mRemainingRepetitions != 0;
- }
- /**
- * @jmx:managed-attribute
- *
- * @return Name of the Timer MBean used in here
- */
- public String getTimerName()
- {
- return mTimerName;
- }
- /**
- * @jmx:managed-attribute
- *
- * @param pTimerName Object Name of the Timer MBean to
- * be used. If null or not a valid ObjectName
- * the default will be used
- */
- public void setTimerName(String pTimerName)
- {
- mTimerName = pTimerName;
- }
- /**
- * @jmx:managed-attribute
- *
- * @param fixedRate the default scheduling to use, fixed-rate or fixed-delay (false, default)
- */
- public void setFixedRate(boolean fixedRate)
- {
- mFixedRate = fixedRate;
- }
-
- /**
- * @jmx:managed-attribute
- *
- * @return the default scheduling to use
- */
- public boolean getFixedRate()
- {
- return mFixedRate;
- }
-
- // -------------------------------------------------------------------------
- // Methods
- // -------------------------------------------------------------------------
- // -------------------------------------------------------------------------
- // ServiceMBean - Methods
- // -------------------------------------------------------------------------
- protected void startService()
- throws Exception
- {
- mTimerObjectName = new ObjectName(mTimerName);
- if (!getServer().isRegistered(mTimerObjectName))
- {
- getServer().createMBean(Timer.class.getName(), mTimerObjectName);
- }
- mTimer = (TimerMBean)MBeanServerInvocationHandler.newProxyInstance(getServer(),
- mTimerObjectName, TimerMBean.class, true);
- mTimerEmitter = (NotificationEmitter)mTimer;
- if (!mTimer.isActive())
- {
- mTimer.start();
- }
- if (mStartOnStart)
- {
- log.debug("Start Scheduler on start up time");
- startSchedule();
- }
- }
- protected void stopService()
- {
- stopSchedule();
- }
- private static boolean isSchedulable(Class c)
- {
- boolean lFound = false;
- do
- {
- Class[] lInterfaces = c.getInterfaces();
- for (int i = 0; i < lInterfaces.length; i++)
- {
- if (lInterfaces[i] == Schedulable.class)
- {
- lFound = true;
- break;
- }
- }
- c = c.getSuperclass();
- }
- while (c != null && !lFound);
- return lFound;
- }
- /**
- * Base class for listeners.
- */
- public abstract class BaseListener
- implements NotificationListener
- {
- final Logger log = Logger.getLogger(BaseListener.class);
- public void handleNotification(
- Notification notification,
- Object handback
- )
- {
- boolean trace = log.isTraceEnabled();
- if (trace)
- {
- log.trace("handleNotification: " + notification);
- }
- if (!isStarted())
- {
- log.trace("Scheduler not started");
- stopSchedule();
- return;
- }
- if (mRemainingRepetitions == 0)
- {
- log.trace("No more repetitions");
- stopSchedule();
- return;
- }
- if (mRemainingRepetitions > 0)
- {
- mRemainingRepetitions--;
- if (trace)
- log.trace("Remaining repetitions: " + mRemainingRepetitions);
- }
- invoke(notification);
- if (mWaitForNextCallToStop)
- {
- stopSchedule();
- }
- }
- /**
- * Invokes the scheduler method.
- */
- protected abstract void invoke(Notification notification);
- }
- // -------------------------------------------------------------------------
- // Inner Classes
- // -------------------------------------------------------------------------
- /**
- * Calls {@link Schedulable#perform} on a plain Java Object.
- */
- public class PojoScheduler extends BaseListener
- {
- protected void invoke(Notification notification)
- {
- ClassLoader currentTCL = TCLActions.getContextClassLoader();
- try
- {
- ClassLoader loader = TCLActions.getClassLoader(mSchedulable.getClass());
- TCLActions.setContextClassLoader(loader);
- Date lTimeStamp = new Date(notification.getTimeStamp());
- mSchedulable.perform(lTimeStamp, getRemainingRepetitions());
- }
- catch (Exception e)
- {
- log.error("Scheduler.perform call failed", e);
- }
- finally
- {
- TCLActions.setContextClassLoader(currentTCL);
- }
- }
- }
- /**
- * Invokes an operation on an MBean.
- */
- public class MBeanListener extends BaseListener
- {
- protected void invoke(Notification notification)
- {
- Object[] lArguments = new Object[mSchedulableMBeanArguments.length];
- for (int i = 0; i < lArguments.length; i++)
- {
- switch (mSchedulableMBeanArguments[i])
- {
- case NOTIFICATION:
- lArguments[i] = notification;
- break;
- case DATE:
- lArguments[i] = new Date(notification.getTimeStamp());
- break;
- case REPETITIONS:
- lArguments[i] = new Long(mRemainingRepetitions);
- break;
- case SCHEDULER_NAME:
- lArguments[i] = getServiceName();
- break;
- default:
- lArguments[i] = null;
- }
- }
- if (log.isTraceEnabled())
- {
- log.debug("invoke " + mSchedulableMBean + " " + mSchedulableMBeanMethodName);
- log.debug("arguments: " + Arrays.asList(lArguments));
- log.debug("argument types: " + Arrays.asList(mSchedulableMBeanArgumentTypes));
- }
- try
- {
- getServer().invoke(
- mSchedulableMBean,
- mSchedulableMBeanMethodName,
- lArguments,
- mSchedulableMBeanArgumentTypes
- );
- }
- catch (Exception e)
- {
- log.error("Invoke failed for " + mSchedulableMBean + " " + mSchedulableMBeanMethodName, e);
- }
- }
- }
- }