[jboss-cvs] JBossAS SVN: r107116 - in branches/JBPAPP_5_1: server/src/main/org/jboss/ejb/txtimer and 4 other directories.
jboss-cvs-commits at lists.jboss.org
jboss-cvs-commits at lists.jboss.org
Tue Jul 27 11:42:01 EDT 2010
Author: jaikiran
Date: 2010-07-27 11:41:59 -0400 (Tue, 27 Jul 2010)
New Revision: 107116
Added:
branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/PersistencePolicyExt.java
branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerRestoringTimerService.java
branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/UpdateableDatabasePersistencePlugin.java
branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/
branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/SimpleTimer.java
branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/TimeoutTracker.java
branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/TimerSLSB.java
branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/unit/
branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/unit/TimerServiceRestoreTestCase.java
Modified:
branches/JBPAPP_5_1/ejb3/src/main/org/jboss/as/ejb3/timerservice/TimerServiceFacade.java
branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/DatabasePersistencePolicy.java
branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/DatabasePersistencePolicyMBean.java
branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/EJBTimerServiceImpl.java
branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/GeneralPurposeDatabasePersistencePlugin.java
branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/OracleDatabasePersistencePlugin.java
branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerHandleImpl.java
branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerImpl.java
branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerServiceImpl.java
branches/JBPAPP_5_1/testsuite/imports/sections/ejb3.xml
Log:
JBPAPP-4681 Fix timer expiry on restarts
Modified: branches/JBPAPP_5_1/ejb3/src/main/org/jboss/as/ejb3/timerservice/TimerServiceFacade.java
===================================================================
--- branches/JBPAPP_5_1/ejb3/src/main/org/jboss/as/ejb3/timerservice/TimerServiceFacade.java 2010-07-27 14:19:08 UTC (rev 107115)
+++ branches/JBPAPP_5_1/ejb3/src/main/org/jboss/as/ejb3/timerservice/TimerServiceFacade.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -34,12 +34,11 @@
import javax.ejb.TimerService;
import javax.management.ObjectName;
+import org.jboss.ejb.AllowedOperationsAssociation;
+import org.jboss.ejb.txtimer.PersistentIdTimerService;
+import org.jboss.ejb.txtimer.TimerRestoringTimerService;
import org.jboss.logging.Logger;
-import org.jboss.ejb.txtimer.PersistentIdTimerService;
-
-import org.jboss.ejb.AllowedOperationsAssociation;
-
/**
* Holds the association with the container, without exposing it.
*
@@ -48,7 +47,7 @@
*
* @version $Revision$
*/
-public class TimerServiceFacade implements PersistentIdTimerService
+public class TimerServiceFacade implements TimerRestoringTimerService
{
private static Logger log = Logger.getLogger(TimerServiceFacade.class);
@@ -86,6 +85,7 @@
}
// JBPAPP-3926
+ @Override
public Timer createTimer(Date initialExpiration, long intervalDuration, Serializable info, String timerId) throws IllegalArgumentException, IllegalStateException, EJBException
{
if (delegate instanceof PersistentIdTimerService)
@@ -99,6 +99,28 @@
return delegate.createTimer(initialExpiration, intervalDuration, info);
}
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Timer restoreTimer(Date initialExpiration, long intervalDuration, Date nextExpiry, Serializable info,
+ String timerId) throws IllegalArgumentException, IllegalStateException, EJBException
+ {
+ if (delegate instanceof TimerRestoringTimerService)
+ {
+ TimerRestoringTimerService persistentTimerService = (TimerRestoringTimerService) delegate;
+ // restore the timer
+ return persistentTimerService.restoreTimer(initialExpiration, intervalDuration, nextExpiry, info, timerId);
+ }
+ else
+ {
+ log.warn("Unable to restore timer, since the delegate timerservice " + delegate.getClass() + " isn't of type "
+ + TimerRestoringTimerService.class + " - will create the timer afresh");
+ // we can't "restore" the timer state, so let's just recreate the timer afresh, using the initial expiry date and the repeat interval
+ return delegate.createTimer(initialExpiration, intervalDuration, info);
+ }
+ }
public Timer createTimer(long duration, Serializable info) throws IllegalArgumentException, IllegalStateException, EJBException
{
Modified: branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/DatabasePersistencePolicy.java
===================================================================
--- branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/DatabasePersistencePolicy.java 2010-07-27 14:19:08 UTC (rev 107115)
+++ branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/DatabasePersistencePolicy.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -238,6 +238,27 @@
log.warn("Unable to clear timers", e);
}
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void updateNextTimeout(String timerId, TimedObjectId timedObjectId, Date nextTimeout)
+ {
+ try
+ {
+ if (this.dbpPlugin instanceof UpdateableDatabasePersistencePlugin)
+ {
+ ((UpdateableDatabasePersistencePlugin) this.dbpPlugin).updateNextTimeout(timerId, timedObjectId, nextTimeout);
+ }
+ }
+ catch (SQLException sqle)
+ {
+ log.error("Could not update the next timeout date for timer: " + timerId + " timedObjectId: " + timedObjectId,
+ sqle);
+ }
+
+ }
/** Re-read the current persistent timers list, clear the db of timers,
* and restore the timers.
Modified: branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/DatabasePersistencePolicyMBean.java
===================================================================
--- branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/DatabasePersistencePolicyMBean.java 2010-07-27 14:19:08 UTC (rev 107115)
+++ branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/DatabasePersistencePolicyMBean.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -36,7 +36,7 @@
* @version $Revision$
* @since 09-Sep-2004
*/
-public interface DatabasePersistencePolicyMBean extends ServiceMBean, PersistencePolicy
+public interface DatabasePersistencePolicyMBean extends ServiceMBean, PersistencePolicyExt
{
/** The default object name */
ObjectName OBJECT_NAME = ObjectNameFactory.create("jboss.ejb:service=EJBTimerService,persistencePolicy=database");
Modified: branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/EJBTimerServiceImpl.java
===================================================================
--- branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/EJBTimerServiceImpl.java 2010-07-27 14:19:08 UTC (rev 107115)
+++ branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/EJBTimerServiceImpl.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -23,19 +23,20 @@
// $Id$
+import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Collections;
+import java.util.Date;
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.EJBException;
+import javax.ejb.Timer;
import javax.ejb.TimerService;
-import javax.ejb.Timer;
-import javax.ejb.EJBException;
+import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.transaction.TransactionManager;
@@ -241,7 +242,16 @@
// Get a proxy to the persistence policy
try
{
- persistencePolicy = (PersistencePolicy)MBeanProxyExt.create(PersistencePolicy.class, persistencePolicyName, server);
+ // JBPAPP-4681
+ if (this.isPersistencePolicyExt(persistencePolicyName))
+ {
+ persistencePolicy = (PersistencePolicy)MBeanProxyExt.create(PersistencePolicyExt.class, persistencePolicyName, server);
+ }
+ else
+ {
+ persistencePolicy = (PersistencePolicy)MBeanProxyExt.create(PersistencePolicy.class, persistencePolicyName, server);
+ }
+
}
catch (Exception e)
{
@@ -249,6 +259,9 @@
persistencePolicy = new NoopPersistencePolicy();
}
+
+
+
// Get the timerId generator
try
{
@@ -478,8 +491,14 @@
ContainerMBean container = (ContainerMBean)MBeanProxyExt.create(ContainerMBean.class, containerId, server);
TimerService timerService = container.getTimerService(targetId.getInstancePk());
+ // JBPAPP-4681
+ if (timerService instanceof TimerRestoringTimerService)
+ {
+ TimerRestoringTimerService timerRestoringTimerService = (TimerRestoringTimerService) timerService;
+ timerRestoringTimerService.restoreTimer(handle.getFirstTime(), handle.getPeriode(), handle.getNextTimeout(), handle.getInfo(), handle.getTimerId());
+ }
// Fix for JBPAPP-3926
- if (timerService instanceof PersistentIdTimerService)
+ else if (timerService instanceof PersistentIdTimerService)
{
PersistentIdTimerService persistentIdTimerService = (PersistentIdTimerService) timerService;
persistentIdTimerService.createTimer(handle.getFirstTime(), handle.getPeriode(), handle.getInfo(), handle.getTimerId());
@@ -530,4 +549,46 @@
}
return retBuffer.toString();
}
+
+ /**
+ * Returns true if the {@link Class} which backs the MBean instance represented by the
+ * passed <code>persistencePolicyObjectName</code> is of type {@link PersistencePolicyExt}.
+ * Else returns false
+ *
+ * @param persistencePolicyObjectName The {@link ObjectName} of the persistence policy MBean
+ * @return
+ */
+ // JBPAPP-4681
+ private boolean isPersistencePolicyExt(ObjectName persistencePolicyObjectName)
+ {
+ if (persistencePolicyObjectName == null)
+ {
+ return false;
+ }
+ try
+ {
+ // get the MBean ObjectInstance from the MBean server
+ ObjectInstance persistencePolicyMBeanInstance = this.server.getObjectInstance(persistencePolicyObjectName);
+ // get hold of the class name
+ String persistencePolicyInstanceClassName = persistencePolicyMBeanInstance.getClassName();
+
+ // load the persistence policy class
+ ClassLoader tccl = Thread.currentThread().getContextClassLoader();
+ Class<?> persistencePolicyClass = tccl.loadClass(persistencePolicyInstanceClassName);
+ // check whether it's of type PersistencePolicyExt
+ if (PersistencePolicyExt.class.isAssignableFrom(persistencePolicyClass))
+ {
+ return true;
+ }
+ }
+ catch (Exception e)
+ {
+ // ignore
+ log.debug("Could not determine whether: " + persistencePolicyObjectName + " implements "
+ + PersistencePolicyExt.class + " - will assume that it doesn't");
+ return false;
+ }
+
+ return false;
+ }
}
Modified: branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/GeneralPurposeDatabasePersistencePlugin.java
===================================================================
--- branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/GeneralPurposeDatabasePersistencePlugin.java 2010-07-27 14:19:08 UTC (rev 107115)
+++ branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/GeneralPurposeDatabasePersistencePlugin.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -63,7 +63,7 @@
* @version $Revision$
* @since 23-Sep-2004
*/
-public class GeneralPurposeDatabasePersistencePlugin implements DatabasePersistencePluginExt
+public class GeneralPurposeDatabasePersistencePlugin implements UpdateableDatabasePersistencePlugin
{
/** logging support */
private static Logger log = Logger.getLogger(GeneralPurposeDatabasePersistencePlugin.class);
@@ -152,6 +152,7 @@
" " + getColumnTimerID() + " varchar(80) not null," +
" " + getColumnTargetID() + " varchar(250) not null," +
" " + getColumnInitialDate() + " " + dateType + " not null," +
+ " " + getColumnNextDate() + " " + dateType + "," +
" " + getColumnTimerInterval() + " " + longType + "," +
" " + getColumnInstancePK() + " " + objectType + "," +
" " + getColumnInfo() + " " + objectType + ", ");
@@ -174,6 +175,27 @@
st = con.createStatement();
st.executeUpdate(createTableDDL.toString());
}
+ else if (this.isNextDateColumnPresent() == false) // the timer table exists but the next timeout date column is absent, so create the column
+ {
+ // JBPAPP-4681 https://jira.jboss.org/browse/JBPAPP-4681 introduces a new column
+ // in the table. Here, we make sure to create the column in already existing DBs
+
+ con = ds.getConnection();
+
+ String dateType = typeMapping.getTypeMappingMetaData(Timestamp.class).getSqlType();
+
+ // The alter table DDL
+ StringBuffer alterTableDDL = new StringBuffer("alter table " + getTableName() + " ADD " +
+ " " + getColumnNextDate() + " " + dateType);
+
+ log.debug("Adding new column " + getColumnNextDate()
+ + " to table " + getTableName() + " - executing DDL: "
+ + alterTableDDL);
+
+ st = con.createStatement();
+ st.executeUpdate(alterTableDDL.toString());
+
+ }
}
catch (SQLException e)
{
@@ -201,8 +223,8 @@
con = ds.getConnection();
String sql = "insert into " + getTableName() + " " +
- "(" + getColumnTimerID() + "," + getColumnTargetID() + "," + getColumnInitialDate() + "," + getColumnTimerInterval() + "," + getColumnInstancePK() + "," + getColumnInfo() + ") " +
- "values (?,?,?,?,?,?)";
+ "(" + getColumnTimerID() + "," + getColumnTargetID() + "," + getColumnInitialDate() + "," + getColumnTimerInterval() + "," + getColumnInstancePK() + "," + getColumnInfo() + "," + getColumnNextDate() + ") " +
+ "values (?,?,?,?,?,?,?)";
st = con.prepareStatement(sql);
st.setString(1, timerId);
@@ -229,7 +251,10 @@
{
st.setBytes(6, bytes);
}
-
+ // set the next timeout date, which when the timer is being created, is equal to the initial
+ // date of expiry.
+ st.setTimestamp(7, new Timestamp(initialExpiration.getTime()));
+
int rows = st.executeUpdate();
if (rows != 1)
log.error("Unable to insert timer for: " + timedObjectId);
@@ -267,6 +292,7 @@
if (containerId == null || containerId.equals(targetId.getContainerId()))
{
Date initialDate = rs.getTimestamp(getColumnInitialDate());
+ Date nextTimeout = rs.getTimestamp(getColumnNextDate());
long interval = rs.getLong(getColumnTimerInterval());
Serializable pKey = (Serializable)deserialize(rs.getBytes(getColumnInstancePK()));
Serializable info = null;
@@ -283,7 +309,7 @@
}
// is this really needed? targetId encapsulates pKey as well!
targetId = new TimedObjectId(targetId.getContainerId(), pKey);
- TimerHandleImpl handle = new TimerHandleImpl(timerId, targetId, initialDate, interval, info);
+ TimerHandleImpl handle = new TimerHandleImpl(timerId, targetId, initialDate, nextTimeout, interval, info);
list.add(handle);
}
}
@@ -355,7 +381,79 @@
JDBCUtil.safeClose(con);
}
}
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException If either <code>timerId</code> or <code>timedObjectId</code> or both are null
+ */
+ @Override
+ public void updateNextTimeout(String timerId, TimedObjectId timedObjectId, Date nextTimeout) throws SQLException
+ {
+ if (timerId == null || timedObjectId == null)
+ {
+ throw new NullPointerException();
+ }
+
+ log.debug("Updating next timeout date to " + nextTimeout + " for timer: " + timerId + " ,timedObjectId: "
+ + timedObjectId.toString());
+
+ Connection con = null;
+ PreparedStatement st = null;
+ try
+ {
+ con = ds.getConnection();
+
+ // update the next timeout date column value
+ String sql = "update " + getTableName() +
+ " " + " SET " + getColumnNextDate() + "=?" +
+ " where " + getColumnTimerID() + "=? and " + getColumnTargetID() + "=?";
+
+ st = con.prepareStatement(sql);
+
+ if (nextTimeout == null)
+ {
+ JDBCTypeMappingMetaData typeMapping = (JDBCTypeMappingMetaData)server.getAttribute(metaDataName, "TypeMappingMetaData");
+ if (typeMapping == null)
+ {
+ throw new IllegalStateException("Cannot obtain type mapping from: " + metaDataName);
+ }
+ int dateType = typeMapping.getTypeMappingMetaData(Timestamp.class).getJdbcType();
+ // set the next timeout as null
+ st.setNull(1, dateType);
+ }
+ else
+ {
+ st.setTimestamp(1, new Timestamp(nextTimeout.getTime()));
+ }
+ st.setString(2, timerId);
+ st.setString(3, timedObjectId.toString());
+
+ // run the update sql
+ int rows = st.executeUpdate();
+
+ if (rows != 1)
+ {
+ log.debug("Unexpected update row count: " + rows + " for timer: " + timerId + " timedObjectId: "
+ + timedObjectId.toString());
+ }
+ }
+ catch (SQLException sqle)
+ {
+ throw sqle;
+ }
+ catch (Exception e)
+ {
+ log.error("Could not update next timeout date for timerId: " + timerId + " timedObjectId: "
+ + timedObjectId.toString(), e);
+ }
+ finally
+ {
+ JDBCUtil.safeClose(st);
+ JDBCUtil.safeClose(con);
+ }
+ }
+
/** Get the timer table name */
public String getTableName()
{
@@ -380,6 +478,12 @@
return "INITIALDATE";
}
+ /** Returns the column name of the next timeout date */
+ public String getColumnNextDate()
+ {
+ return "NEXTDATE";
+ }
+
/** Get the timer interval column name */
public String getColumnTimerInterval()
{
@@ -463,5 +567,49 @@
return null;
}
}
+
+ /**
+ * Returns true if the timer table already contains the next timeout date column (represented
+ * by {@link #getColumnNextDate()}). Else returns false.
+ * @return
+ */
+ // JBPAPP-4681
+ private boolean isNextDateColumnPresent()
+ {
+ // Just fire a query on the timer table with the next timeout date column in the
+ // select clause. If the query fails, then the column in considered absent.
+ // I don't like this implementation, but this is the simplest. And since this method
+ // is only there to take care of already existing Timer table, this impl should be OK for now
+ Connection con = null;
+ PreparedStatement st = null;
+ try
+ {
+ con = ds.getConnection();
+
+ String sql = "select " + getColumnNextDate() + " from " + getTableName() + " where " + getColumnTimerID()
+ + "=?";
+
+ st = con.prepareStatement(sql);
+ st.setString(1, "");
+
+ st.executeQuery();
+ return true;
+
+ }
+ catch (SQLException sqle)
+ {
+ // consider any sqlexception as an indication of column absence.
+ // I don't like this, but this is the simplest way to figure out the presence of the
+ // column, instead of having to go via the DatabaseMetaData JDBC API.
+ // After all, this method comes into picture only when the timer table is already present
+ // (i.e. on existing deployments)
+ return false;
+ }
+ finally
+ {
+ JDBCUtil.safeClose(st);
+ JDBCUtil.safeClose(con);
+ }
+ }
}
Modified: branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/OracleDatabasePersistencePlugin.java
===================================================================
--- branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/OracleDatabasePersistencePlugin.java 2010-07-27 14:19:08 UTC (rev 107115)
+++ branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/OracleDatabasePersistencePlugin.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -75,8 +75,8 @@
String sql = "insert into " + getTableName() + " " +
"(" + getColumnTimerID() + "," + getColumnTargetID() +
"," + getColumnInitialDate() + "," + getColumnTimerInterval() +
- "," + getColumnInstancePK() + "," + getColumnInfo() + ") " +
- "values (?,?,?,?,?,?)";
+ "," + getColumnInstancePK() + "," + getColumnInfo() + "," + getColumnNextDate() + ") " +
+ "values (?,?,?,?,?,?,?)";
st = con.prepareStatement(sql);
st.setString(1, timerId);
@@ -105,6 +105,9 @@
{
st.setBytes(6, null);
}
+ // set the next timeout date, which when the timer is being created, is equal to the initial
+ // date of expiry.
+ st.setTimestamp(7, new Timestamp(initialExpiration.getTime()));
int rows = st.executeUpdate();
if (rows != 1)
@@ -145,6 +148,8 @@
if (containerId == null || containerId.equals(targetId.getContainerId()))
{
Date initialDate = rs.getTimestamp(getColumnInitialDate());
+ Date nextTimeout = rs.getTimestamp(getColumnNextDate());
+
long interval = rs.getLong(getColumnTimerInterval());
InputStream isPk = rs.getBinaryStream(getColumnInstancePK());
@@ -164,7 +169,7 @@
}
// is this really needed? targetId encapsulates pKey as well!
targetId = new TimedObjectId(targetId.getContainerId(), pKey);
- TimerHandleImpl handle = new TimerHandleImpl(timerId, targetId, initialDate, interval, info);
+ TimerHandleImpl handle = new TimerHandleImpl(timerId, targetId, initialDate, nextTimeout, interval, info);
list.add(handle);
}
Added: branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/PersistencePolicyExt.java
===================================================================
--- branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/PersistencePolicyExt.java (rev 0)
+++ branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/PersistencePolicyExt.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+import java.util.Date;
+
+/**
+ * An extension of {@link PersistencePolicy} that allows updating the next timeout
+ * date of an existing timer
+ *
+ * @author Jaikiran Pai
+ * @version $Revision: $
+ */
+public interface PersistencePolicyExt extends PersistencePolicy
+{
+
+ /**
+ * Updates the next timeout date of a timer, in the persistence store
+ *
+ * @param timerId The timer id
+ * @param timedObjectId The id of the timed object
+ */
+ void updateNextTimeout(String timerId, TimedObjectId timedObjectId, Date nextTimeout);
+}
Modified: branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerHandleImpl.java
===================================================================
--- branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerHandleImpl.java 2010-07-27 14:19:08 UTC (rev 107115)
+++ branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerHandleImpl.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -23,17 +23,18 @@
// $Id$
-import javax.ejb.EJBException;
-import javax.ejb.NoSuchObjectLocalException;
-import javax.ejb.Timer;
-import javax.ejb.TimerHandle;
-import javax.management.ObjectName;
import java.io.Serializable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringTokenizer;
+import javax.ejb.EJBException;
+import javax.ejb.NoSuchObjectLocalException;
+import javax.ejb.Timer;
+import javax.ejb.TimerHandle;
+import javax.management.ObjectName;
+
/**
* An implementation of the TimerHandle
*
@@ -52,6 +53,7 @@
private String timerId;
private TimedObjectId timedObjectId;
private Date firstTime;
+ private Date nextTimeout;
private long periode;
private Serializable info;
private int hashCode;
@@ -64,13 +66,16 @@
timerId = timer.getTimerId();
timedObjectId = timer.getTimedObjectId();
firstTime = timer.getFirstTime();
+ nextTimeout = new Date(timer.getNextExpire());
periode = timer.getPeriode();
info = timer.getInfoInternal();
}
/**
* Construct a handle from individual parameters
+ * @deprecated Use {@link #TimerHandleImpl(String, TimedObjectId, Date, Date, long, Serializable)}
*/
+ @Deprecated
TimerHandleImpl(String timerId, TimedObjectId timedObjectId, Date firstTime, long periode, Serializable info)
{
this.timerId = timerId;
@@ -79,6 +84,25 @@
this.periode = periode;
this.info = info;
}
+
+ /**
+ * Constructs an timer handle from the passed parameters
+ * @param timerId
+ * @param timedObjectId
+ * @param firstTime
+ * @param nextTimeout
+ * @param periode
+ * @param info
+ */
+ TimerHandleImpl(String timerId, TimedObjectId timedObjectId, Date firstTime, Date nextTimeout, long periode, Serializable info)
+ {
+ this.timerId = timerId;
+ this.timedObjectId = timedObjectId;
+ this.firstTime = firstTime;
+ this.nextTimeout = nextTimeout;
+ this.periode = periode;
+ this.info = info;
+ }
/**
* Construct a handle from external form
@@ -177,6 +201,11 @@
{
return info;
}
+
+ public Date getNextTimeout()
+ {
+ return this.nextTimeout;
+ }
/**
* Obtain a reference to the txtimer represented by this handle.
Modified: branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerImpl.java
===================================================================
--- branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerImpl.java 2010-07-27 14:19:08 UTC (rev 107115)
+++ branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerImpl.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -98,6 +98,12 @@
private int timerState;
private Timer utilTimer;
private int hashCode;
+
+ /**
+ * Flag which indicates if one or more timeouts were missed due to (for example) server
+ * being down
+ */
+ private boolean missedTimeout;
/**
* Schedules the txtimer for execution at the specified time with a specified periode.
@@ -115,15 +121,59 @@
void startTimer(Date firstTime, long periode)
{
- this.firstTime = firstTime;
- this.nextExpire = firstTime.getTime();
+ this.startTimer(firstTime, firstTime, periode);
+
+ }
+
+ /**
+ * Schedules any tasks for the timer, based on the passed <code>firstTimeout<code>,
+ * the <code>nextTimeout</code> and the <code>periode</code>
+ *
+ * @param firstTimeout {@link Date} on which the first timeout of the timer occurs/occurred
+ * @param nextTimeout The {@link Date} on which the next timeout of the timer occurs. Can be null, in which case
+ * the <code>firstTimeout</code> is considered to be the next timeout.
+ * @param periode The repeat interval of the timer
+ */
+ void startTimer(Date firstTimeout, Date nextTimeout, long periode)
+ {
+ this.firstTime = firstTimeout;
+ if (nextTimeout == null)
+ {
+ nextTimeout = firstTimeout;
+ }
+ this.nextExpire = nextTimeout.getTime();
this.periode = periode;
+
+ Date now = new Date();
+ // if the next timeout points to a time in the past, then
+ // it means that the server was either down or the timeout wasn't fired for
+ // some other reason. As per the EJB spec, we should fire the timeout
+ // for the *missed* timeout atleast once. Here we create a single action
+ // timeout which will fire immediately. At the same time we also (re)compute the
+ // actual next timeout *from now* and create a periodic timer task for the same
+ if (nextTimeout.before(now))
+ {
+ long next = nextTimeout.getTime();
+ long current = now.getTime();
+ // recompute the next timeout based on the current time
+ while (next < current)
+ {
+ next += periode;
+ }
+ this.nextExpire = next;
+ // persist the recomputed next timeout
+ this.persistNextTimeout();
+ // set the flag which indicates that this timer has missed one (or more) timeouts.
+ // This flag will later be used for firing a single action (backlog) timer
+ this.missedTimeout = true;
+ }
timerService.addTimer(this);
registerTimerWithTx();
// the timer will actually go ACTIVE on tx commit
startInTx();
+
}
public String getTimerId()
@@ -371,6 +421,13 @@
else
{
setTimerState(ACTIVE);
+ // if some timeouts were missed, then trigger a single action
+ // timer task for the backlog
+ if (this.missedTimeout)
+ {
+ this.triggerBacklogTimeoutNow();
+ }
+ // schedule the regular timeouts
scheduleTimeout();
}
}
@@ -386,12 +443,26 @@
private void scheduleTimeout()
{
if (periode > 0)
+ {
+ // schedule the periodic timer task
utilTimer.schedule(new TimerTaskImpl(this), new Date(nextExpire), periode);
+ }
else
- utilTimer.schedule(new TimerTaskImpl(this), new Date(nextExpire));
+ {
+ utilTimer.schedule(new TimerTaskImpl(this), new Date(nextExpire));
+ }
}
/**
+ * Schedules a timer task to fire once, immediately. Used for
+ * triggering a task for backlog timeouts (i.e. timeouts which were missed)
+ */
+ private void triggerBacklogTimeoutNow()
+ {
+ utilTimer.schedule(new TimerTaskImpl(this, true), new Date());
+ }
+
+ /**
* Throws NoSuchObjectLocalException if the txtimer was canceled or has expired
*/
private void assertTimedOut()
@@ -459,6 +530,13 @@
switch (timerState)
{
case STARTED_IN_TX:
+ // if some timeouts were missed, then trigger a single action
+ // timer task for the backlog
+ if (this.missedTimeout)
+ {
+ this.triggerBacklogTimeoutNow();
+ }
+ // schedule the regular timeouts
scheduleTimeout();
setTimerState(ACTIVE);
break;
@@ -525,10 +603,30 @@
{
private TimerImpl timer;
+ /**
+ * A backlog indicates that this {@link TimerTaskImpl} was fired
+ * for a timeout which occurred in the past (for example, the timeout occurred when the server
+ * was down and this timer task was fired to account for that)
+ */
+ private boolean backlog;
+
public TimerTaskImpl(TimerImpl timer)
{
this.timer = timer;
}
+
+ /**
+ *
+ * @param timer The timer instance
+ * @param backlog True if this {@link TimerTaskImpl} was fired for a timeout which occurred in the past
+ * (for example, the timeout occurred when the server
+ * was down and this timer task was fired to account for that)
+ */
+ public TimerTaskImpl(TimerImpl timer, boolean backlog)
+ {
+ this.timer = timer;
+ this.backlog = backlog;
+ }
/**
* The action to be performed by this txtimer task.
@@ -538,11 +636,15 @@
log.debug("run: " + timer);
// Set next scheduled execution attempt. This is used only
- // for reporting (getTimeRemaining()/getNextTimeout())
+ // for reporting (getTimeRemaining()/getNextTimeout()) and for persisting
// and not from the underlying jdk timer implementation.
- if (isActive() && periode > 0)
+ // If it's a backlog task, then do not change the next timeout, since
+ // a backlog task is "fire only once" task.
+ if (isActive() && periode > 0 && !this.backlog)
{
nextExpire += periode;
+
+ TimerImpl.this.persistNextTimeout();
}
// If a retry thread is in progress, we don't want to allow another
@@ -583,4 +685,16 @@
}
}
}
+
+ /**
+ * Persist the next timeout of the timer to the persistence store
+ */
+ private void persistNextTimeout()
+ {
+ PersistencePolicy persistencePolicy = this.timerService.getPersistencePolicy();
+ if (persistencePolicy != null && persistencePolicy instanceof PersistencePolicyExt)
+ {
+ ((PersistencePolicyExt) persistencePolicy).updateNextTimeout(this.timerId, this.timedObjectId, new Date(nextExpire));
+ }
+ }
}
Added: branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerRestoringTimerService.java
===================================================================
--- branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerRestoringTimerService.java (rev 0)
+++ branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerRestoringTimerService.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+import java.io.Serializable;
+import java.util.Date;
+
+import javax.ejb.EJBException;
+import javax.ejb.Timer;
+import javax.ejb.TimerService;
+
+/**
+ * {@link TimerService} implementation which is able to restore a persisted timer
+ *
+ * @author Jaikiran Pai
+ * @version $Revision: $
+ */
+public interface TimerRestoringTimerService extends PersistentIdTimerService
+{
+ /**
+ * Restores a timer from the state provided through the passed parameters.
+ * <p>
+ * This involves scheduling any appropriate task(s), for the appropriate next timeout.
+ * </p>
+ *
+ * @param initialExpiration The first expiry {@link Date} of the {@link Timer}, when it was created
+ * @param intervalDuration The repeat interval (in milli. seconds) for the timeouts
+ * @param nextExpiry The next expiry {@link Date} of the timer. This need not be same as the <code>intialExpiration</code>.
+ * The next expiry date of a periodic timer keeps changing whereas the intial (first) expiry date remains constant.
+ * @param info The info to be passed to the timeout method
+ * @param timerId The id of the timer being restored
+ * @return Returns the restored timer
+ * @throws IllegalArgumentException
+ * @throws IllegalStateException
+ * @throws EJBException
+ */
+ public Timer restoreTimer(Date initialExpiration, long intervalDuration, Date nextExpiry, Serializable info, String timerId)
+ throws IllegalArgumentException, IllegalStateException, EJBException;
+
+}
Modified: branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerServiceImpl.java
===================================================================
--- branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerServiceImpl.java 2010-07-27 14:19:08 UTC (rev 107115)
+++ branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/TimerServiceImpl.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -34,7 +34,6 @@
import javax.ejb.EJBException;
import javax.ejb.Timer;
import javax.ejb.TimerHandle;
-import javax.ejb.TimerService;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
@@ -53,7 +52,7 @@
* @version $Revision$
* @since 07-Apr-2004
*/
-public class TimerServiceImpl implements PersistentIdTimerService
+public class TimerServiceImpl implements TimerRestoringTimerService
{
// logging support
private static Logger log = Logger.getLogger(TimerServiceImpl.class);
@@ -322,6 +321,42 @@
return activeTimers;
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Timer restoreTimer(Date initialExpiration, long intervalDuration, Date nextExpiry, Serializable info,
+ String timerId) throws IllegalArgumentException, IllegalStateException, EJBException
+ {
+ if (initialExpiration == null)
+ throw new IllegalArgumentException("initial expiration is null");
+ if (intervalDuration < 0)
+ throw new IllegalArgumentException("interval duration is negative");
+ if (timerId == null)
+ throw new IllegalArgumentException("timerId is null");
+
+ try
+ {
+ TimerImpl timer = new TimerImpl(this, timerId, timedObjectId, timedObjectInvoker, info);
+ // store the timer info
+ persistencePolicy.insertTimer(timerId, timedObjectId, initialExpiration, intervalDuration, info);
+ // additionally, persist the next timeout too (can't store this next timeout date
+ // through the insert API, because it doesn't allow a way to pass the next timeout date). So
+ // this additional update
+ if (persistencePolicy instanceof PersistencePolicyExt)
+ {
+ ((PersistencePolicyExt) persistencePolicy).updateNextTimeout(timerId, timedObjectId, nextExpiry);
+ }
+ // now start the timer
+ timer.startTimer(initialExpiration, nextExpiry, intervalDuration);
+ return timer;
+ }
+ catch (Exception e)
+ {
+ throw new EJBException("Failed to restore timer", e);
+ }
+ }
+
// Package protected ---------------------------------------------
/**
@@ -374,4 +409,9 @@
log.error("Retry timeout failed for timer: " + txtimer, e);
}
}
+
+ PersistencePolicy getPersistencePolicy()
+ {
+ return this.persistencePolicy;
+ }
}
Added: branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/UpdateableDatabasePersistencePlugin.java
===================================================================
--- branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/UpdateableDatabasePersistencePlugin.java (rev 0)
+++ branches/JBPAPP_5_1/server/src/main/org/jboss/ejb/txtimer/UpdateableDatabasePersistencePlugin.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -0,0 +1,45 @@
+/*
+ * 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;
+
+import java.sql.SQLException;
+import java.util.Date;
+
+/**
+ * Extension to {@link DatabasePersistencePluginExt} which allows for updating the next timeout
+ * date of an existing timer
+ *
+ * @author Jaikiran Pai
+ * @version $Revision: $
+ */
+public interface UpdateableDatabasePersistencePlugin extends DatabasePersistencePluginExt
+{
+
+ /**
+ * Updates the next timeout date of a timer, in the database
+ *
+ * @param timerId The timer id
+ * @param timedObjectId The id of the timed object
+ */
+ void updateNextTimeout(String timerId, TimedObjectId timedObjectId, Date nextTimeout) throws SQLException;
+
+}
Modified: branches/JBPAPP_5_1/testsuite/imports/sections/ejb3.xml
===================================================================
--- branches/JBPAPP_5_1/testsuite/imports/sections/ejb3.xml 2010-07-27 14:19:08 UTC (rev 107115)
+++ branches/JBPAPP_5_1/testsuite/imports/sections/ejb3.xml 2010-07-27 15:41:59 UTC (rev 107116)
@@ -189,13 +189,24 @@
<fileset dir="${build.resources}/ejb3/webservice" includes="**"/>
</jar>
</target>
+
+ <target name="jbpapp4681" depends="compile">
+ <mkdir dir="${build.lib}" />
+
+ <jar destfile="${build.lib}/jbpapp4681.jar">
+ <fileset dir="${build.classes}">
+ <include name="org/jboss/test/ejb3/jbpapp4681/**" />
+ </fileset>
+ </jar>
+ </target>
<target name="_jars-ejb3" depends="ejb3-servlet,jbas6161,jbas6239,ejbthree1597,jbas5713,
ejb3iiop,
ejb3-webservice,
jbpapp2260,
jbpapp2473,
- jbpapp3026
+ jbpapp3026,
+ jbpapp4681
">
<mkdir dir="${build.lib}" />
Added: branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/SimpleTimer.java
===================================================================
--- branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/SimpleTimer.java (rev 0)
+++ branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/SimpleTimer.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -0,0 +1,43 @@
+/*
+ * 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.test.ejb3.jbpapp4681;
+
+import java.util.Date;
+
+/**
+ * SimpleTimer
+ *
+ * @author Jaikiran Pai
+ * @version $Revision: $
+ */
+public interface SimpleTimer
+{
+
+ void createTimer(Date initialExpiry, long interval, String testName);
+
+ void createTimer(Date initialExpiry, String testName);
+
+ void cancelAllTimers();
+
+ int getTimeoutCount(String testName);
+
+}
Added: branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/TimeoutTracker.java
===================================================================
--- branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/TimeoutTracker.java (rev 0)
+++ branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/TimeoutTracker.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -0,0 +1,80 @@
+/*
+ * 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.test.ejb3.jbpapp4681;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.jboss.logging.Logger;
+
+/**
+ * TimeoutTracker
+ *
+ * @author Jaikiran Pai
+ * @version $Revision: $
+ */
+public class TimeoutTracker
+{
+
+ private static TimeoutTracker instance;
+
+ private static Logger logger = Logger.getLogger(TimeoutTracker.class);
+
+ private Map<String, Integer> timeoutCount;
+
+ public synchronized static TimeoutTracker getInstance()
+ {
+ if (instance == null)
+ {
+ instance = new TimeoutTracker();
+ }
+ return instance;
+ }
+
+ private TimeoutTracker()
+ {
+ this.timeoutCount = new HashMap<String, Integer>();
+ }
+
+ public void trackTimeout(String name)
+ {
+ logger.info("Tracking timeout for: " + name);
+ Integer count = this.timeoutCount.get(name);
+ if (count == null)
+ {
+ count = new Integer(0);
+ }
+ count++;
+ logger.info("Number of timeouts for: " + name + " = " + count);
+ this.timeoutCount.put(name, count);
+ }
+
+ public int getTimeoutCount(String name)
+ {
+ Integer count = this.timeoutCount.get(name);
+ if (count == null)
+ {
+ return 0;
+ }
+ return count;
+ }
+}
Added: branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/TimerSLSB.java
===================================================================
--- branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/TimerSLSB.java (rev 0)
+++ branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/TimerSLSB.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -0,0 +1,103 @@
+/*
+ * 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.test.ejb3.jbpapp4681;
+
+import java.util.Collection;
+import java.util.Date;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.Resource;
+import javax.ejb.Remote;
+import javax.ejb.Stateless;
+import javax.ejb.Timeout;
+import javax.ejb.Timer;
+import javax.ejb.TimerService;
+
+import org.jboss.ejb3.annotation.RemoteBinding;
+import org.jboss.logging.Logger;
+
+/**
+ * TimerSLSB
+ *
+ * @author Jaikiran Pai
+ * @version $Revision: $
+ */
+ at Stateless
+ at Remote
+ at RemoteBinding (jndiBinding = TimerSLSB.JNDI_NAME)
+public class TimerSLSB implements SimpleTimer
+{
+
+ public static final String JNDI_NAME = "JBPAPP-4681-TimerSLSB";
+
+ @Resource
+ private TimerService timerService;
+
+ private TimeoutTracker timeoutTracker;
+
+ private static Logger logger = Logger.getLogger(TimerSLSB.class);
+
+ @PostConstruct
+ public void onConstruct()
+ {
+ this.timeoutTracker = TimeoutTracker.getInstance();
+ }
+
+ @Override
+ public void createTimer(Date initialExpiry, long interval, String testName)
+ {
+ this.timerService.createTimer(initialExpiry, interval, testName);
+
+ }
+
+ @Override
+ public void createTimer(Date initialExpiry, String testName)
+ {
+ this.timerService.createTimer(initialExpiry, testName);
+
+ }
+
+ @Timeout
+ public void onTimeout(Timer timer)
+ {
+ logger.info("Timeout invoked at : " + new Date() + " for timer " + timer);
+ this.timeoutTracker.trackTimeout(timer.getInfo().toString());
+ }
+
+ @Override
+ public void cancelAllTimers()
+ {
+ Collection<Timer> timers = this.timerService.getTimers();
+ for (Timer timer : timers)
+ {
+ timer.cancel();
+ }
+
+ }
+
+ @Override
+ public int getTimeoutCount(String name)
+ {
+ return this.timeoutTracker.getTimeoutCount(name);
+ }
+
+}
Added: branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/unit/TimerServiceRestoreTestCase.java
===================================================================
--- branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/unit/TimerServiceRestoreTestCase.java (rev 0)
+++ branches/JBPAPP_5_1/testsuite/src/main/org/jboss/test/ejb3/jbpapp4681/unit/TimerServiceRestoreTestCase.java 2010-07-27 15:41:59 UTC (rev 107116)
@@ -0,0 +1,153 @@
+/*
+ * 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.test.ejb3.jbpapp4681.unit;
+
+import java.util.Date;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+
+import org.jboss.logging.Logger;
+import org.jboss.test.JBossTestCase;
+import org.jboss.test.ejb3.jbpapp4681.SimpleTimer;
+import org.jboss.test.ejb3.jbpapp4681.TimerSLSB;
+
+/**
+ * Tests that timers that are restored after a redeployment of the applications,
+ * fire at the right time.
+ *
+ * @see JBPAPP-4681 https://jira.jboss.org/browse/JBPAPP-4681
+ *
+ * @author Jaikiran Pai
+ * @version $Revision: $
+ */
+public class TimerServiceRestoreTestCase extends JBossTestCase
+{
+
+ private static Logger logger = Logger.getLogger(TimerServiceRestoreTestCase.class);
+
+ private static final String DEPLOYMENT_NAME = "jbpapp4681.jar";
+
+ public TimerServiceRestoreTestCase(String name)
+ {
+ super(name);
+ }
+
+ /**
+ *
+ * @return
+ * @throws Exception
+ */
+ public static Test suite() throws Exception
+ {
+ return getDeploySetup(TimerServiceRestoreTestCase.class, DEPLOYMENT_NAME);
+ }
+
+ /**
+ * Tests that a periodic timer is restored and a timeout is fired at the right time
+ * on redeploy of the application
+ *
+ * @throws Exception
+ */
+ public void testTimerRestoreForPeriodicTimer() throws Exception
+ {
+ // make sure the deployment was deployed successfully
+ serverFound();
+
+ SimpleTimer bean = (SimpleTimer) this.getInitialContext().lookup(TimerSLSB.JNDI_NAME);
+ // cancel all existing timers
+ bean.cancelAllTimers();
+
+ Date now = new Date();
+ long TEN_SECONDS = 10000;
+ String testName = "PeriodicTimer";
+ bean.createTimer(now, TEN_SECONDS, testName);
+
+ // wait for the intial timeout to occur
+ logger.info("Sleeping for 1 seconds for the timeout to happen");
+ Thread.sleep(1000);
+
+ int timeoutCount = bean.getTimeoutCount(testName);
+ Assert.assertEquals("Unexpected initial timeout count", 1, timeoutCount);
+
+ // redeploy the app
+ this.redeploy(DEPLOYMENT_NAME);
+
+ // the time from the initial timeout and the redeployment shouldn't be 10 seconds.
+ // so the timeout count returned from the bean, immediately after redeployment, must
+ // be 0.
+ bean = (SimpleTimer) this.getInitialContext().lookup(TimerSLSB.JNDI_NAME);
+ int timeoutCountImmediatelyAfterRedeploy = bean.getTimeoutCount(testName);
+ Assert.assertEquals("Unexpected timeout count immediately after redeploy", 0, timeoutCountImmediatelyAfterRedeploy);
+
+ // now wait for a few more seconds for the next timeout to occur (the first one after redeploy)
+ logger.info("Sleeping for 10 seconds (after redeploy) for the timeout to happen");
+ Thread.sleep(TEN_SECONDS);
+
+ int finalTimeoutCount = bean.getTimeoutCount(testName);
+ Assert.assertEquals("Unexpected final timeout count after redeploy", 1, finalTimeoutCount);
+ }
+
+ /**
+ * Tests that a single action timer is restored and a timeout is fired at the right time
+ * on redeploy of the application
+ *
+ * @throws Exception
+ */
+ public void testTimerRestoreForSingleActionTimer() throws Exception
+ {
+ // make sure the deployment was deployed successfully
+ serverFound();
+
+ SimpleTimer bean = (SimpleTimer) this.getInitialContext().lookup(TimerSLSB.JNDI_NAME);
+
+ // cancel all existing timers
+ bean.cancelAllTimers();
+
+ long TEN_SECONDS = 10000;
+ Date tenSecondsFromNow = new Date(System.currentTimeMillis() + TEN_SECONDS);
+ String testName = "SingleActionTimer";
+ bean.createTimer(tenSecondsFromNow, testName);
+
+ // no timeout should occur, before 10 seconds from now
+ int timeoutCount = bean.getTimeoutCount(testName);
+ Assert.assertEquals("Unexpected initial timeout count", 0, timeoutCount);
+
+ // redeploy the app
+ this.redeploy(DEPLOYMENT_NAME);
+
+ // the time from the initial timeout and the redeployment shouldn't be 10 seconds.
+ // so the timeout count returned from the bean, immediately after redeployment, must
+ // be 0.
+ bean = (SimpleTimer) this.getInitialContext().lookup(TimerSLSB.JNDI_NAME);
+ int timeoutCountImmediatelyAfterRedeploy = bean.getTimeoutCount(testName);
+ Assert.assertEquals("Unexpected timeout count immediately after redeploy", 0, timeoutCountImmediatelyAfterRedeploy);
+
+ // now wait for a few more seconds for the next timeout to occur (the first one after redeploy)
+ logger.info("Sleeping for 10 seconds (after redeploy) for the timeout to happen");
+ Thread.sleep(TEN_SECONDS);
+
+ int finalTimeoutCount = bean.getTimeoutCount(testName);
+ Assert.assertEquals("Unexpected final timeout count after redeploy", 1, finalTimeoutCount);
+ }
+
+}
More information about the jboss-cvs-commits
mailing list