/* * 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 87717 2009-04-23 09:30:56Z galder.zamarreno@jboss.com $ 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.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: 87717 $ * @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 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 createTimer(Date expiration, Serializable info) 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 Collection 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 timerServiceMap = Collections.synchronizedMap(new HashMap()); // 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> it = timerServiceMap.entrySet().iterator(); while (it.hasNext()) { Map.Entry 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> it = timerServiceMap.entrySet().iterator(); while (it.hasNext()) { Map.Entry 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()); // get the first tick time long TimeBugFirstTick = handle.getFirstTime().getTime(); //get the duration long TimeBugPeriode = handle.getPeriode(); // assign fastracking if we are tracking a short timer boolean fasktrack = false; if (TimeBugPeriode < 60000L) { fasktrack = true; } long [] Timerparams = scheduletracker(TimeBugFirstTick,TimeBugPeriode); timerService.createTimer(Timerparams[0],Timerparams[1],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> it = timerServiceMap.entrySet().iterator(); while (it.hasNext()) { Map.Entry 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(); } private long[] scheduletracker (long tracker_initial,long tracker_duration) { long [] Timeparams = new long [2]; long schedule = 0L; int TimeIntegrals = 0; long TimeDuration =0L; // create initial schedule schedule = tracker_initial+tracker_duration; // the main part while (schedule < System.currentTimeMillis()+5000L) { // Getting the time difference TimeDuration = System.currentTimeMillis() - schedule ; //Getting the number of lost expirations TimeIntegrals = (int) (TimeDuration/tracker_duration); //just add once if there is no lost expiration if ((TimeDuration%tracker_duration)<=tracker_duration) { schedule = schedule+tracker_duration; } else { //if there are lost expirations add that time to the schedule long offset = tracker_duration* (long) (TimeIntegrals); schedule = schedule+offset; System.out.println(schedule); } } // now the schedule is greater than sys time& we have 5 seconds to set that Timeparams [0] = schedule-System.currentTimeMillis(); Timeparams [1] = tracker_duration; // returning the values return Timeparams; } }