/*
* 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.ejb.txtimer;
// $Id: EJBTimerServiceImpl.java 102915 2010-03-24 18:08:38Z jaikiran $
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Date;
import java.io.Serializable;
import javax.ejb.TimerService;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.ScheduleExpression;
import javax.ejb.EJBException;
import javax.management.ObjectName;
import javax.transaction.TransactionManager;
import org.jboss.ejb.Container;
import org.jboss.ejb.ContainerMBean;
import org.jboss.logging.Logger;
import org.jboss.mx.util.MBeanProxyExt;
import org.jboss.system.ServiceMBeanSupport;
import org.jboss.tm.TransactionManagerFactory;
import org.jboss.tm.TransactionManagerLocator;
/**
* A service that implements this interface provides an Tx aware EJBTimerService.
*
* @author Thomas.Diesler@jboss.org
* @author Dimitris.Andreadis@jboss.org
* @version $Revision: 102915 $
* @since 07-Apr-2004
*/
public class EJBTimerServiceImpl extends ServiceMBeanSupport
implements EJBTimerServiceImplMBean
{
// Logging support
private static Logger log = Logger.getLogger(EJBTimerServiceImpl.class);
/**
* Used for objects that don't implement javax.ejb.TimedObject but still call getTimerService()
* According to the CTS, it's allowed (jbcts-381).
*/
public static TimerService FOR_NON_TIMED_OBJECT = new TimerService()
{
public Timer createTimer(long duration, Serializable info) throws IllegalArgumentException,
IllegalStateException,
EJBException
{
throw new IllegalStateException("The object does not implement javax.ejb.TimedObject interface!");
}
public Timer createSingleActionTimer(long duration, TimerConfig timerConfig) throws IllegalArgumentException,
IllegalStateException,
EJBException
{
throw new IllegalStateException("The object does not implement javax.ejb.TimedObject interface!");
}
public Timer createTimer(long initialDuration, long intervalDuration, Serializable info)
throws IllegalArgumentException, IllegalStateException, EJBException
{
throw new IllegalStateException("The object does not implement javax.ejb.TimedObject interface!");
}
public Timer createIntervalTimer(long initialDuration, long intervalDuration, TimerConfig timerConfig)
throws IllegalArgumentException, IllegalStateException, EJBException
{
throw new IllegalStateException("The object does not implement javax.ejb.TimedObject interface!");
}
public Timer createTimer(Date expiration, Serializable info) throws IllegalArgumentException,
IllegalStateException,
EJBException
{
throw new IllegalStateException("The object does not implement javax.ejb.TimedObject interface!");
}
public Timer createSingleActionTimer(Date expiration, TimerConfig timerConfig) throws IllegalArgumentException,
IllegalStateException,
EJBException
{
throw new IllegalStateException("The object does not implement javax.ejb.TimedObject interface!");
}
public Timer createTimer(Date initialExpiration, long intervalDuration, Serializable info)
throws IllegalArgumentException, IllegalStateException, EJBException
{
throw new IllegalStateException("The object does not implement javax.ejb.TimedObject interface!");
}
public Timer createIntervalTimer(Date initialExpiration, long intervalDuration, TimerConfig timerConfig) throws IllegalArgumentException,
IllegalStateException,
EJBException
{
throw new IllegalStateException("The object does not implement javax.ejb.TimedObject interface!");
}
public Timer createCalendarTimer(ScheduleExpression schedule) throws IllegalArgumentException,
IllegalStateException,
EJBException
{
throw new IllegalStateException("The object does not implement javax.ejb.TimedObject interface!");
}
public Timer createCalendarTimer(ScheduleExpression schedule, TimerConfig timerConfig) throws IllegalArgumentException,
IllegalStateException,
EJBException
{
throw new IllegalStateException("The object does not implement javax.ejb.TimedObject interface!");
}
public Collection<Timer> getTimers() throws IllegalStateException, EJBException
{
return Collections.EMPTY_LIST;
}
};
// Attributes
// The object name of the retry policy
private ObjectName retryPolicyName;
// The object name of the persistence policy
private ObjectName persistencePolicyName;
// The TimerIdGenerator class name
private String timerIdGeneratorClassName;
// The TimedObjectInvoker class name
private String timedObjectInvokerClassName;
// The TransactionManagerFactory
private TransactionManagerFactory transactionManagerFactory;
// Plug-ins
// The tx manager plug-in
private TransactionManager transactionManager;
// The retry policy plug-in
private RetryPolicy retryPolicy;
// The persistence policy plug-in
private PersistencePolicy persistencePolicy;
// The timerId generator plug-in
private TimerIdGenerator timerIdGenerator;
// Maps the timedObjectId to TimerServiceImpl objects
private Map<TimedObjectId, TimerServiceImpl> timerServiceMap = Collections.synchronizedMap(new HashMap<TimedObjectId, TimerServiceImpl>());
// Attributes ----------------------------------------------------
/**
* Get the object name of the retry policy.
*
* @jmx.managed-attribute
*/
public ObjectName getRetryPolicy()
{
return retryPolicyName;
}
/**
* Set the object name of the retry policy.
*
* @jmx.managed-attribute
*/
public void setRetryPolicy(ObjectName retryPolicyName)
{
this.retryPolicyName = retryPolicyName;
}
/**
* Get the object name of the persistence policy.
*
* @jmx.managed-attribute
*/
public ObjectName getPersistencePolicy()
{
return persistencePolicyName;
}
/**
* Set the object name of the persistence policy.
*
* @jmx.managed-attribute
*/
public void setPersistencePolicy(ObjectName persistencePolicyName)
{
this.persistencePolicyName = persistencePolicyName;
}
/**
* Get the TimerIdGenerator class name
*
* @jmx.managed-attribute
*/
public String getTimerIdGeneratorClassName()
{
return timerIdGeneratorClassName;
}
/**
* Get the TimerIdGenerator class name
*
* @jmx.managed-attribute
*/
public void setTimerIdGeneratorClassName(String timerIdGeneratorClassName)
{
this.timerIdGeneratorClassName = timerIdGeneratorClassName;
}
/**
* Get the TimedObjectInvoker class name
*
* @jmx.managed-attribute
*/
public String getTimedObjectInvokerClassName()
{
return timedObjectInvokerClassName;
}
/**
* Set the TimedObjectInvoker class name
*
* @jmx.managed-attribute
*/
public void setTimedObjectInvokerClassName(String timedObjectInvokerClassName)
{
this.timedObjectInvokerClassName = timedObjectInvokerClassName;
}
/**
* Set the TransactionManagerFactory
*/
public void setTransactionManagerFactory(TransactionManagerFactory factory)
{
this.transactionManagerFactory = factory;
}
// ServiceMBeanSupport Lifecycle ---------------------------------
protected void startService() throws Exception
{
// Setup plugins, fall back to safe defaults
// Get the TransactionManager from the factory, fall-back to the locator
if (transactionManagerFactory != null)
transactionManager = transactionManagerFactory.getTransactionManager();
else
transactionManager = TransactionManagerLocator.getInstance().locate();
// Get a proxy to the retry policy
try
{
retryPolicy = (RetryPolicy)MBeanProxyExt.create(RetryPolicy.class, getRetryPolicy(), server);
}
catch (Exception e)
{
log.error("Cannot obtain the implementation of a RetryPolicy", e);
}
// Get a proxy to the persistence policy
try
{
persistencePolicy = (PersistencePolicy)MBeanProxyExt.create(PersistencePolicy.class, persistencePolicyName, server);
}
catch (Exception e)
{
log.warn("Cannot obtain the implementation of a PersistencePolicy, using NoopPersistencePolicy: " + e.toString());
persistencePolicy = new NoopPersistencePolicy();
}
// Get the timerId generator
try
{
Class<?> timerIdGeneratorClass = getClass().getClassLoader().loadClass(timerIdGeneratorClassName);
timerIdGenerator = (TimerIdGenerator)timerIdGeneratorClass.newInstance();
}
catch (Exception e)
{
log.warn("Cannot obtain the implementation of a TimerIdGenerator, using BigIntegerTimerIdGenerator: " + e.toString());
timerIdGenerator = new BigIntegerTimerIdGenerator();
}
}
protected void stopService()
{
// Cleanup plugins
transactionManager = null;
retryPolicy = null;
persistencePolicy = null;
timerIdGenerator = null;
}
// EJBTimerService Operations ------------------------------------
/**
* Create a TimerService for a given TimedObjectId that lives in a JBoss Container.
* The TimedObjectInvoker is constructed from the invokerClassName.
*
* @param containerId The string identifier for a class of TimedObjects
* @param instancePk The rimary key for an instance of a TimedObject, may be null
* @param container The Container that is associated with the TimerService
* @return the TimerService
*/
public TimerService createTimerService(ObjectName containerId, Object instancePk, Container container)
{
TimedObjectInvoker invoker = null;
try
{
TimedObjectId timedObjectId = new TimedObjectId(containerId, instancePk);
Class<?> invokerClass = getClass().getClassLoader().loadClass(timedObjectInvokerClassName);
Constructor<?> constr = invokerClass.getConstructor(new Class[]{TimedObjectId.class, Container.class});
invoker = (TimedObjectInvoker)constr.newInstance(new Object[]{timedObjectId, container});
}
catch (Exception e)
{
log.error("Cannot create TimedObjectInvoker: " + timedObjectInvokerClassName, e);
return null;
}
return createTimerService(containerId, instancePk, invoker);
}
/**
* Create a TimerService for a given TimedObjectId that is invoked through the given invoker
*
* @param containerId The string identifier for a class of TimedObjects
* @param instancePk The rimary key for an instance of a TimedObject, may be null
* @param invoker The TimedObjectInvoker
* @return the TimerService
*/
public TimerService createTimerService(ObjectName containerId, Object instancePk, TimedObjectInvoker invoker)
{
TimedObjectId timedObjectId = new TimedObjectId(containerId, instancePk);
TimerServiceImpl timerService = timerServiceMap.get(timedObjectId);
if (timerService == null)
{
timerService = new TimerServiceImpl(timedObjectId, invoker,
transactionManager, persistencePolicy, retryPolicy, timerIdGenerator);
log.debug("createTimerService: " + timerService);
timerServiceMap.put(timedObjectId, timerService);
}
return timerService;
}
/**
* Get the TimerService for a given TimedObjectId
*
* @param containerId The string identifier for a class of TimedObjects
* @param instancePk The rimary key for an instance of a TimedObject, may be null
* @return The TimerService, or null if it does not exist
*/
public TimerService getTimerService(ObjectName containerId, Object instancePk)
{
TimedObjectId timedObjectId = new TimedObjectId(containerId, instancePk);
return timerServiceMap.get(timedObjectId);
}
/**
* Remove the TimerService for a given containerId/pKey (TimedObjectId),
* along with any persisted timer information.
*
* This should be used for removing the TimerService and Timers
* associated with a particular entity bean, when it gets removed.
*
* @param containerId The string identifier for a class of TimedObjects
* @param pKey The primary key for an instance of a TimedObject, may be null
*/
public void removeTimerService(ObjectName containerId, Object instancePk)
{
TimedObjectId timedObjectId = new TimedObjectId(containerId, instancePk);
// remove a single timer service
if (timedObjectId.getInstancePk() != null)
{
TimerServiceImpl timerService = (TimerServiceImpl)getTimerService(containerId, instancePk);
if (timerService != null)
{
log.debug("removeTimerService: " + timerService);
// don't keep persistent state about the timer
// this is really an entity->remove()
timerService.shutdown(false);
timerServiceMap.remove(timedObjectId);
}
}
else
{
// assume we don't want to keep timer state when the container
// gets undeployed, this is the legacy behaviour
removeTimerService(containerId, false);
}
}
/**
* Remove the TimerService for a given containerId.
*
* This should be used to remove the timer service and timers for
* any type of container (session, entity, message) at the time of
* undeployment.
*
* @param containerId The string identifier for a class of TimedObjects
* @param keepState Flag indicating whether timer persistent state should be kept or removed
*/
public void removeTimerService(ObjectName containerId, boolean keepState) throws IllegalStateException
{
// remove all timers with the given containerId
synchronized(timerServiceMap)
{
Iterator<Map.Entry<TimedObjectId, TimerServiceImpl>> it = timerServiceMap.entrySet().iterator();
while (it.hasNext())
{
Map.Entry<TimedObjectId, TimerServiceImpl> entry = it.next();
TimedObjectId key = entry.getKey();
TimerServiceImpl timerService = entry.getValue();
if (containerId.equals(key.getContainerId()))
{
log.debug("removeTimerService: " + timerService);
timerService.shutdown(keepState);
it.remove();
}
}
}
}
/**
* Remove the TimerService for a given containerId/pKey (TimedObjectId).
*
* @param containerId The string identifier for a class of TimedObjects
* @param pKey The primary key for an instance of a TimedObject, may be null
* @param keepState Flag indicating whether timer persistent state should be kept or removed
*/
public void removeTimerService(ObjectName containerId, Object instancePk, boolean keepState) throws IllegalStateException
{
// remove a single timer service
TimedObjectId timedObjectId = new TimedObjectId(containerId, instancePk);
if (timedObjectId.getInstancePk() != null)
{
TimerServiceImpl timerService = (TimerServiceImpl)getTimerService(containerId, instancePk);
if (timerService != null)
{
log.debug("removeTimerService: " + timerService);
timerService.shutdown(false);
timerServiceMap.remove(timedObjectId);
}
}
// remove all timers with the given containerId
else
{
synchronized(timerServiceMap)
{
Iterator<Map.Entry<TimedObjectId, TimerServiceImpl>> it = timerServiceMap.entrySet().iterator();
while (it.hasNext())
{
Map.Entry<TimedObjectId, TimerServiceImpl> entry = it.next();
TimedObjectId key = (TimedObjectId) entry.getKey();
TimerServiceImpl timerService = (TimerServiceImpl) entry.getValue();
if (containerId.equals(key.getContainerId()))
{
log.debug("removeTimerService: " + timerService);
timerService.shutdown(keepState);
it.remove();
}
}
}
}
}
/**
* Restore the persisted timers for a given ejb container
*
* @param containerId The ejb container id
* @param loader The classloader to use for loading the timers
*/
public void restoreTimers(ObjectName containerId, ClassLoader loader) throws IllegalStateException
{
assert persistencePolicy != null : "persistencePolicy is not set";
// find out all the persisted handles, for the specified container
List handles = persistencePolicy.listTimerHandles(containerId, loader);
if (handles.isEmpty() == false)
{
// first remove the persisted handles from the db
for (Iterator i = handles.iterator(); i.hasNext(); )
{
TimerHandleImpl handle = (TimerHandleImpl)i.next();
persistencePolicy.deleteTimer(handle.getTimerId(), handle.getTimedObjectId());
}
// make a second pass to re-create the timers; use the container
// itself to retrieve the correct TimerService/ for each handle,
// then use the standard ejb timer API to recreate the timer
for (Iterator i = handles.iterator(); i.hasNext(); )
{
TimerHandleImpl handle = (TimerHandleImpl)i.next();
try
{
TimedObjectId targetId = handle.getTimedObjectId();
ContainerMBean container = (ContainerMBean)MBeanProxyExt.create(ContainerMBean.class, containerId, server);
TimerService timerService = container.getTimerService(targetId.getInstancePk());
timerService.createTimer(handle.getFirstTime(), handle.getPeriode(), handle.getInfo());
}
catch (Exception e)
{
log.warn("Unable to restore timer record: " + handle, e);
}
}
}
}
// EJBTimerServiceImplMbean operations ---------------------------
/**
* List the timers registered with all TimerService objects
*
* @jmx.managed-operation
*/
public String listTimers()
{
StringBuffer retBuffer = new StringBuffer();
synchronized(timerServiceMap)
{
Iterator<Map.Entry<TimedObjectId, TimerServiceImpl>> it = timerServiceMap.entrySet().iterator();
while (it.hasNext())
{
Map.Entry<TimedObjectId, TimerServiceImpl> entry = it.next();
TimedObjectId timedObjectId = (TimedObjectId) entry.getKey();
retBuffer.append(timedObjectId + "\n");
TimerServiceImpl timerService = (TimerServiceImpl) entry.getValue();
Collection col = timerService.getAllTimers();
for (Iterator iterator = col.iterator(); iterator.hasNext();)
{
TimerImpl timer = (TimerImpl) iterator.next();
TimerHandleImpl handle = new TimerHandleImpl(timer);
retBuffer.append(" handle: " + handle + "\n");
retBuffer.append(" " + timer + "\n");
}
}
}
return retBuffer.toString();
}
}