package org.jboss.varia.scheduler;
import java.util.ArrayList;
import java.util.Date;
import java.util.Hashtable;
import java.util.Iterator;
import javax.management.InstanceNotFoundException;
import javax.management.JMException;
import javax.management.MBeanException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.timer.TimerNotification;
import org.jboss.logging.Logger;
import org.jboss.system.ServiceMBeanSupport;
public class ScheduleManager
extends ServiceMBeanSupport
implements ScheduleManagerMBean
{
public static String DEFAULT_TIMER_NAME = "jboss:service=Timer";
private static int sCounter = 0;
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;
private static final int ID = 5;
private static final int NEXT_DATE = 6;
private String mTimerName = DEFAULT_TIMER_NAME;
private ObjectName mTimer;
private boolean mScheduleIsStarted = false;
private boolean mWaitForNextCallToStop = false;
private boolean mStartOnStart = false;
private boolean mIsPaused = false;
private ArrayList mProviders = new ArrayList();
private Hashtable mSchedules = new Hashtable();
public ScheduleManager()
{
}
public void startSchedules()
{
if (!isStarted())
{
Iterator i = mSchedules.entrySet().iterator();
while (i.hasNext())
{
ScheduleInstance lInstance = (ScheduleInstance) i.next();
try
{
lInstance.start();
}
catch (JMException jme)
{
log.error("Could not start a Schedule", jme);
}
}
}
mScheduleIsStarted = true;
}
public void stopSchedules(boolean pDoItNow)
{
if (isStarted())
{
Iterator i = mSchedules.entrySet().iterator();
while (i.hasNext())
{
ScheduleInstance lInstance = (ScheduleInstance) i.next();
try
{
lInstance.stop();
}
catch (JMException jme)
{
log.error("Could not stop a Schedule", jme);
}
}
}
mScheduleIsStarted = false;
}
public void restartSchedule()
{
stopSchedules(true);
startSchedules();
}
public void registerProvider(String pProviderObjectName)
{
if (pProviderObjectName == null)
{
throw new RuntimeException("Provider must not be null");
}
int lIndex = mProviders.indexOf(pProviderObjectName);
if (lIndex >= 0)
{
return;
}
mProviders.add(pProviderObjectName);
try
{
ObjectName lProviderName = new ObjectName(pProviderObjectName);
server.invoke(
lProviderName,
"startProviding",
new Object[]{},
new String[]{}
);
}
catch (JMException jme)
{
log.error("Could not call startProviding() on the provider", jme);
}
}
public void unregisterProvider(String pProviderObjectName)
{
int lIndex = mProviders.indexOf(pProviderObjectName);
if (lIndex < 0)
{
return;
}
try
{
ObjectName lProviderName = new ObjectName(pProviderObjectName);
server.invoke(
lProviderName,
"stopProviding",
new Object[]{},
new String[]{}
);
}
catch (JMException jme)
{
log.error("Could not call stopProviding() on the provider", jme);
}
finally
{
mProviders.remove(pProviderObjectName);
}
}
public int addSchedule(
ObjectName pProvider,
ObjectName pTarget,
String pMethodName,
String[] pMethodSignature,
Date pStartDate,
long pPeriod,
int pRepetitions
)
{
ScheduleInstance lInstance = new ScheduleInstance(
pProvider,
pTarget,
pMethodName,
pMethodSignature,
pStartDate,
pRepetitions,
pPeriod
);
if (isStarted())
{
try
{
lInstance.start();
}
catch (JMException jme)
{
log.error("Could not start the Schedule", jme);
}
}
int lID = lInstance.getID();
mSchedules.put(new Integer(lID), lInstance);
return lID;
}
public void removeSchedule(int pIdentification)
{
ScheduleInstance lInstance = (ScheduleInstance) mSchedules.get(new Integer(pIdentification));
try
{
lInstance.stop();
}
catch (JMException jme)
{
log.error("Could not stop a Schedule", jme);
}
mSchedules.remove(new Integer(pIdentification));
}
public String getSchedules()
{
Iterator i = mSchedules.entrySet().iterator();
StringBuffer lReturn = new StringBuffer();
boolean lFirst = true;
while (i.hasNext())
{
ScheduleInstance lInstance = (ScheduleInstance) i.next();
if (lFirst)
{
lReturn.append(lInstance.mIdentification + "");
lFirst = false;
}
else
{
lReturn.append("," + lInstance.mIdentification);
}
}
return lReturn.toString();
}
public boolean isPaused()
{
return mIsPaused;
}
public void setPaused(boolean pIsPaused)
{
mIsPaused = pIsPaused;
}
public boolean isStarted()
{
return getState() == STARTED;
}
public boolean isStartAtStartup()
{
return mStartOnStart;
}
public void setStartAtStartup(boolean pStartAtStartup)
{
mStartOnStart = pStartAtStartup;
}
public String getTimerName()
{
return mTimerName;
}
public void setTimerName(String pTimerName)
{
mTimerName = pTimerName;
}
public ObjectName getObjectName(
MBeanServer pServer,
ObjectName pName
)
throws MalformedObjectNameException
{
return pName == null ? OBJECT_NAME : pName;
}
protected void destroyService()
{
Iterator i = mSchedules.entrySet().iterator();
while (i.hasNext())
{
ScheduleInstance lInstance = (ScheduleInstance) i.next();
unregisterProvider(lInstance.mProvider.toString());
}
}
protected void startService()
throws Exception
{
try
{
mTimer = new ObjectName(mTimerName);
}
catch (MalformedObjectNameException mone)
{
mTimer = new ObjectName(DEFAULT_TIMER_NAME);
}
if (!getServer().isRegistered(mTimer))
{
getServer().createMBean("javax.management.timer.Timer", mTimer);
}
if (!((Boolean) getServer().getAttribute(mTimer, "Active")).booleanValue())
{
getServer().invoke(
mTimer,
"start",
new Object[]{},
new String[]{}
);
}
log.debug("Start Schedules when Service is (re) started");
startSchedules();
}
protected void stopService()
{
stopSchedules(true);
}
public class MBeanListener
implements NotificationListener
{
private final Logger log = Logger.getLogger(MBeanListener.class);
private ScheduleInstance mSchedule;
public MBeanListener(ScheduleInstance pSchedule)
{
mSchedule = pSchedule;
}
public void handleNotification(
Notification pNotification,
Object pHandback
)
{
log.debug("MBeanListener.handleNotification(), notification: " + pNotification);
try
{
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);
}
}
}
private static class NotificationFilter implements javax.management.NotificationFilter
{
private Integer mId;
public NotificationFilter(Integer pId)
{
mId = pId;
}
public boolean isNotificationEnabled(Notification pNotification)
{
if (pNotification instanceof TimerNotification)
{
TimerNotification lTimerNotification = (TimerNotification) pNotification;
return lTimerNotification.getNotificationID().equals(mId);
}
return false;
}
}
private class ScheduleInstance
{
private final Logger log = Logger.getLogger(ScheduleInstance.class);
private int mIdentification;
private MBeanListener mListener;
public int mNotificationID;
public ObjectName mProvider;
public ObjectName mTarget;
public int mInitialRepetitions;
public int mRemainingRepetitions = 0;
public Date mStartDate;
public long mPeriod;
public String mMethodName;
public int[] mSchedulableMBeanArguments;
public String[] mSchedulableMBeanArgumentTypes;
public ScheduleInstance(
ObjectName pProvider,
ObjectName pTarget,
String pMethodName,
String[] pMethodArguments,
Date pStartDate,
int pRepetitions,
long pPeriod
)
{
mProvider = pProvider;
mTarget = pTarget;
mInitialRepetitions = pRepetitions;
mStartDate = pStartDate;
mPeriod = pPeriod;
mMethodName = pMethodName;
mSchedulableMBeanArguments = new int[pMethodArguments.length];
mSchedulableMBeanArgumentTypes = new String[pMethodArguments.length];
for (int i = 0; i < pMethodArguments.length; i++)
{
String lToken = pMethodArguments[i];
if (lToken.equals("ID"))
{
mSchedulableMBeanArguments[i] = ID;
mSchedulableMBeanArgumentTypes[i] = Integer.class.getName();
}
else if (lToken.equals("NOTIFICATION"))
{
mSchedulableMBeanArguments[i] = NOTIFICATION;
mSchedulableMBeanArgumentTypes[i] = Notification.class.getName();
}
else if (lToken.equals("NEXT_DATE"))
{
mSchedulableMBeanArguments[i] = NEXT_DATE;
mSchedulableMBeanArgumentTypes[i] = Date.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;
mSchedulableMBeanArgumentTypes[i] = lToken;
}
}
mIdentification = (sCounter++);
}
public void start()
throws JMException
{
Date lStartDate = null;
if (mStartDate.getTime() < new Date().getTime() && mPeriod > 0)
{
long lNow = new Date().getTime() + 100;
int lSkipRepeats = (int) ((lNow - mStartDate.getTime()) / mPeriod) + 1;
log.debug("Old start date: " + mStartDate + ", now: " + new Date(lNow) + ", Skip repeats: " + lSkipRepeats);
if (mInitialRepetitions > 0)
{
if (lSkipRepeats >= mInitialRepetitions)
{
log.warn("No repetitions left because start date is in the past and could " +
"not be reached by Initial Repetitions * Schedule Period");
return;
}
else
{
mRemainingRepetitions = mInitialRepetitions - lSkipRepeats;
}
}
else
{
if (mInitialRepetitions == 0)
{
mRemainingRepetitions = 0;
}
else
{
mRemainingRepetitions = -1;
}
}
lStartDate = new Date(mStartDate.getTime() + (lSkipRepeats * mPeriod));
}
else
{
lStartDate = mStartDate;
mRemainingRepetitions = mInitialRepetitions;
}
mNotificationID = ((Integer) getServer().invoke(
mTimer,
"addNotification",
new Object[]{
"Schedule",
"Scheduler Notification",
new Integer(getID()), lStartDate,
new Long(mPeriod),
mRemainingRepetitions < 0 ?
new Long(0) :
new Long(mRemainingRepetitions)
},
new String[]{
String.class.getName(),
String.class.getName(),
Object.class.getName(),
Date.class.getName(),
Long.TYPE.getName(),
Long.TYPE.getName()
}
)).intValue();
mListener = new MBeanListener(this);
getServer().addNotificationListener(
mTimer,
mListener,
new NotificationFilter(new Integer(mNotificationID)),
null
);
log.debug("start(), add Notification to Timer with ID: " + mNotificationID);
}
public void stop()
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 (!(e instanceof InstanceNotFoundException))
{
throw mbe;
}
}
}
public int getID()
{
return mIdentification;
}
}
}