[jboss-cvs] JBossAS SVN: r58158 - in trunk: testsuite/src/main/org/jboss/test/util/test varia/src/main/org/jboss/varia/scheduler varia/src/main/org/jboss/varia/scheduler/example
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Mon Nov 6 14:25:10 EST 2006
Author: genman
Date: 2006-11-06 14:25:06 -0500 (Mon, 06 Nov 2006)
New Revision: 58158
Modified:
trunk/testsuite/src/main/org/jboss/test/util/test/SchedulerUnitTestCase.java
trunk/varia/src/main/org/jboss/varia/scheduler/AbstractScheduleProvider.java
trunk/varia/src/main/org/jboss/varia/scheduler/ScheduleManager.java
trunk/varia/src/main/org/jboss/varia/scheduler/Scheduler.java
trunk/varia/src/main/org/jboss/varia/scheduler/SchedulerMBean.java
trunk/varia/src/main/org/jboss/varia/scheduler/example/SchedulableMBeanExample.java
trunk/varia/src/main/org/jboss/varia/scheduler/example/SchedulableMBeanExampleMBean.java
Log:
JIRA-3126
Added tests of the scheduler feature
The tests do not include error cases or some configuration cases yet
Cleaned up some of the scheduler classes:
Better logging (trace over debug), use MBean proxies, divided long methods
Addressed some potential race conditions in ScheduleManager
Modified: trunk/testsuite/src/main/org/jboss/test/util/test/SchedulerUnitTestCase.java
===================================================================
--- trunk/testsuite/src/main/org/jboss/test/util/test/SchedulerUnitTestCase.java 2006-11-06 19:24:43 UTC (rev 58157)
+++ trunk/testsuite/src/main/org/jboss/test/util/test/SchedulerUnitTestCase.java 2006-11-06 19:25:06 UTC (rev 58158)
@@ -22,9 +22,13 @@
package org.jboss.test.util.test;
import java.net.URL;
+import java.util.Date;
import javax.management.ObjectName;
+import javax.management.MBeanServerInvocationHandler;
import org.jboss.test.JBossTestCase;
+import org.jboss.varia.scheduler.ScheduleManager;
+import org.jboss.varia.scheduler.example.SchedulableMBeanExampleMBean;
/**
* Test case for the Scheduler Utility. The test
@@ -42,6 +46,9 @@
public class SchedulerUnitTestCase
extends JBossTestCase
{
+
+ private String file = "../resources/util/test-default-scheduler-service.xml";
+
/**
* Constructor for the SchedulerUnitTestCase object
*
@@ -52,52 +59,118 @@
super(name);
}
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
// Public --------------------------------------------------------
+ private void registered(ObjectName on) throws Exception
+ {
+ assertTrue(on + " isRegistered", getServer().isRegistered(on));
+ }
+
+ private SchedulableMBeanExampleMBean get(ObjectName on) throws Exception
+ {
+ SchedulableMBeanExampleMBean ex = (SchedulableMBeanExampleMBean)
+ MBeanServerInvocationHandler.newProxyInstance(getServer(), on,
+ SchedulableMBeanExampleMBean.class, false);
+ return ex;
+ }
+
+ private void check(ObjectName on, int hits, long remaining) throws Exception
+ {
+ SchedulableMBeanExampleMBean ex = get(on);
+ assertNotNull("name " + on, ex);
+
+ assertEquals("hits", hits, ex.getHitCount());
+
+ Date hd = ex.getHitDate();
+ if (hits > 0)
+ {
+ Date now = new Date();
+ assertNotNull("hit date", hd);
+ assertTrue("date " + hd + " " + now, !hd.after(now));
+ assertTrue("note", ex.getHitNotification() != null);
+ assertTrue("sched", ex.getSchedulerName() != null);
+ assertEquals("remaining", remaining, ex.getRemainingRepetitions());
+ }
+ assertEquals(null, ex.getTestString());
+ }
+
/**
- * Checks if the Scheduler is deployed and if not then
- * deployed the default one now.
+ * Tests the default scheduler.
*/
public void testDefaultScheduler()
throws Exception
{
- // The class loader used to locate the configuration file
- ClassLoader lLoader = Thread.currentThread().getContextClassLoader();
- assertTrue("ContextClassloader missing", lLoader != null);
- //Get URL for deployable *service.xml file in resources
- URL serviceURL = lLoader.getResource("util/test-default-scheduler-service.xml");
- if (serviceURL == null)
- {
- //if we're running from the jmxtest.jar, it should be here instead
- serviceURL = lLoader.getResource("test-default-scheduler-service.xml");
- }
- assertTrue("resource test-default-scheduler-service.xml not found", serviceURL != null);
+ ObjectName ex1 = new ObjectName("test:name=SchedulableMBeanExample,instance=1");
+ ObjectName ex2 = new ObjectName("test:name=SchedulableMBeanExample,instance=2");
+ ObjectName ex3 = new ObjectName("test:name=SchedulableMBeanExample,instance=3");
+ ObjectName scheduler0 = new ObjectName("test:service=Scheduler");
+ ObjectName scheduler1 = new ObjectName("test:service=Scheduler,name=SchedulableMBeanExample,instance=1");
+ ObjectName scheduler2 = new ObjectName("test:service=Scheduler,name=SchedulableMBeanExample,instance=2");
+ ObjectName manager1 = new ObjectName("test:service=Scheduler,name=ScheduleManager");
+
+ deploy(file);
try
{
- deploy(serviceURL.toString());
- ObjectName scheduler = new ObjectName("test:service=Scheduler");
- assertTrue("test:service=Scheduler isRegistered",
- getServer().isRegistered(scheduler));
- ObjectName ex1 = new ObjectName("test:name=SchedulableMBeanExample");
- assertTrue("test:name=SchedulableMBeanExample isRegistered",
- getServer().isRegistered(ex1));
- ObjectName scheduler2 = new ObjectName("test:service=Scheduler,name=SchedulableMBeanExample");
- assertTrue("test:service=Scheduler,name=SchedulableMBeanExample isRegistered",
- getServer().isRegistered(scheduler2));
+ registered(new ObjectName(ScheduleManager.DEFAULT_TIMER_NAME));
+ registered(ex1);
+ registered(ex2);
+ registered(ex3);
+ registered(scheduler0);
+ registered(scheduler1);
+ registered(scheduler2);
+ registered(manager1);
+ check(ex1, 0, 0);
+ check(ex2, 0, 0);
+ check(ex3, 0, 0);
+
+ Thread.sleep(1100); // NOW == one second
+ check(ex1, 1, 0);
+ check(ex2, 0, 0); // StartAtStartup is false
+ check(ex3, 1, 2);
+ invoke(scheduler2, "startSchedule", null, null);
+
+ Thread.sleep(400); // one period
+ check(ex1, 1, 0); // done
+ check(ex2, 0, 0); // wait 500 more
+ check(ex3, 2, 1); // last
+
+ Thread.sleep(500); // one period
+ check(ex1, 1, 0); // done
+ check(ex2, 1, -1); // first
+ check(ex3, 3, 0); // done
+
+ Thread.sleep(500); // one period
+ check(ex2, 2, -1); // first
+ invoke(scheduler2, "stopSchedule", new Object[] { Boolean.TRUE }, new String[] { boolean.class.getName() });
+
+ Thread.sleep(500); // one period
+ check(ex2, 2, -1); // no more
+ invoke(scheduler2, "startSchedule", null, null);
+
+ Thread.sleep(1001); // one period
+ check(ex2, 3, -1); // restated
}
finally
{
- undeploy(serviceURL.toString());
- } // end of try-finally
+ undeploy(file);
+ }
}
- /** Test the deployment of a ear containing a sar which creates an
+ /**
+ * Test the deployment of a ear containing a sar which creates an
* instance of the org.jboss.varia.scheduler.Scheduler service with a
* Schedulable class that exists in an external jar referenced by the
* sar manifest.
*
* @throws Exception
- */
public void testExternalServiceJar() throws Exception
{
// Deploy the external jar containg the Schedulable
@@ -117,4 +190,5 @@
undeploy("scheduler.jar");
}
}
+ */
}
Modified: trunk/varia/src/main/org/jboss/varia/scheduler/AbstractScheduleProvider.java
===================================================================
--- trunk/varia/src/main/org/jboss/varia/scheduler/AbstractScheduleProvider.java 2006-11-06 19:24:43 UTC (rev 58157)
+++ trunk/varia/src/main/org/jboss/varia/scheduler/AbstractScheduleProvider.java 2006-11-06 19:25:06 UTC (rev 58158)
@@ -28,6 +28,7 @@
import javax.management.MBeanException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
+import javax.management.MBeanServerInvocationHandler;
import org.jboss.system.ServiceMBeanSupport;
@@ -49,6 +50,7 @@
{
/** The schedule manager ObjectName */
private ObjectName scheduleManagerName = ScheduleManagerMBean.OBJECT_NAME;
+ private ScheduleManagerMBean manager;
// ------------------------------------------------------------------------
// Constructors
@@ -127,30 +129,13 @@
ObjectName pTarget, String pMethodName, String[] pMethodSignature,
Date pStart, long pPeriod, int pRepetitions) throws JMException
{
- return (
- (Integer) server.invoke(
- scheduleManagerName,
- "addSchedule",
- new Object[] {
- serviceName,
+ return manager.addSchedule(serviceName,
pTarget,
pMethodName,
pMethodSignature,
pStart,
- new Long(pPeriod),
- new Integer((int) pRepetitions)
- },
- new String[] {
- ObjectName.class.getName(),
- ObjectName.class.getName(),
- String.class.getName(),
- String[].class.getName(),
- Date.class.getName(),
- Long.TYPE.getName(),
- Integer.TYPE.getName()
- }
- )
- ).intValue();
+ pPeriod,
+ pRepetitions);
}
/**
@@ -160,12 +145,7 @@
*/
protected void removeSchedule(int pID) throws JMException
{
- server.invoke(
- scheduleManagerName,
- "removeSchedule",
- new Object[] { new Integer(pID)},
- new String[] { Integer.TYPE.getName()}
- );
+ manager.removeSchedule(pID);
}
// -------------------------------------------------------------------------
@@ -184,6 +164,8 @@
*/
protected void startService() throws Exception
{
+ this.manager = (ScheduleManagerMBean)MBeanServerInvocationHandler.newProxyInstance(
+ getServer(), scheduleManagerName, ScheduleManagerMBean.class, false);
startScheduleProviderService();
}
@@ -208,12 +190,7 @@
protected void startScheduleProviderService()
throws InstanceNotFoundException, MBeanException, ReflectionException
{
- server.invoke(
- scheduleManagerName,
- "registerProvider",
- new Object[] { serviceName.toString()},
- new String[] { String.class.getName()}
- );
+ manager.registerProvider(serviceName.toString());
}
/**
@@ -222,19 +199,7 @@
protected void stopScheduleProviderService()
throws InstanceNotFoundException, MBeanException, ReflectionException
{
- try
- {
- server.invoke(
- scheduleManagerName,
- "unregisterProvider",
- new Object[] { serviceName.toString()},
- new String[] { String.class.getName()}
- );
- }
- catch (JMException jme)
- {
- log.error("Could not unregister the Provider from the Schedule Manager", jme);
- }
+ manager.unregisterProvider(serviceName.toString());
}
}
Modified: trunk/varia/src/main/org/jboss/varia/scheduler/ScheduleManager.java
===================================================================
--- trunk/varia/src/main/org/jboss/varia/scheduler/ScheduleManager.java 2006-11-06 19:24:43 UTC (rev 58157)
+++ trunk/varia/src/main/org/jboss/varia/scheduler/ScheduleManager.java 2006-11-06 19:25:06 UTC (rev 58158)
@@ -22,30 +22,43 @@
package org.jboss.varia.scheduler;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Date;
-import java.util.Hashtable;
import java.util.Iterator;
+import java.util.Map;
import javax.management.InstanceNotFoundException;
import javax.management.JMException;
import javax.management.MBeanException;
import javax.management.MBeanServer;
+import javax.management.MBeanServerInvocationHandler;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.management.NotificationListener;
+import javax.management.NotificationEmitter;
import javax.management.ObjectName;
import javax.management.timer.TimerNotification;
+import javax.management.timer.TimerMBean;
+import javax.management.timer.Timer;
+import EDU.oswego.cs.dl.util.concurrent.SynchronizedInt;
+import EDU.oswego.cs.dl.util.concurrent.SynchronizedBoolean;
+import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
import org.jboss.logging.Logger;
import org.jboss.system.ServiceMBeanSupport;
/**
- * Schedule Manager which manage the Schedule and their matching Timer notifications
- * and notification listeners.
- * Each provider has to register when it is started where in turn their startProviding()
- * method is called which allows him to add its Schedules because the Manager is now
- * ready.
+ * ScheduleManager manages multiple scheduled timer listeners.
+ * These are registered using the {@link #addSchedule} operation.
+ * Providers (basically MBean lifecycle listeners) can be registered using the
+ * {@link #registerProvider} operation.
*
+ * Because of the way the JBoss deployment model works (no way to readily
+ * invoke operations at deployment time), prefer to use the {@link Scheduler}
+ * MBean instead.
+ *
* @author <a href="mailto:andreas at jboss.org">Andreas Schaefer</a>
* @author <a href="mailto:dimitris at jboss.org">Dimitris Andreadis</a>
* @version $Revision$
@@ -58,11 +71,15 @@
// Constants
// -------------------------------------------------------------------------
- /** Default Timer Object Name **/
+ /**
+ * Default Timer Object Name
+ */
public static String DEFAULT_TIMER_NAME = "jboss:service=Timer";
- /** Counter for the Schedule Instance **/
- private static int sCounter = 0;
+ /**
+ * Counter for the number of scheduled instances.
+ */
+ private static SynchronizedInt sCounter = new SynchronizedInt(0);
private static final int NOTIFICATION = 0;
private static final int DATE = 1;
@@ -77,21 +94,24 @@
// -------------------------------------------------------------------------
private String mTimerName = DEFAULT_TIMER_NAME;
- private ObjectName mTimer;
+ private ObjectName mTimerObjectName;
+ private TimerMBean mTimer;
+ private NotificationEmitter mTimerEmitter;
- private boolean mWaitForNextCallToStop = false;
- private boolean mStartOnStart = false;
+ private boolean mStartOnStart = true;
private boolean mFixedRate = false;
- private boolean mIsPaused = false;
+ private SynchronizedBoolean mIsPaused = new SynchronizedBoolean(false);
/**
- * List of registered providers to inform them when the
- * Schedule is stop / started or destroyed
+ * List of registered AbstractScheduleProvider ObjectNames to inform
+ * when the this manager is stop or started.
*/
- private ArrayList mProviders = new ArrayList();
+ private List mProviders = Collections.synchronizedList(new ArrayList());
- /** List of added Schedules */
- private Hashtable mSchedules = new Hashtable();
+ /**
+ * Maps Integer to registered ScheduleInstance.
+ */
+ private Map mSchedules = new ConcurrentReaderHashMap();
// -------------------------------------------------------------------------
// Constructors
@@ -115,6 +135,7 @@
*/
public void startSchedules()
{
+ log.debug("startSchedules()");
// Check if not already started
if (!isStarted())
{
@@ -127,9 +148,9 @@
{
lInstance.start();
}
- catch (JMException jme)
+ catch (JMException e)
{
- log.error("Could not start a Schedule", jme);
+ log.error("Could not start: " + lInstance, e);
}
}
}
@@ -158,16 +179,16 @@
{
lInstance.stop();
}
- catch (JMException jme)
+ catch (JMException e)
{
- log.error("Could not stop a Schedule", jme);
+ log.error("Could not stop: " + lInstance, e);
}
}
}
}
/**
- * Stops the server right now and starts it right now.
+ * Stops existing schedules immediately and restarts them right away.
*
* @jmx:managed-operation
*/
@@ -178,7 +199,7 @@
}
/**
- * Register a Provider to make him available. In turn this
+ * Register a Provider to make it available. In turn this
* method calls "startProviding()" method on the Provider
* to indicate that the Provider can start adding Schedules.
*
@@ -188,35 +209,43 @@
*/
public void registerProvider(String pProviderObjectName)
{
- if (pProviderObjectName == null)
+ try
+ {
+ registerProvider(new ObjectName(pProviderObjectName));
+ }
+ catch (JMException jme)
{
- throw new RuntimeException("Provider must not be null");
+ log.error("Could not call startProviding() on " + pProviderObjectName, jme);
}
- int lIndex = mProviders.indexOf(pProviderObjectName);
- if (lIndex >= 0)
+ }
+
+ /**
+ * Register a Provider to make it available. In turn this
+ * method calls "startProviding()" method on the Provider
+ * to indicate that the Provider can start adding Schedules.
+ *
+ * @param pProviderObjectName Object Name of the Provider
+ *
+ * @jmx:managed-operation
+ */
+ public void registerProvider(ObjectName pProviderObjectName)
+ throws JMException
+ {
+ if (pProviderObjectName == null)
{
- // Provider found then do nothing
- return;
+ throw new MalformedObjectNameException("Provider must not be null");
}
- // No provider found ==> a new provider
- mProviders.add(pProviderObjectName);
- // Initiate the Provider to provide schedules
- try
- {
- ObjectName lProviderName = new ObjectName(pProviderObjectName);
- //log.debug( "calling startProviding(): " + lProviderName + ", based on: " + pProviderObjectName );
- server.invoke(
- lProviderName,
- "startProviding",
- new Object[]{},
- new String[]{}
- );
- }
- catch (JMException jme)
- {
- log.error("Could not call startProviding() on the provider", jme);
- // Ignore Exceptions
- }
+ synchronized (mProviders) {
+ if (mProviders.contains(pProviderObjectName))
+ throw new JMException("Already registered: " + pProviderObjectName);
+ mProviders.add(pProviderObjectName);
+ }
+ server.invoke(
+ pProviderObjectName,
+ "startProviding",
+ new Object[]{},
+ new String[]{}
+ );
}
/**
@@ -229,36 +258,38 @@
*/
public void unregisterProvider(String pProviderObjectName)
{
- int lIndex = mProviders.indexOf(pProviderObjectName);
- if (lIndex < 0)
- {
- // Provider not found then do nothing
- return;
- }
- // Stop the Provider from providing schedules
try
{
- ObjectName lProviderName = new ObjectName(pProviderObjectName);
- server.invoke(
- lProviderName,
- "stopProviding",
- new Object[]{},
- new String[]{}
- );
+ unregisterProvider(new ObjectName(pProviderObjectName));
}
catch (JMException jme)
{
- log.error("Could not call stopProviding() on the provider", jme);
- // Ignore Exceptions
+ log.error("Could not call stopProviding() on " + pProviderObjectName, jme);
}
- finally
- {
- // Finally remove the provider
- mProviders.remove(pProviderObjectName);
- }
}
/**
+ * Unregister a Provider which in turn calls "stopProviding()"
+ * indicating to the Provider to remove all the Schedules.
+ *
+ * @param pProviderObjectName Object Name of the Provider
+ *
+ * @jmx:managed-operation
+ */
+ public void unregisterProvider(ObjectName pProviderObjectName)
+ throws JMException
+ {
+ if (!mProviders.remove(pProviderObjectName))
+ return;
+ server.invoke(
+ pProviderObjectName,
+ "stopProviding",
+ new Object[]{},
+ new String[]{}
+ );
+ }
+
+ /**
* Adds a new Schedule to the Scheduler
*
* @param pTarget Object Name of the Target MBean
@@ -283,12 +314,6 @@
int pRepetitions
)
{
- /*
- String sig = "{";
- for (int i = 0; i < pMethodSignature.length; i++) sig += pMethodSignature[i];
- sig += "}";
- log.info( "ScheduleManager.addScheduler(" + pProvider + ", " + pTarget + ", " + pMethodName + "," + sig + ", " + pStartDate + ", " + pPeriod + ", " + pRepetitions);
- */
ScheduleInstance lInstance = new ScheduleInstance(
pProvider,
pTarget,
@@ -298,7 +323,6 @@
pRepetitions,
pPeriod
);
- // Start it now if the Schedule Manager is started
if (isStarted())
{
try
@@ -307,7 +331,7 @@
}
catch (JMException jme)
{
- log.error("Could not start the Schedule", jme);
+ log.error("Could not start " + lInstance, jme);
}
}
int lID = lInstance.getID();
@@ -329,11 +353,13 @@
ScheduleInstance lInstance = (ScheduleInstance) mSchedules.get(new Integer(pIdentification));
try
{
+ if (lInstance == null)
+ throw new InstanceNotFoundException();
lInstance.stop();
}
- catch (JMException jme)
+ catch (JMException e)
{
- log.error("Could not stop a Schedule", jme);
+ log.error("Could not stop " + lInstance, e);
}
mSchedules.remove(new Integer(pIdentification));
}
@@ -353,12 +379,12 @@
ScheduleInstance lInstance = (ScheduleInstance) i.next();
if (lFirst)
{
- lReturn.append(lInstance.mIdentification + "");
+ lReturn.append(lInstance.mIdentification);
lFirst = false;
}
else
{
- lReturn.append("," + lInstance.mIdentification);
+ lReturn.append(",").append(lInstance.mIdentification);
}
}
return lReturn.toString();
@@ -371,7 +397,7 @@
*/
public boolean isPaused()
{
- return mIsPaused;
+ return mIsPaused.get();
}
/**
@@ -382,7 +408,7 @@
*/
public void setPaused(boolean pIsPaused)
{
- mIsPaused = pIsPaused;
+ mIsPaused.set(pIsPaused);
}
/**
@@ -483,40 +509,26 @@
*/
protected void startService() throws Exception
{
- // Create Timer MBean if necessary
- try
+ mTimerObjectName = new ObjectName(mTimerName);
+ if (!getServer().isRegistered(mTimerObjectName))
{
- mTimer = new ObjectName(mTimerName);
+ getServer().createMBean(Timer.class.getName(), mTimerObjectName);
}
- catch (MalformedObjectNameException mone)
+ mTimer = (TimerMBean)MBeanServerInvocationHandler.newProxyInstance(getServer(),
+ mTimerObjectName, TimerMBean.class, true);
+ mTimerEmitter = (NotificationEmitter)mTimer;
+ if (!mTimer.isActive())
{
- mTimer = new ObjectName(DEFAULT_TIMER_NAME);
+ mTimer.start();
}
-
- if (!getServer().isRegistered(mTimer))
- {
- getServer().createMBean("javax.management.timer.Timer", mTimer);
- }
- if (!((Boolean) getServer().getAttribute(mTimer, "Active")).booleanValue())
- {
- // Now start the Timer
- getServer().invoke(
- mTimer,
- "start",
- new Object[]{},
- new String[]{}
- );
- }
- log.debug("Start Schedules when Service is (re) started");
startSchedules();
}
/**
- * Stops all available Schedules.
- **/
+ * Stops all Schedules.
+ */
protected void stopService()
{
- // Stop the schedules right now !!
stopSchedules(true);
}
@@ -558,105 +570,105 @@
public void handleNotification(Notification pNotification, Object pHandback)
{
- log.debug("MBeanListener.handleNotification(), notification: " + pNotification);
- try
- {
- // If schedule is started invoke the schedule method on the Schedulable instance
- log.debug("Scheduler is started: " + isStarted());
- Date lTimeStamp = new Date(pNotification.getTimeStamp());
- if (isStarted())
- {
- if (mSchedule.mRemainingRepetitions > 0 || mSchedule.mRemainingRepetitions < 0)
- {
- if (mSchedule.mRemainingRepetitions > 0)
- {
- mSchedule.mRemainingRepetitions--;
- }
- if (!mIsPaused)
- {
- Object[] lArguments = new Object[mSchedule.mSchedulableMBeanArguments.length];
- for (int i = 0; i < lArguments.length; i++)
- {
- switch (mSchedule.mSchedulableMBeanArguments[i])
- {
- case ID:
- lArguments[i] = pNotification.getUserData();
- break;
- case NOTIFICATION:
- lArguments[i] = pNotification;
- break;
- case DATE:
- lArguments[i] = lTimeStamp;
- break;
- case REPETITIONS:
- lArguments[i] = new Long(mSchedule.mRemainingRepetitions);
- break;
- case SCHEDULER_NAME:
- lArguments[i] = getServiceName();
- break;
- case NEXT_DATE:
- lArguments[i] = new Date(lTimeStamp.getTime() + mSchedule.mPeriod);
- break;
- default:
- lArguments[i] = null;
- }
- }
- log.debug("MBean Arguments are: " + java.util.Arrays.asList(lArguments));
- log.debug("MBean Arguments Types are: " + java.util.Arrays.asList(mSchedule.mSchedulableMBeanArgumentTypes));
- try
- {
- log.debug("invoke(" + mSchedule.mTarget + ", " + mSchedule.mMethodName);
- getServer().invoke(
- mSchedule.mTarget,
- mSchedule.mMethodName,
- lArguments,
- mSchedule.mSchedulableMBeanArgumentTypes
- );
- }
- catch (javax.management.JMRuntimeException jmre)
- {
- log.error("Invoke of the Schedulable MBean failed", jmre);
- }
- catch (javax.management.JMException jme)
- {
- log.error("Invoke of the Schedulable MBean failed", jme);
- }
- log.debug("Remaining Repititions: " + mSchedule.mRemainingRepetitions +
- ", wait for next call to stop: " + mWaitForNextCallToStop);
- if (mSchedule.mRemainingRepetitions == 0 || mWaitForNextCallToStop)
- {
- mSchedule.stop();
- }
- }
- }
- }
- else
- {
- mSchedule.stop();
- }
- }
- catch (Exception e)
- {
- log.error("Handling a Scheduler call failed", e);
- }
+ boolean trace = log.isTraceEnabled();
+ if (trace) {
+ log.trace("MBeanListener.handleNotification: " + pNotification);
+ }
+
+ try
+ {
+ if (!isStarted()) {
+ log.trace("Scheduler not started");
+ mSchedule.stop();
+ return;
+ }
+ if (mSchedule.mRemainingRepetitions == 0)
+ {
+ log.trace("No more repetitions");
+ mSchedule.stop();
+ return;
+ }
+ if (mIsPaused.get())
+ {
+ log.trace("Paused");
+ return;
+ }
+ if (mSchedule.mRemainingRepetitions > 0)
+ {
+ mSchedule.mRemainingRepetitions--;
+ if (trace)
+ log.trace("Remaining repetitions: " + mSchedule.mRemainingRepetitions);
+ }
+ Object[] lArguments = getArguments(pNotification);
+ if (trace)
+ {
+ log.trace("invoke " + mSchedule);
+ log.trace("arguments are: " + Arrays.asList(lArguments));
+ }
+
+ ObjectName on = mSchedule.mTarget;
+ String mn = mSchedule.mMethodName;
+ getServer().invoke(on, mn, lArguments,
+ mSchedule.mSchedulableMBeanArgumentTypes
+ );
+ }
+ catch (Exception e)
+ {
+ log.error("Invoke failed: " + mSchedule.getTargetString(), e);
+ }
}
+
+ private Object[] getArguments(Notification pNotification)
+ {
+ Object[] lArguments = new Object[mSchedule.mSchedulableMBeanArguments.length];
+ Date lTimeStamp = new Date(pNotification.getTimeStamp());
+ for (int i = 0; i < lArguments.length; i++)
+ {
+ switch (mSchedule.mSchedulableMBeanArguments[i])
+ {
+ case ID:
+ lArguments[i] = pNotification.getUserData();
+ break;
+ case NOTIFICATION:
+ lArguments[i] = pNotification;
+ break;
+ case DATE:
+ lArguments[i] = lTimeStamp;
+ break;
+ case REPETITIONS:
+ lArguments[i] = new Long(mSchedule.mRemainingRepetitions);
+ break;
+ case SCHEDULER_NAME:
+ lArguments[i] = getServiceName();
+ break;
+ case NEXT_DATE:
+ lArguments[i] = new Date(lTimeStamp.getTime() + mSchedule.mPeriod);
+ break;
+ default:
+ lArguments[i] = null;
+ }
+ }
+ return lArguments;
+ }
+
}
/**
* Filter to ensure that each Scheduler only gets notified when it is supposed to.
*/
- private static class NotificationFilter implements javax.management.NotificationFilter
+ static class IdNotificationFilter implements javax.management.NotificationFilter
{
+ private static final Logger log = Logger.getLogger(IdNotificationFilter.class);
- private Integer mId;
+ private Integer filterId;
/**
* Create a Filter.
* @param pId the Scheduler id
*/
- public NotificationFilter(Integer pId)
+ public IdNotificationFilter(int filterId)
{
- mId = pId;
+ this.filterId = new Integer(filterId);
}
/**
@@ -664,12 +676,17 @@
*/
public boolean isNotificationEnabled(Notification pNotification)
{
- if (pNotification instanceof TimerNotification)
- {
- TimerNotification lTimerNotification = (TimerNotification) pNotification;
- return lTimerNotification.getNotificationID().equals(mId);
- }
- return false;
+ if (!(pNotification instanceof TimerNotification))
+ return false;
+ TimerNotification lTimerNotification = (TimerNotification) pNotification;
+ if (log.isTraceEnabled())
+ log.trace("isNotificationEnabled(), filterId=" + filterId +
+ ", notification=" + pNotification +
+ ", notificationId=" + lTimerNotification.getNotificationID() +
+ ", timestamp=" + lTimerNotification.getTimeStamp() +
+ ", message=" + lTimerNotification.getMessage()
+ );
+ return lTimerNotification.getNotificationID().equals(filterId);
}
}
@@ -753,7 +770,7 @@
mSchedulableMBeanArgumentTypes[i] = lToken;
}
}
- mIdentification = (sCounter++);
+ mIdentification = sCounter.increment();
}
/**
@@ -805,36 +822,20 @@
lStartDate = mStartDate;
mRemainingRepetitions = mInitialRepetitions;
}
- mNotificationID = ((Integer) getServer().invoke(
- mTimer,
- "addNotification",
- new Object[]{
- "Schedule",
- "Scheduler Notification",
- new Integer(getID()), // User Object
- lStartDate,
- new Long(mPeriod),
- mRemainingRepetitions < 0 ? new Long(0) : new Long(mRemainingRepetitions),
- new Boolean(mFixedRate)
- },
- new String[]{
- String.class.getName(),
- String.class.getName(),
- Object.class.getName(),
- Date.class.getName(),
- Long.TYPE.getName(),
- Long.TYPE.getName(),
- Boolean.TYPE.getName()
- }
- )).intValue();
- // Register the notification listener at the MBeanServer
+ mNotificationID = mTimer.addNotification(
+ "Schedule", "Scheduler Notification",
+ new Integer(getID()), // User Object
+ lStartDate,
+ new Long(mPeriod),
+ mRemainingRepetitions < 0 ? new Long(0) : new Long(mRemainingRepetitions),
+ Boolean.valueOf(mFixedRate)
+ );
mListener = new MBeanListener(this);
- getServer().addNotificationListener(
- mTimer,
- mListener,
- new NotificationFilter(new Integer(mNotificationID)),
- // No object handback necessary
- null
+ mTimerEmitter.addNotificationListener(
+ mListener,
+ new IdNotificationFilter(mNotificationID),
+ // No object handback necessary
+ null
);
log.debug("start(), add Notification to Timer with ID: " + mNotificationID);
}
@@ -847,38 +848,31 @@
throws JMException
{
log.debug("stopSchedule(), notification id: " + mNotificationID);
- getServer().removeNotificationListener(
- mTimer,
- mListener
- );
- try
- {
- getServer().invoke(
- mTimer,
- "removeNotification",
- new Object[]{
- new Integer(mNotificationID)
- },
- new String[]{
- Integer.class.getName()
- }
- );
- }
- catch (MBeanException mbe)
- {
- Exception e = mbe.getTargetException();
- // If target exception is InstanceNotFoundException then
- // the notification is already removed so ignore it
- if (!(e instanceof InstanceNotFoundException))
- {
- throw mbe;
- }
- }
+ mTimerEmitter.removeNotificationListener(mListener);
+ try
+ {
+ mTimer.removeNotification(mNotificationID);
+ }
+ catch (InstanceNotFoundException e)
+ {
+ log.trace(e);
+ }
}
public int getID()
{
return mIdentification;
}
+
+ public String toString()
+ {
+ return "Schedule target=" + getTargetString();
+ }
+
+ public String getTargetString()
+ {
+ return mTarget + " " + mMethodName + "" + Arrays.asList(mSchedulableMBeanArgumentTypes);
+ }
+
}
}
Modified: trunk/varia/src/main/org/jboss/varia/scheduler/Scheduler.java
===================================================================
--- trunk/varia/src/main/org/jboss/varia/scheduler/Scheduler.java 2006-11-06 19:24:43 UTC (rev 58157)
+++ trunk/varia/src/main/org/jboss/varia/scheduler/Scheduler.java 2006-11-06 19:25:06 UTC (rev 58158)
@@ -27,11 +27,17 @@
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;
@@ -39,14 +45,21 @@
import org.jboss.util.Classes;
/**
- * Scheduler Instance to allow clients to run this as a scheduling service for
- * any Schedulable instances.
- * <br>
- * ATTENTION: The scheduler instance only allows to run one schedule at a time.
- * Therefore when you want to run two schedules create to instances with this
- * MBean. Suggested Object Name for the MBean are:<br>
- * :service=Scheduler,schedule=<you schedule name><br>
+ * 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 at jboss.org">Andreas Schaefer</a>
* @author Cameron (camtabor)
@@ -62,8 +75,7 @@
public static String JNDI_NAME = "scheduler:domain";
public static String JMX_NAME = "scheduler";
- /** Default Timer Object Name **/
- public static String DEFAULT_TIMER_NAME = "jboss:service=Timer";
+ public static String DEFAULT_TIMER_NAME = ScheduleManager.DEFAULT_TIMER_NAME;
private static final int NOTIFICATION = 0;
private static final int DATE = 1;
@@ -77,9 +89,11 @@
private long mActualSchedulePeriod;
private long mRemainingRepetitions = 0;
- private int mActualSchedule = -1;
+ private int mNotificationID = -1;
private String mTimerName = DEFAULT_TIMER_NAME;
- private ObjectName mTimer;
+ private ObjectName mTimerObjectName;
+ private TimerMBean mTimer;
+ private NotificationEmitter mTimerEmitter;
private Schedulable mSchedulable;
private boolean mScheduleIsStarted = false;
@@ -110,22 +124,35 @@
private long mInitialRepetitions;
private boolean mFixedRate = false;
- private NotificationListener listener;
+ private NotificationListener mListener;
// -------------------------------------------------------------------------
// Constructors
// -------------------------------------------------------------------------
/**
- * Default (no-args) Constructor
+ * 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
@@ -150,7 +177,7 @@
}
/**
- *
+ * Constructs a new Scheduler instance.
* @param pSchedulableClass
* @param pInitArguments
* @param pInitTypes
@@ -182,7 +209,145 @@
// -------------------------------------------------------------------------
// 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
@@ -196,215 +361,54 @@
*/
public void startSchedule()
{
- // Check if not already started
- if (!isStarted())
+ if (isStarted())
{
- try
- {
- // Check the given attributes if correct
- if (mUseMBean)
- {
- 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()
- };
- }
- }
- else
- {
- if (mSchedulableClass == null)
- {
- log.debug("Schedulable Class is not set");
- throw new InvalidParameterException(
- "Schedulable Class must be set"
- );
- }
- if (mSchedulableArgumentList.length != mSchedulableArgumentTypeList.length)
- {
- log.debug("Schedulable Class Arguments and Types do not match in length");
- throw new InvalidParameterException(
- "Schedulable Class Arguments and Types do not match in length"
- );
- }
- }
- if (mSchedulePeriod <= 0)
- {
- log.debug("Schedule Period is less than 0 (ms)");
- throw new InvalidParameterException(
- "Schedule Period must be set and greater than 0 (ms)"
- );
- }
- if (!mUseMBean)
- {
- // 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");
- }
- }
+ log.debug("already started");
+ return;
+ }
- mRemainingRepetitions = mInitialRepetitions;
- mActualSchedulePeriod = mSchedulePeriod;
- Date lStartDate = null;
- // Register the Schedule at the Timer
- // If start date is NOW then take the current date
- if (mStartDateIsNow)
- {
- mStartDate = new Date(new Date().getTime() + 1000);
- lStartDate = mStartDate;
- }
- else
- {
- // Check if initial start date is in the past
- if (mStartDate.getTime() < new Date().getTime())
- {
- // 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;
- }
- }
- lStartDate = new Date(mStartDate.getTime() + (lSkipRepeats * mActualSchedulePeriod));
- }
- else
- {
- lStartDate = mStartDate;
- }
- }
- log.debug("Schedule initial call to: " + lStartDate + ", remaining repetitions: " + mRemainingRepetitions);
- // Add an initial call
- mActualSchedule = ((Integer) getServer().invoke(
- mTimer,
- "addNotification",
- new Object[]{
- "Schedule",
- "Scheduler Notification",
- null, // User Object
- lStartDate,
- new Long(mActualSchedulePeriod),
- mRemainingRepetitions < 0 ? new Long(0) : new Long(mRemainingRepetitions),
- new Boolean(mFixedRate)
- },
- new String[]{
- String.class.getName(),
- String.class.getName(),
- Object.class.getName(),
- Date.class.getName(),
- Long.TYPE.getName(),
- Long.TYPE.getName(),
- Boolean.TYPE.getName()
- }
- )).intValue();
- if (mUseMBean)
- {
- listener = new MBeanListener(mSchedulableMBean);
- }
- else
- {
- listener = new Listener(mSchedulable);
- }
- // Register the notification listener at the MBeanServer
- getServer().addNotificationListener(
- mTimer,
- listener,
- new Scheduler.NotificationFilter(new Integer(mActualSchedule)),
- // No object handback necessary
- null
- );
- mScheduleIsStarted = true;
- mIsRestartPending = false;
- }
- catch (Exception e)
- {
- log.error("operation failed", e);
- }
- }
+ 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.
*
@@ -416,9 +420,10 @@
*/
public void stopSchedule(boolean pDoItNow)
{
+ log.debug("stopSchedule(" + pDoItNow + ")");
try
{
- if (mActualSchedule < 0)
+ if (mNotificationID < 0)
{
mScheduleIsStarted = false;
mWaitForNextCallToStop = false;
@@ -426,29 +431,22 @@
}
if (pDoItNow)
{
+ log.debug("stopSchedule(), removing schedule id: " + mNotificationID);
mWaitForNextCallToStop = false;
- // Remove notification listener now
- if (listener != null)
+ if (mListener != null)
{
- getServer().removeNotificationListener(
- mTimer,
- listener
- );
- listener = null;
+ mTimerEmitter.removeNotificationListener(mListener);
+ try
+ {
+ mTimer.removeNotification(mNotificationID);
+ }
+ catch (InstanceNotFoundException e)
+ {
+ log.trace(e);
+ }
+ mListener = null;
}
- log.debug("stopSchedule(), schedule id: " + mActualSchedule);
- getServer().invoke(
- mTimer,
- "removeNotification",
- new Object[]{
- new Integer(mActualSchedule)
- },
- new String[]{
- Integer.class.getName()
- }
- );
- log.debug("stopSchedule(), removed schedule id: " + mActualSchedule);
- mActualSchedule = -1;
+ mNotificationID = -1;
mScheduleIsStarted = false;
}
else
@@ -458,7 +456,7 @@
}
catch (Exception e)
{
- log.error("operation failed", e);
+ log.error("stopSchedule failed", e);
}
}
@@ -469,7 +467,7 @@
*/
public void restartSchedule()
{
- stopSchedule(true);
+ stopSchedule();
startSchedule();
}
@@ -687,7 +685,7 @@
/**
* @jmx:managed-attribute
*
- * @return Object Name if a Schedulalbe MBean is set
+ * @return Object Name if a Schedulable MBean is set
*/
public String getSchedulableMBean()
{
@@ -725,10 +723,9 @@
mSchedulableMBean = new ObjectName(pSchedulableMBean);
mUseMBean = true;
}
- catch (MalformedObjectNameException mone)
+ catch (MalformedObjectNameException e)
{
- log.error("Schedulable MBean Object Name is malformed", mone);
- throw new InvalidParameterException("Schedulable MBean is not correctly formatted");
+ throw new InvalidParameterException("Schedulable MBean name invalid " + pSchedulableMBean);
}
}
@@ -780,14 +777,14 @@
return;
}
int lIndex = pSchedulableMBeanMethod.indexOf('(');
- String lMethodName = "";
- if (lIndex < 0)
+ String lMethodName;
+ if (lIndex == -1)
{
lMethodName = pSchedulableMBeanMethod.trim();
mSchedulableMBeanArguments = new int[0];
mSchedulableMBeanArgumentTypes = new String[0];
}
- else if (lIndex > 0)
+ else
{
lMethodName = pSchedulableMBeanMethod.substring(0, lIndex).trim();
}
@@ -818,7 +815,7 @@
}
else
{
- StringTokenizer lTokenizer = new StringTokenizer(lArguments, ",");
+ StringTokenizer lTokenizer = new StringTokenizer(lArguments, ", ");
mSchedulableMBeanArguments = new int[lTokenizer.countTokens()];
mSchedulableMBeanArgumentTypes = new String[lTokenizer.countTokens()];
for (int i = 0; lTokenizer.hasMoreTokens(); i++)
@@ -974,7 +971,7 @@
}
else if (mStartDateString.equals("NOW"))
{
- mStartDate = new Date(new Date().getTime() + 1000);
+ mStartDate = getNow();
mStartDateIsNow = true;
}
else
@@ -985,7 +982,7 @@
mStartDate = new Date(lDate);
mStartDateIsNow = false;
}
- catch (Exception e)
+ catch (NumberFormatException e)
{
try
{
@@ -999,7 +996,7 @@
catch (Exception e2)
{
log.error("Could not parse given date string: " + mStartDateString, e2);
- throw new InvalidParameterException("Schedulable Date is not of correct format");
+ throw new InvalidParameterException("Schedulable Date is not of correct format: " + mStartDateString);
}
}
}
@@ -1022,7 +1019,7 @@
* @jmx:managed-attribute
*
* @param pNumberOfCalls Initial Number of scheduled calls. If -1 then the number
- * is unlimted.
+ * is infinite
*
* @throws InvalidParameterException If the given value is less or equal than 0
*/
@@ -1155,32 +1152,18 @@
protected void startService()
throws Exception
{
- // Create Timer MBean if need be
-
- try
+ mTimerObjectName = new ObjectName(mTimerName);
+ if (!getServer().isRegistered(mTimerObjectName))
{
- mTimer = new ObjectName(mTimerName);
+ getServer().createMBean(Timer.class.getName(), mTimerObjectName);
}
- catch (MalformedObjectNameException mone)
+ mTimer = (TimerMBean)MBeanServerInvocationHandler.newProxyInstance(getServer(),
+ mTimerObjectName, TimerMBean.class, true);
+ mTimerEmitter = (NotificationEmitter)mTimer;
+ if (!mTimer.isActive())
{
- mTimer = new ObjectName(DEFAULT_TIMER_NAME);
+ mTimer.start();
}
-
- if (!getServer().isRegistered(mTimer))
- {
- getServer().createMBean("javax.management.timer.Timer", mTimer);
- }
- if (!((Boolean) getServer().getAttribute(mTimer, "Active")).booleanValue())
- {
- // Now start the Timer
- getServer().invoke(
- mTimer,
- "start",
- new Object[]{},
- new String[]{}
- );
- }
-
if (mStartOnStart)
{
log.debug("Start Scheduler on start up time");
@@ -1190,8 +1173,7 @@
protected void stopService()
{
- // Stop the schedule right now !!
- stopSchedule(true);
+ stopSchedule();
}
private static boolean isSchedulable(Class c)
@@ -1214,71 +1196,79 @@
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
// -------------------------------------------------------------------------
- public class Listener
- implements NotificationListener
+ /**
+ * Calls {@link Schedulable#perform} on a plain Java Object.
+ */
+ public class PojoScheduler extends BaseListener
{
- private final Logger log = Logger.getLogger(Listener.class);
- private Schedulable mDelegate;
- public Listener(Schedulable pDelegate)
+ protected void invoke(Notification notification)
{
- mDelegate = pDelegate;
- }
-
- public void handleNotification(Notification notification,Object handback)
- {
- log.debug("Listener.handleNotification(), notification: " + notification);
ClassLoader currentTCL = TCLActions.getContextClassLoader();
try
{
- ClassLoader loader = TCLActions.getClassLoader(mDelegate.getClass());
+ ClassLoader loader = TCLActions.getClassLoader(mSchedulable.getClass());
TCLActions.setContextClassLoader(loader);
- // If schedule is started invoke the schedule method on the Schedulable instance
- log.debug("Scheduler is started: " + isStarted());
Date lTimeStamp = new Date(notification.getTimeStamp());
- if (isStarted())
- {
- if (getRemainingRepetitions() > 0 || getRemainingRepetitions() < 0)
- {
- if (mRemainingRepetitions > 0)
- {
- mRemainingRepetitions--;
- }
- mDelegate.perform(
- lTimeStamp,
- getRemainingRepetitions()
- );
- log.debug("Remaining Repititions: " + getRemainingRepetitions() +
- ", wait for next call to stop: " + mWaitForNextCallToStop);
- if (getRemainingRepetitions() == 0 || mWaitForNextCallToStop)
- {
- stopSchedule(true);
- }
- }
- }
- else
- {
- // Schedule is stopped therefore remove the Schedule
- getServer().invoke(
- mTimer,
- "removeNotification",
- new Object[]{
- new Integer(mActualSchedule)
- },
- new String[]{
- Integer.class.getName()
- }
- );
- mActualSchedule = -1;
- }
+ mSchedulable.perform(lTimeStamp, getRemainingRepetitions());
}
catch (Exception e)
{
- log.error("Handling a Scheduler call failed", e);
+ log.error("Scheduler.perform call failed", e);
}
finally
{
@@ -1287,182 +1277,54 @@
}
}
- public class MBeanListener
- implements NotificationListener
- {
- private final Logger log = Logger.getLogger(Listener.class);
-
- private ObjectName mDelegate;
-
- public MBeanListener(ObjectName pDelegate)
- {
- mDelegate = pDelegate;
- }
-
- public void handleNotification(
- Notification notification,
- Object handback
- )
- {
- log.debug("MBeanListener.handleNotification(), notification: " + notification);
- try
- {
- // If schedule is started invoke the schedule method on the Schedulable instance
- log.debug("Scheduler is started: " + isStarted());
- Date lTimeStamp = new Date(notification.getTimeStamp());
- if (isStarted())
- {
- if (getRemainingRepetitions() > 0 || getRemainingRepetitions() < 0)
- {
- if (mRemainingRepetitions > 0)
- {
- mRemainingRepetitions--;
- }
- 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] = lTimeStamp;
- break;
- case REPETITIONS:
- lArguments[i] = new Long(mRemainingRepetitions);
- break;
- case SCHEDULER_NAME:
- lArguments[i] = getServiceName();
- break;
- default:
- lArguments[i] = null;
- }
- }
- log.debug("MBean Arguments are: " + java.util.Arrays.asList(lArguments));
- log.debug("MBean Arguments Types are: " + java.util.Arrays.asList(mSchedulableMBeanArgumentTypes));
- try
- {
- getServer().invoke(
- mDelegate,
- mSchedulableMBeanMethodName,
- lArguments,
- mSchedulableMBeanArgumentTypes
- );
- }
- catch (javax.management.JMRuntimeException jmre)
- {
- log.error("Invoke of the Schedulable MBean failed", jmre);
- }
- catch (javax.management.JMException jme)
- {
- log.error("Invoke of the Schedulable MBean failed", jme);
- }
- log.debug("Remaining Repititions: " + getRemainingRepetitions() +
- ", wait for next call to stop: " + mWaitForNextCallToStop);
- if (getRemainingRepetitions() == 0 || mWaitForNextCallToStop)
- {
- stopSchedule(true);
- }
- }
- }
- else
- {
- // Schedule is stopped therefore remove the Schedule
- getServer().invoke(
- mTimer,
- "removeNotification",
- new Object[]{
- new Integer(mActualSchedule)
- },
- new String[]{
- Integer.class.getName()
- }
- );
- mActualSchedule = -1;
- }
- }
- catch (Exception e)
- {
- log.error("Handling a Scheduler call failed", e);
- }
- }
- }
-
/**
- * Filter to ensure that each Scheduler only gets notified when it is supposed to.
- */
- private static class NotificationFilter implements javax.management.NotificationFilter
+ * Invokes an operation on an MBean.
+ */
+ public class MBeanListener extends BaseListener
{
-
- /** Class logger. */
- private static final Logger log = Logger.getLogger(NotificationFilter.class);
-
- private Integer mId;
-
- /**
- * Create a Filter.
- * @param id the Scheduler id
- */
- public NotificationFilter(Integer id)
+ protected void invoke(Notification notification)
{
- mId = id;
+ 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);
+ }
}
-
- /**
- * Determine if the notification should be sent to this Scheduler
- */
- public boolean isNotificationEnabled(Notification notification)
- {
- if (notification instanceof TimerNotification)
- {
- TimerNotification lTimerNotification = (TimerNotification) notification;
- if (log.isTraceEnabled())
- log.trace("Scheduler.NotificationFilter.isNotificationEnabled(), Id: " + mId +
- ", notification: " + notification +
- ", notification Id: " + lTimerNotification.getNotificationID() +
- ", timestamp: " + lTimerNotification.getTimeStamp() +
- ", message: " + lTimerNotification.getMessage()
- );
- return lTimerNotification.getNotificationID().equals(mId);
- }
- return false;
- }
}
- /**
- * A test class for a Schedulable Class
- **/
- public static class SchedulableExample
- implements Schedulable
- {
-
- /** Class logger. */
- private static final Logger log = Logger.getLogger(Scheduler.SchedulableExample.class);
-
- private String mName;
- private int mValue;
-
- public SchedulableExample(
- String pName,
- int pValue
- )
- {
- mName = pName;
- mValue = pValue;
- }
-
- /**
- * Just log the call
- **/
- public void perform(
- Date pTimeOfCall,
- long pRemainingRepetitions
- )
- {
- log.info("Schedulable Examples is called at: " + pTimeOfCall +
- ", remaining repetitions: " + pRemainingRepetitions +
- ", test, name: " + mName + ", value: " + mValue);
- }
- }
}
Modified: trunk/varia/src/main/org/jboss/varia/scheduler/SchedulerMBean.java
===================================================================
--- trunk/varia/src/main/org/jboss/varia/scheduler/SchedulerMBean.java 2006-11-06 19:24:43 UTC (rev 58157)
+++ trunk/varia/src/main/org/jboss/varia/scheduler/SchedulerMBean.java 2006-11-06 19:25:06 UTC (rev 58158)
@@ -164,6 +164,11 @@
void stopSchedule(boolean doItNow);
/**
+ * Stops the schedule immediately.
+ */
+ void stopSchedule();
+
+ /**
* Stops the server right now and starts it right now.
*/
void restartSchedule();
Modified: trunk/varia/src/main/org/jboss/varia/scheduler/example/SchedulableMBeanExample.java
===================================================================
--- trunk/varia/src/main/org/jboss/varia/scheduler/example/SchedulableMBeanExample.java 2006-11-06 19:24:43 UTC (rev 58157)
+++ trunk/varia/src/main/org/jboss/varia/scheduler/example/SchedulableMBeanExample.java 2006-11-06 19:25:06 UTC (rev 58158)
@@ -38,9 +38,8 @@
import org.jboss.varia.scheduler.Schedulable;
/**
+ * A sample SchedulableMBean that records when an event is received.
*
- * A sample SchedulableMBean
- *
* @jmx:mbean extends="org.jboss.system.ServiceMBean"
*
* @author <a href="mailto:andreas at jboss.org">Andreas Schaefer</a>
@@ -52,39 +51,99 @@
extends ServiceMBeanSupport
implements SchedulableMBeanExampleMBean
{
- /**
- * Default (no-args) Constructor
- **/
- public SchedulableMBeanExample()
- {
- }
+
+ private Notification notification;
+ private Date date;
+ private long repetitions;
+ private ObjectName name;
+ private String test;
+ private int hitCount;
// -------------------------------------------------------------------------
// SchedulableExampleMBean Methods
// -------------------------------------------------------------------------
/**
+ * Called by ScheduleManager.
* @jmx:managed-operation
*/
- public void hit( Notification lNotification, Date lDate, long lRepetitions, ObjectName lName, String lTest ) {
- log.info( "got hit"
- + ", notification: " + lNotification
- + ", date: " + lDate
- + ", remaining repetitions: " + lRepetitions
- + ", scheduler name: " + lName
- + ", test string: " + lTest
- );
+ public void hit(Notification notification, Date date, long repetitions, ObjectName name, String test)
+ {
+ log.info("got hit");
+ this.notification = notification;
+ this.date = date;
+ this.repetitions = repetitions;
+ this.name = name;
+ this.test = test;
hitCount++;
+ log.info(this.toString());
}
/**
- * @jmx:managed-operation
+ * Returns the number of hits.
+ * @jmx:managed-attribute
*/
public int getHitCount()
{
return hitCount;
}
- private int hitCount = 0;
+ /**
+ * Returns the last hit date.
+ * @jmx:managed-attribute
+ */
+ public Date getHitDate()
+ {
+ return date;
+ }
+ /**
+ * Returns the last hit notification.
+ * @jmx:managed-attribute
+ */
+ public Notification getHitNotification()
+ {
+ return notification;
+ }
+
+ /**
+ * Returns the last hit date.
+ * @jmx:managed-attribute
+ */
+ public long getRemainingRepetitions()
+ {
+ return repetitions;
+ }
+
+ /**
+ * Returns the object name.
+ * @jmx:managed-attribute
+ */
+ public ObjectName getSchedulerName()
+ {
+ return name;
+ }
+
+ /**
+ * Returns the test string.
+ * @jmx:managed-attribute
+ */
+ public String getTestString()
+ {
+ return test;
+ }
+
+ /**
+ * Returns a debug string.
+ */
+ public String toString() {
+ return super.toString()
+ + " name=" + getName()
+ + " hitCount=" + hitCount
+ + " notification=" + notification
+ + " date=" + date
+ + " repetitions=" + repetitions
+ + " name=" + name
+ + " test string=" + test;
+ }
}
Modified: trunk/varia/src/main/org/jboss/varia/scheduler/example/SchedulableMBeanExampleMBean.java
===================================================================
--- trunk/varia/src/main/org/jboss/varia/scheduler/example/SchedulableMBeanExampleMBean.java 2006-11-06 19:24:43 UTC (rev 58157)
+++ trunk/varia/src/main/org/jboss/varia/scheduler/example/SchedulableMBeanExampleMBean.java 2006-11-06 19:25:06 UTC (rev 58158)
@@ -26,8 +26,13 @@
*/
public interface SchedulableMBeanExampleMBean extends org.jboss.system.ServiceMBean {
- void hit(javax.management.Notification lNotification,java.util.Date lDate,long lRepetitions,javax.management.ObjectName lName,java.lang.String lTest) ;
+ void hit(javax.management.Notification lNotification, java.util.Date lDate, long lRepetitions, javax.management.ObjectName name, String test) ;
int getHitCount() ;
+ javax.management.Notification getHitNotification() ;
+ java.util.Date getHitDate() ;
+ long getRemainingRepetitions() ;
+ String getTestString() ;
+ javax.management.ObjectName getSchedulerName() ;
}
More information about the jboss-cvs-commits
mailing list