You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by da...@apache.org on 2007/03/07 08:26:45 UTC

svn commit: r515484 [1/2] - in /incubator/openejb/trunk/openejb3: container/openejb-core/src/main/java/org/apache/openejb/ container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ container/openejb-core/src/main/java/org/apache/openej...

Author: dain
Date: Tue Mar  6 23:26:43 2007
New Revision: 515484

URL: http://svn.apache.org/viewvc?view=rev&rev=515484
Log:
Ported basic timer support from 2

Added:
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/DatabaseTimerStore.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerService.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerServiceImpl.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/MemoryTimerStore.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/TimerData.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/TimerHandleImpl.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/TimerImpl.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/TimerServiceImpl.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/TimerStore.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/TimerStoreException.java
    incubator/openejb/trunk/openejb3/itests/openejb-itests-beans/src/main/java/org/apache/openejb/test/beans/TimerSync.java
    incubator/openejb/trunk/openejb3/itests/openejb-itests-beans/src/main/java/org/apache/openejb/test/beans/TimerSyncBean.java
Modified:
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanBuilder.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/BaseContext.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/cmp/CmpContainer.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityContainer.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbHomeProxyHandler.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbObjectProxyHandler.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbInstanceFactory.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/SimplePassivater.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessContainer.java
    incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessInstanceManager.java
    incubator/openejb/trunk/openejb3/itests/openejb-itests-beans/src/main/java/org/apache/openejb/test/object/OperationsPolicy.java
    incubator/openejb/trunk/openejb3/itests/openejb-itests-beans/src/main/java/org/apache/openejb/test/stateless/BasicStatelessBean.java
    incubator/openejb/trunk/openejb3/itests/openejb-itests-beans/src/main/java/org/apache/openejb/test/stateless/BasicStatelessLocalObject.java
    incubator/openejb/trunk/openejb3/itests/openejb-itests-beans/src/main/java/org/apache/openejb/test/stateless/BasicStatelessObject.java
    incubator/openejb/trunk/openejb3/itests/openejb-itests-beans/src/main/java/org/apache/openejb/test/stateless/BasicStatelessPojoBean.java
    incubator/openejb/trunk/openejb3/itests/openejb-itests-beans/src/main/resources/META-INF/ejb-jar.xml
    incubator/openejb/trunk/openejb3/itests/openejb-itests-client/src/main/java/org/apache/openejb/test/stateless/BMTStatelessAllowedOperationsTests.java
    incubator/openejb/trunk/openejb3/itests/openejb-itests-client/src/main/java/org/apache/openejb/test/stateless/StatelessAllowedOperationsTests.java
    incubator/openejb/trunk/openejb3/server/openejb-http/src/main/java/org/apache/openejb/server/httpd/HttpServer.java
    incubator/openejb/trunk/openejb3/server/openejb-server/src/main/java/org/apache/openejb/server/ServicePool.java

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java?view=diff&rev=515484&r1=515483&r2=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/DeploymentInfo.java Tue Mar  6 23:26:43 2007
@@ -16,6 +16,8 @@
  */
 package org.apache.openejb;
 
+import org.apache.openejb.core.timer.EjbTimerService;
+
 import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.Map;
@@ -97,6 +99,10 @@
     List<Injection> getInjections();
 
     void setContainer(Container container);
+
+    Method getEjbTimeout();
+
+    EjbTimerService getEjbTimerService();
 
     public interface BusinessLocalHome extends javax.ejb.EJBLocalHome {
         Object create();

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanBuilder.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanBuilder.java?view=diff&rev=515484&r1=515483&r2=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanBuilder.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanBuilder.java Tue Mar  6 23:26:43 2007
@@ -21,6 +21,7 @@
 import org.apache.openejb.OpenEJBException;
 import org.apache.openejb.core.CoreDeploymentInfo;
 import org.apache.openejb.core.DeploymentContext;
+import org.apache.openejb.core.timer.EjbTimerServiceImpl;
 import org.apache.openejb.core.cmp.CmpUtil;
 import org.apache.openejb.core.interceptor.InterceptorData;
 import org.apache.openejb.util.Index;
@@ -29,6 +30,8 @@
 
 import javax.naming.Context;
 import javax.persistence.EntityManagerFactory;
+import javax.ejb.TimedObject;
+import javax.ejb.Timer;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -172,7 +175,13 @@
         
         deployment.setPostConstruct(getCallback(ejbClass, bean.postConstruct));
         deployment.setPreDestroy(getCallback(ejbClass, bean.preDestroy));
+
+        // Timer
         deployment.setEjbTimeout(getTimeout(ejbClass, bean.timeoutMethod));
+        if (deployment.getEjbTimeout() != null) {
+            EjbTimerServiceImpl timerService = new EjbTimerServiceImpl(deployment);
+            deployment.setEjbTimerService(timerService);
+        }
 
         // interceptors
         InterceptorBuilder interceptorBuilder = new InterceptorBuilder(defaultInterceptors, bean);
@@ -268,7 +277,9 @@
     private Method getTimeout(Class ejbClass, NamedMethodInfo info) {
         Method timeout = null;
         try {
-            if (info.methodParams != null) {
+            if (TimedObject.class.isAssignableFrom(ejbClass)) {
+                timeout = TimedObject.class.getMethod("ejbTimeout", Timer.class);
+            } else if (info.methodParams != null) {
                 List<Class> parameterTypes = new ArrayList<Class>();
 
                 for (String paramType : info.methodParams) {

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/BaseContext.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/BaseContext.java?view=diff&rev=515484&r1=515483&r2=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/BaseContext.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/BaseContext.java Tue Mar  6 23:26:43 2007
@@ -33,6 +33,8 @@
 import javax.transaction.UserTransaction;
 
 import org.apache.openejb.DeploymentInfo;
+import org.apache.openejb.core.timer.EjbTimerService;
+import org.apache.openejb.core.timer.TimerServiceImpl;
 import org.apache.openejb.spi.SecurityService;
 
 
@@ -204,7 +206,7 @@
             DeploymentInfo di = threadContext.getDeploymentInfo();
 
             if (di.isBeanManagedTransaction()) {
-                throw new IllegalStateException("bean-managed transaction beans can not access the getRollbackOnly() method");
+                throw new IllegalStateException("bean-managed transaction beans can not access the getRollbackOnly() method: deploymentId=" + di.getDeploymentID());
             }
 
             try {
@@ -223,7 +225,13 @@
         }
 
         public TimerService getTimerService() throws IllegalStateException {
-            return null;  //todo: consider this autogenerated code
+            ThreadContext threadContext = ThreadContext.getThreadContext();
+            DeploymentInfo deploymentInfo = threadContext.getDeploymentInfo();
+            EjbTimerService timerService = deploymentInfo.getEjbTimerService();
+            if (timerService == null) {
+                throw new IllegalStateException("This ejb does not support timers " + deploymentInfo.getDeploymentID());
+            }
+            return new TimerServiceImpl(timerService, threadContext.getPrimaryKey());
         }
 
         public Object lookup(String name) {

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java?view=diff&rev=515484&r1=515483&r2=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/CoreDeploymentInfo.java Tue Mar  6 23:26:43 2007
@@ -33,6 +33,7 @@
 import javax.ejb.SessionBean;
 import javax.ejb.MessageDrivenBean;
 import javax.ejb.TimedObject;
+import javax.ejb.Timer;
 import javax.persistence.EntityManagerFactory;
 import javax.naming.Context;
 
@@ -63,8 +64,10 @@
 import org.apache.openejb.core.transaction.TxSupports;
 import org.apache.openejb.core.interceptor.InterceptorData;
 import org.apache.openejb.core.mdb.MessageDrivenBeanManagedTxPolicy;
+import org.apache.openejb.core.timer.EjbTimerService;
 import org.apache.openejb.util.proxy.ProxyManager;
 import org.apache.openejb.util.Index;
+import org.apache.openejb.util.Logger;
 
 /**
  * @org.apache.xbean.XBean element="deployment"
@@ -87,7 +90,9 @@
     private Method preDestroy;
     private Method prePassivate;
     private Method postActivate;
+
     private Method ejbTimeout;
+    private EjbTimerService ejbTimerService;
 
     private boolean isBeanManagedTransaction;
     private boolean isReentrant;
@@ -186,7 +191,7 @@
         }
         if (TimedObject.class.isAssignableFrom(beanClass)) {
             try {
-                this.ejbTimeout = beanClass.getMethod("ejbTimeout");
+                this.ejbTimeout = beanClass.getMethod("ejbTimeout", Timer.class);
             } catch (NoSuchMethodException e) {
                 throw new IllegalStateException(e);
             }
@@ -275,7 +280,8 @@
     public TransactionPolicy getTransactionPolicy(Method method) {
         TransactionPolicy policy = methodTransactionPolicies.get(method);
         if (policy == null && !isBeanManagedTransaction) {
-            org.apache.log4j.Logger.getLogger("OpenEJB").info("The following method doesn't have a transaction policy assigned: " + method);
+            Logger log = Logger.getInstance("OpenEJB", "org.apache.openejb.util.resources");
+            log.info("The following method doesn't have a transaction policy assigned: " + method);
         }
         if (policy == null && container instanceof TransactionContainer) {
             if (isBeanManagedTransaction) {
@@ -935,5 +941,13 @@
 
     public void setEjbTimeout(Method ejbTimeout) {
         this.ejbTimeout = ejbTimeout;
+    }
+
+    public EjbTimerService getEjbTimerService() {
+        return ejbTimerService;
+    }
+
+    public void setEjbTimerService(EjbTimerService ejbTimerService) {
+        this.ejbTimerService = ejbTimerService;
     }
 }

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/cmp/CmpContainer.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/cmp/CmpContainer.java?view=diff&rev=515484&r1=515483&r2=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/cmp/CmpContainer.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/cmp/CmpContainer.java Tue Mar  6 23:26:43 2007
@@ -37,6 +37,7 @@
 import javax.ejb.EntityBean;
 import javax.ejb.ObjectNotFoundException;
 import javax.ejb.RemoveException;
+import javax.ejb.Timer;
 import javax.transaction.TransactionManager;
 
 import org.apache.openejb.ApplicationException;
@@ -48,6 +49,7 @@
 import org.apache.openejb.core.CoreDeploymentInfo;
 import org.apache.openejb.core.Operation;
 import org.apache.openejb.core.ThreadContext;
+import org.apache.openejb.core.timer.EjbTimerService;
 import org.apache.openejb.core.entity.EntityContext;
 import org.apache.openejb.core.transaction.TransactionContainer;
 import org.apache.openejb.core.transaction.TransactionContext;
@@ -123,60 +125,73 @@
         deploy((CoreDeploymentInfo) deploymentInfo);
     }
 
-    public void undeploy(DeploymentInfo deploymentInfo) throws OpenEJBException {
-        undeploy((CoreDeploymentInfo)deploymentInfo);
-    }
+    public void deploy(CoreDeploymentInfo deploymentInfo) throws OpenEJBException {
+        synchronized (this) {
+            Object deploymentId = deploymentInfo.getDeploymentID();
 
-    public synchronized void undeploy(CoreDeploymentInfo deploymentInfo) throws OpenEJBException {
-        deploymentsById.remove(deploymentInfo.getDeploymentID());
-        deploymentsByClass.remove(deploymentInfo.getCmpImplClass());
+            Object cmpEngineKey = deploymentInfo.getJarPath();
+            if (cmpEngineKey == null) {
+                cmpEngineKey = deploymentInfo.getClassLoader();
+            }
 
-        try {
-            Field field = deploymentInfo.getCmpImplClass().getField("deploymentInfo");
-            field.set(null, null);
-        } catch (Exception e) {
-            // ignore
+            CmpEngine cmpEngine = cmpEngines.get(cmpEngineKey);
+            if (cmpEngine == null) {
+                cmpEngine = createCmpEngine(deploymentInfo.getJarPath(), deploymentInfo.getClassLoader());
+                cmpEngines.put(cmpEngineKey, cmpEngine);
+            }
+            cmpEngine.deploy(deploymentInfo);
+            deploymentInfo.setContainerData(cmpEngine);
+
+            // try to set deploymentInfo static field on bean implementation class
+            try {
+                Field field = deploymentInfo.getCmpImplClass().getField("deploymentInfo");
+                field.set(null, deploymentInfo);
+            } catch (Exception e) {
+                // ignore
+            }
+
+            // add to indexes
+            deploymentsById.put(deploymentId, deploymentInfo);
+            deploymentsByClass.put(deploymentInfo.getCmpImplClass(), deploymentInfo);
+            deploymentInfo.setContainer(this);
         }
 
-        CmpEngine cmpEngine = (CmpEngine) deploymentInfo.getContainerData();
-        cmpEngine.undeploy(deploymentInfo);
+        EjbTimerService timerService = deploymentInfo.getEjbTimerService();
+        if (timerService != null) {
+            timerService.start();
+        }
+    }
 
-        if (cmpEngine.isEmpty()){
-            cmpEngines.remove(deploymentInfo.getJarPath());
-            cmpEngines.remove(deploymentInfo.getClassLoader());
+    public void undeploy(DeploymentInfo deploymentInfo) throws OpenEJBException {
+        EjbTimerService timerService = deploymentInfo.getEjbTimerService();
+        if (timerService != null) {
+            timerService.stop();
         }
-        deploymentInfo.setContainer(null);
-        deploymentInfo.setContainerData(null);
+        undeploy((CoreDeploymentInfo)deploymentInfo);
     }
 
-    public synchronized void deploy(CoreDeploymentInfo deploymentInfo) throws OpenEJBException {
-        Object deploymentId = deploymentInfo.getDeploymentID();
+    public void undeploy(CoreDeploymentInfo deploymentInfo) throws OpenEJBException {
+        synchronized (this) {
+            deploymentsById.remove(deploymentInfo.getDeploymentID());
+            deploymentsByClass.remove(deploymentInfo.getCmpImplClass());
 
-        Object cmpEngineKey = deploymentInfo.getJarPath();
-        if (cmpEngineKey == null) {
-            cmpEngineKey = deploymentInfo.getClassLoader();
-        }
+            try {
+                Field field = deploymentInfo.getCmpImplClass().getField("deploymentInfo");
+                field.set(null, null);
+            } catch (Exception e) {
+                // ignore
+            }
 
-        CmpEngine cmpEngine = cmpEngines.get(cmpEngineKey);
-        if (cmpEngine == null) {
-            cmpEngine = createCmpEngine(deploymentInfo.getJarPath(), deploymentInfo.getClassLoader());
-            cmpEngines.put(cmpEngineKey, cmpEngine);
-        }
-        cmpEngine.deploy(deploymentInfo);
-        deploymentInfo.setContainerData(cmpEngine);
+            CmpEngine cmpEngine = (CmpEngine) deploymentInfo.getContainerData();
+            cmpEngine.undeploy(deploymentInfo);
 
-        // try to set deploymentInfo static field on bean implementation class
-        try {
-            Field field = deploymentInfo.getCmpImplClass().getField("deploymentInfo");
-            field.set(null, deploymentInfo);
-        } catch (Exception e) {
-            // ignore
+            if (cmpEngine.isEmpty()){
+                cmpEngines.remove(deploymentInfo.getJarPath());
+                cmpEngines.remove(deploymentInfo.getClassLoader());
+            }
+            deploymentInfo.setContainer(null);
+            deploymentInfo.setContainerData(null);
         }
-
-        // add to indexes
-        deploymentsById.put(deploymentId, deploymentInfo);
-        deploymentsByClass.put(deploymentInfo.getCmpImplClass(), deploymentInfo);
-        deploymentInfo.setContainer(this);
     }
 
     public Object getEjbInstance(CoreDeploymentInfo deployInfo, Object primaryKey) {
@@ -693,10 +708,23 @@
             EntityBean entityBean = (EntityBean) cmpEngine.loadBean(callContext, callContext.getPrimaryKey());
             ejbRemove(entityBean);
             cmpEngine.removeBean(callContext);
+            cancelTimers(callContext);
         } catch (Throwable e) {// handle reflection exception
             txPolicy.handleSystemException(e, null, txContext);
         } finally {
             txPolicy.afterInvoke(null, txContext);
+        }
+    }
+
+    private void cancelTimers(ThreadContext threadContext) {
+        CoreDeploymentInfo deploymentInfo = threadContext.getDeploymentInfo();
+        Object primaryKey = threadContext.getPrimaryKey();
+
+        // stop timers
+        if (primaryKey != null && deploymentInfo.getEjbTimerService() != null) {
+            for (Timer timer : deploymentInfo.getEjbTimerService().getTimers(primaryKey)) {
+                timer.cancel();
+            }
         }
     }
 

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityContainer.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityContainer.java?view=diff&rev=515484&r1=515483&r2=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityContainer.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/entity/EntityContainer.java Tue Mar  6 23:26:43 2007
@@ -20,12 +20,16 @@
 import java.rmi.RemoteException;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Iterator;
+import java.util.Enumeration;
+import java.util.Collection;
 
 import javax.ejb.EJBHome;
 import javax.ejb.EJBLocalHome;
 import javax.ejb.EJBLocalObject;
 import javax.ejb.EJBObject;
 import javax.ejb.EntityBean;
+import javax.ejb.Timer;
 import javax.transaction.Transaction;
 import javax.transaction.TransactionManager;
 
@@ -38,6 +42,7 @@
 import org.apache.openejb.core.Operation;
 import org.apache.openejb.core.ThreadContext;
 import org.apache.openejb.core.CoreDeploymentInfo;
+import org.apache.openejb.core.timer.EjbTimerService;
 import org.apache.openejb.core.transaction.TransactionContainer;
 import org.apache.openejb.core.transaction.TransactionContext;
 import org.apache.openejb.core.transaction.TransactionPolicy;
@@ -66,12 +71,13 @@
         instanceManager = new EntityInstanceManager(this, transactionManager, securityService, poolSize);
     }
 
-    public DeploymentInfo [] deployments() {
+    public synchronized DeploymentInfo [] deployments() {
         return deploymentRegistry.values().toArray(new DeploymentInfo[deploymentRegistry.size()]);
     }
 
-    public DeploymentInfo getDeploymentInfo(Object deploymentID) {
-        return deploymentRegistry.get(deploymentID);
+    public synchronized DeploymentInfo getDeploymentInfo(Object deploymentID) {
+        String id = (String) deploymentID;
+        return deploymentRegistry.get(id);
     }
 
     public ContainerType getContainerType() {
@@ -83,18 +89,32 @@
     }
 
     public void deploy(DeploymentInfo info) throws OpenEJBException {
-        Map registry = new HashMap(deploymentRegistry);
-        registry.put(info.getDeploymentID(), info);
-        deploymentRegistry = registry;
-        org.apache.openejb.core.CoreDeploymentInfo di = (org.apache.openejb.core.CoreDeploymentInfo) info;
-        di.setContainer(this);
+        synchronized (this) {
+            CoreDeploymentInfo deploymentInfo = (CoreDeploymentInfo) info;
+            deploymentRegistry.put((String)deploymentInfo.getDeploymentID(), deploymentInfo);
+            deploymentInfo.setContainer(this);
+        }
         instanceManager.deploy(info);
+
+        EjbTimerService timerService = info.getEjbTimerService();
+        if (timerService != null) {
+            timerService.start();
+        }
     }
 
     public void undeploy(DeploymentInfo info) throws OpenEJBException {
-        deploymentRegistry.remove(info.getDeploymentID());
+        EjbTimerService timerService = info.getEjbTimerService();
+        if (timerService != null) {
+            timerService.stop();
+        }
+
         instanceManager.undeploy(info);
-        info.setContainer(null);
+
+        synchronized (this) {
+            String id = (String) info.getDeploymentID();
+            deploymentRegistry.remove(id);
+            info.setContainer(null);
+        }
     }
 
     public Object invoke(Object deployID, Method callMethod, Object [] args, Object primKey, Object securityIdentity) throws org.apache.openejb.OpenEJBException {
@@ -209,8 +229,7 @@
         return returnValue;
     }
 
-    public void ejbLoad_If_No_Transaction(ThreadContext callContext, EntityBean bean)
-            throws org.apache.openejb.SystemException, Exception {
+    public void ejbLoad_If_No_Transaction(ThreadContext callContext, EntityBean bean) throws Exception {
         Operation orginalOperation = callContext.getCurrentOperation();
         if (orginalOperation == Operation.BUSINESS || orginalOperation == Operation.REMOVE) {
 
@@ -222,12 +241,12 @@
             }
 
             if (currentTx == null) {
-                callContext.setCurrentOperation(org.apache.openejb.core.Operation.LOAD);
+                callContext.setCurrentOperation(Operation.LOAD);
                 try {
-                    ((javax.ejb.EntityBean) bean).ejbLoad();
+                    bean.ejbLoad();
                 } catch (Exception e) {
 
-                    instanceManager.discardInstance(callContext, (EntityBean) bean);
+                    instanceManager.discardInstance(callContext, bean);
                     throw e;
                 } finally {
                     callContext.setCurrentOperation(orginalOperation);
@@ -255,12 +274,12 @@
             }
 
             if (currentTx == null) {
-                callContext.setCurrentOperation(org.apache.openejb.core.Operation.STORE);
+                callContext.setCurrentOperation(Operation.STORE);
                 try {
-                    ((javax.ejb.EntityBean) bean).ejbStore();
+                    bean.ejbStore();
                 } catch (Exception e) {
 
-                    instanceManager.discardInstance(callContext, (EntityBean) bean);
+                    instanceManager.discardInstance(callContext, bean);
                     throw e;
                 } finally {
                     callContext.setCurrentOperation(currentOp);
@@ -381,7 +400,7 @@
         * The primary keys are converted to ProxyInfo objects.
         */
         if (returnValue instanceof java.util.Collection) {
-            java.util.Iterator keys = ((java.util.Collection) returnValue).iterator();
+            Iterator keys = ((Collection) returnValue).iterator();
             java.util.Vector proxies = new java.util.Vector();
             while (keys.hasNext()) {
                 Object primaryKey = keys.next();
@@ -389,7 +408,7 @@
             }
             returnValue = proxies;
         } else if (returnValue instanceof java.util.Enumeration) {
-            java.util.Enumeration keys = (java.util.Enumeration) returnValue;
+            Enumeration keys = (Enumeration) returnValue;
             java.util.Vector proxies = new java.util.Vector();
             while (keys.hasMoreElements()) {
                 Object primaryKey = keys.nextElement();
@@ -402,15 +421,27 @@
         return returnValue;
     }
 
-    protected Object homeMethod(Method callMethod, Object [] args, ThreadContext callContext)
-            throws org.apache.openejb.OpenEJBException {
-        org.apache.openejb.core.CoreDeploymentInfo deploymentInfo = (org.apache.openejb.core.CoreDeploymentInfo) callContext.getDeploymentInfo();
+    protected Object homeMethod(Method callMethod, Object [] args, ThreadContext callContext) throws OpenEJBException {
+        org.apache.openejb.core.CoreDeploymentInfo deploymentInfo = callContext.getDeploymentInfo();
         callContext.setCurrentOperation(Operation.HOME);
         Method runMethod = deploymentInfo.getMatchingBeanMethod(callMethod);
         return invoke(callMethod, runMethod, args, callContext);
     }
 
-    protected void didRemove(EntityBean bean, ThreadContext callContext) throws OpenEJBException {
+    protected void didRemove(EntityBean bean, ThreadContext threadContext) throws OpenEJBException {
+        cancelTimers(threadContext);
+    }
+
+    private void cancelTimers(ThreadContext threadContext) {
+        CoreDeploymentInfo deploymentInfo = threadContext.getDeploymentInfo();
+        Object primaryKey = threadContext.getPrimaryKey();
+
+        // stop timers
+        if (primaryKey != null && deploymentInfo.getEjbTimerService() != null) {
+            for (Timer timer : deploymentInfo.getEjbTimerService().getTimers(primaryKey)) {
+                timer.cancel();
+            }
+        }
     }
 
     protected void removeEJBObject(Method callMethod, Object [] args, ThreadContext callContext)

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbHomeProxyHandler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbHomeProxyHandler.java?view=diff&rev=515484&r1=515483&r2=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbHomeProxyHandler.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbHomeProxyHandler.java Tue Mar  6 23:26:43 2007
@@ -20,6 +20,7 @@
 import java.lang.reflect.Method;
 import java.rmi.RemoteException;
 import java.util.HashMap;
+import java.util.Map;
 
 import javax.ejb.EJBHome;
 import javax.ejb.EJBException;
@@ -31,11 +32,12 @@
 import org.apache.openejb.loader.SystemInstance;
 import org.apache.openejb.spi.ApplicationServer;
 import org.apache.openejb.util.proxy.ProxyManager;
+import org.apache.openejb.util.Logger;
 
 public abstract class EjbHomeProxyHandler extends BaseEjbProxyHandler {
-    protected final static org.apache.log4j.Category logger = org.apache.log4j.Category.getInstance("OpenEJB");
+    private static final Logger logger = Logger.getInstance("OpenEJB", "org.apache.openejb.util.resources");
 
-    private final HashMap<String,MethodType> dispatchTable;
+    private final Map<String,MethodType> dispatchTable;
 
     private static enum MethodType {
         CREATE,
@@ -47,7 +49,7 @@
 
     public EjbHomeProxyHandler(RpcContainer container, Object pk, Object depID, InterfaceType interfaceType) {
         super(container, pk, depID, interfaceType);
-        dispatchTable = new HashMap();
+        dispatchTable = new HashMap<String,MethodType>();
         dispatchTable.put("create", MethodType.CREATE);
         dispatchTable.put("getEJBMetaData", MethodType.META_DATA);
         dispatchTable.put("getHomeHandle", MethodType.HOME_HANDLE);

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbObjectProxyHandler.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbObjectProxyHandler.java?view=diff&rev=515484&r1=515483&r2=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbObjectProxyHandler.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/ivm/EjbObjectProxyHandler.java Tue Mar  6 23:26:43 2007
@@ -22,12 +22,12 @@
 
 import org.apache.openejb.RpcContainer;
 import org.apache.openejb.InterfaceType;
+import org.apache.openejb.util.Logger;
 import org.apache.openejb.loader.SystemInstance;
 import org.apache.openejb.spi.ApplicationServer;
 
 public abstract class EjbObjectProxyHandler extends BaseEjbProxyHandler {
-
-    protected final static org.apache.log4j.Category logger = org.apache.log4j.Category.getInstance("OpenEJB");
+    private static final Logger logger = Logger.getInstance("OpenEJB", "org.apache.openejb.util.resources");
     static final java.util.HashMap dispatchTable;
 
     static {
@@ -112,7 +112,7 @@
             throw new RemoteException("Container has suffered a SystemException", exc);
         } catch (org.apache.openejb.OpenEJBException oe) {
             exc = (oe.getRootCause() != null) ? oe.getRootCause() : oe;
-            logger.warn("The container received an unexpected exception: ", exc);
+            logger.warning("The container received an unexpected exception: ", exc);
             throw new RemoteException("Unknown Container Exception", oe.getRootCause());
         } finally {
             if (logger.isDebugEnabled()) {

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java?view=diff&rev=515484&r1=515483&r2=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbContainer.java Tue Mar  6 23:26:43 2007
@@ -19,10 +19,11 @@
 
 import org.apache.openejb.OpenEJBException;
 import org.apache.openejb.DeploymentInfo;
-import org.apache.openejb.Container;
 import org.apache.openejb.SystemException;
 import org.apache.openejb.ApplicationException;
 import org.apache.openejb.ContainerType;
+import org.apache.openejb.RpcContainer;
+import org.apache.openejb.util.Logger;
 import org.apache.openejb.spi.SecurityService;
 import org.apache.openejb.core.ThreadContext;
 import org.apache.openejb.core.CoreDeploymentInfo;
@@ -30,7 +31,6 @@
 import org.apache.openejb.core.transaction.TransactionContainer;
 import org.apache.openejb.core.transaction.TransactionContext;
 import org.apache.openejb.core.transaction.TransactionPolicy;
-import org.apache.log4j.Logger;
 import org.apache.xbean.recipe.ObjectRecipe;
 import org.apache.xbean.recipe.Option;
 
@@ -45,8 +45,8 @@
 import java.util.Map;
 import java.util.Arrays;
 
-public class MdbContainer implements Container, TransactionContainer {
-    protected static final Logger logger = Logger.getLogger("OpenEJB");
+public class MdbContainer implements RpcContainer, TransactionContainer {
+    private static final Logger logger = Logger.getInstance("OpenEJB", "org.apache.openejb.util.resources");
     private static final Object[] NO_ARGS = new Object[0];
 
     private final Object containerID;
@@ -211,6 +211,16 @@
         } catch (Exception e) {
             throw new SystemException("Unable to enlist xa resource in the transaction", e);
         }
+    }
+
+    public Object invoke(Object deploymentId, Method method, Object[] args, Object primKey, Object securityIdentity) throws OpenEJBException {
+        // this method can only be used for ejbTimeout
+        DeploymentInfo deploymentInfo = deployments.get(deploymentId);
+        if (!method.equals(deploymentInfo.getEjbTimeout())) {
+            throw new OpenEJBException("RpcContainer invoke method of MdbContainer can only be used to ejbTimeout invocations");
+        }
+        // todo create an instance and invoke the method and destroy instance
+        return null;
     }
 
     public Object invoke(Object instance, Method method, Object... args) throws SystemException, ApplicationException {

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbInstanceFactory.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbInstanceFactory.java?view=diff&rev=515484&r1=515483&r2=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbInstanceFactory.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/mdb/MdbInstanceFactory.java Tue Mar  6 23:26:43 2007
@@ -17,12 +17,12 @@
  */
 package org.apache.openejb.core.mdb;
 
-import org.apache.log4j.Logger;
 import org.apache.openejb.core.CoreDeploymentInfo;
 import org.apache.openejb.core.Operation;
 import org.apache.openejb.core.ThreadContext;
 import org.apache.openejb.spi.SecurityService;
 import org.apache.openejb.Injection;
+import org.apache.openejb.util.Logger;
 import org.apache.xbean.recipe.ObjectRecipe;
 import org.apache.xbean.recipe.Option;
 import org.apache.xbean.recipe.StaticRecipe;
@@ -46,7 +46,7 @@
  * resource adapter.
  */
 public class MdbInstanceFactory {
-    private static final Logger logger = Logger.getLogger("OpenEJB");
+    private static final Logger logger = Logger.getInstance("OpenEJB", "org.apache.openejb.util.resources");
 
     private final CoreDeploymentInfo deploymentInfo;
     private final TransactionManager transactionManager;
@@ -187,7 +187,7 @@
                         objectRecipe.setProperty(injection.getName(), new StaticRecipe(object));
                     }
                 } catch (NamingException e) {
-                    logger.warn("Injection data not found in enc: jndiName='"+injection.getJndiName()+"', target="+injection.getTarget()+"/"+injection.getName());
+                    logger.warning("Injection data not found in enc: jndiName='"+injection.getJndiName()+"', target="+injection.getTarget()+"/"+injection.getName());
                 }
             }
             // only in this case should the callback be used

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/SimplePassivater.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/SimplePassivater.java?view=diff&rev=515484&r1=515483&r2=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/SimplePassivater.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/SimplePassivater.java Tue Mar  6 23:26:43 2007
@@ -17,6 +17,7 @@
 package org.apache.openejb.core.stateful;
 
 import org.apache.openejb.SystemException;
+import org.apache.openejb.util.Logger;
 import org.apache.openejb.core.EnvProps;
 import org.apache.openejb.loader.SystemInstance;
 
@@ -31,7 +32,7 @@
 
 public class SimplePassivater implements PassivationStrategy {
     private File sessionDirectory;
-    final static protected org.apache.log4j.Category logger = org.apache.log4j.Category.getInstance("OpenEJB");
+    private static final Logger logger = Logger.getInstance("OpenEJB", "org.apache.openejb.util.resources");
 
     public SimplePassivater() throws SystemException {
         init(null);

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessContainer.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessContainer.java?view=diff&rev=515484&r1=515483&r2=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessContainer.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessContainer.java Tue Mar  6 23:26:43 2007
@@ -23,6 +23,7 @@
 import org.apache.openejb.core.Operation;
 import org.apache.openejb.core.ThreadContext;
 import org.apache.openejb.core.CoreDeploymentInfo;
+import org.apache.openejb.core.timer.EjbTimerService;
 import org.apache.openejb.core.transaction.TransactionContainer;
 import org.apache.openejb.core.transaction.TransactionContext;
 import org.apache.openejb.core.transaction.TransactionPolicy;
@@ -63,12 +64,13 @@
         }
     }
 
-    public DeploymentInfo [] deployments() {
-        return (DeploymentInfo []) deploymentRegistry.values().toArray(new DeploymentInfo[deploymentRegistry.size()]);
+    public synchronized DeploymentInfo [] deployments() {
+        return deploymentRegistry.values().toArray(new DeploymentInfo[deploymentRegistry.size()]);
     }
 
-    public DeploymentInfo getDeploymentInfo(Object deploymentID) {
-        return (DeploymentInfo) deploymentRegistry.get(deploymentID);
+    public synchronized DeploymentInfo getDeploymentInfo(Object deploymentID) {
+        String id = (String) deploymentID;
+        return deploymentRegistry.get(id);
     }
 
     public ContainerType getContainerType() {
@@ -80,21 +82,35 @@
     }
 
     public void deploy(DeploymentInfo info) throws OpenEJBException {
-        HashMap registry = (HashMap) deploymentRegistry.clone();
-        registry.put(info.getDeploymentID(), info);
-        deploymentRegistry = registry;
-        org.apache.openejb.core.CoreDeploymentInfo di = (org.apache.openejb.core.CoreDeploymentInfo) info;
-        di.setContainer(this);
+        CoreDeploymentInfo deploymentInfo = (CoreDeploymentInfo) info;
+        String id = (String) deploymentInfo.getDeploymentID();
+        synchronized (this) {
+            deploymentRegistry.put(id, deploymentInfo);
+            deploymentInfo.setContainer(this);
+        }
+
+        EjbTimerService timerService = deploymentInfo.getEjbTimerService();
+        if (timerService != null) {
+            timerService.start();
+        }
     }
 
-    public void undeploy(DeploymentInfo info) throws OpenEJBException {
+    public void undeploy(DeploymentInfo info) {
         undeploy((CoreDeploymentInfo)info);
     }
 
-    private synchronized void undeploy(CoreDeploymentInfo deploymentInfo) {
-        deploymentRegistry.remove(deploymentInfo.getDeploymentID());
-        deploymentInfo.setContainer(null);
-        deploymentInfo.setContainerData(null);
+    private void undeploy(CoreDeploymentInfo deploymentInfo) {
+        EjbTimerService timerService = deploymentInfo.getEjbTimerService();
+        if (timerService != null) {
+            timerService.stop();
+        }
+
+        synchronized (this) {
+            String id = (String) deploymentInfo.getDeploymentID();
+            deploymentInfo.setContainer(null);
+            deploymentInfo.setContainerData(null);
+            deploymentRegistry.remove(id);
+        }
     }
 
     public Object invoke(Object deployID, Method callMethod, Object [] args, Object primKey, Object securityIdentity) throws OpenEJBException {
@@ -198,9 +214,5 @@
 
     public void discardInstance(Object instance, ThreadContext context) {
         instanceManager.discardInstance(context, instance);
-    }
-
-    public HashMap getDeploymentRegistry() {
-        return deploymentRegistry;
     }
 }

Modified: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessInstanceManager.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessInstanceManager.java?view=diff&rev=515484&r1=515483&r2=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessInstanceManager.java (original)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateless/StatelessInstanceManager.java Tue Mar  6 23:26:43 2007
@@ -16,7 +16,6 @@
  */
 package org.apache.openejb.core.stateless;
 
-import org.apache.log4j.Category;
 import org.apache.xbean.recipe.ObjectRecipe;
 import org.apache.xbean.recipe.StaticRecipe;
 import org.apache.xbean.recipe.Option;
@@ -30,6 +29,7 @@
 import org.apache.openejb.util.LinkedListStack;
 import org.apache.openejb.util.SafeToolkit;
 import org.apache.openejb.util.Stack;
+import org.apache.openejb.util.Logger;
 
 import javax.ejb.SessionContext;
 import javax.transaction.TransactionManager;
@@ -41,6 +41,7 @@
 import java.util.Map;
 
 public class StatelessInstanceManager {
+    private static final Logger logger = Logger.getInstance("OpenEJB", "org.apache.openejb.util.resources");
 
     protected java.util.HashMap poolMap = new HashMap();
     protected int poolLimit = 0;
@@ -50,7 +51,6 @@
     protected PoolQueue poolQueue = null;
 
     protected final SafeToolkit toolkit = SafeToolkit.getToolkit("StatefulInstanceManager");
-    protected final static Category logger = Category.getInstance("OpenEJB");
     private TransactionManager transactionManager;
     private SecurityService securityService;
 
@@ -124,7 +124,7 @@
                             objectRecipe.setProperty(injection.getName(), new StaticRecipe(object));
                         }
                     } catch (NamingException e) {
-                        logger.warn("Injection data not found in enc: jndiName='"+injection.getJndiName()+"', target="+injection.getTarget()+"/"+injection.getName());
+                        logger.warning("Injection data not found in enc: jndiName='"+injection.getJndiName()+"', target="+injection.getTarget()+"/"+injection.getName());
                     }
                 }
 
@@ -132,7 +132,7 @@
                 Map unsetProperties = objectRecipe.getUnsetProperties();
                 if (unsetProperties.size() > 0){
                     for (Object property : unsetProperties.keySet()) {
-                        logger.warn("Injection: No such property '"+property+"' in class "+beanClass.getName());
+                        logger.warning("Injection: No such property '"+property+"' in class "+beanClass.getName());
                     }
                 }
                 callContext.setCurrentOperation(Operation.LIFECYCLE);

Added: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/DatabaseTimerStore.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/DatabaseTimerStore.java?view=auto&rev=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/DatabaseTimerStore.java (added)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/DatabaseTimerStore.java Tue Mar  6 23:26:43 2007
@@ -0,0 +1,283 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.openejb.core.timer;
+
+import com.thoughtworks.xstream.XStream;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+
+import org.apache.openejb.util.Logger;
+
+public class DatabaseTimerStore implements TimerStore {
+    private static final Logger log = Logger.getInstance("Timer", "org.apache.openejb.util.resources");
+
+    private static final String createSequenceSQL = "create sequence timertasks_seq";
+    private static final String createTableSQLWithSequence = "create table timertasks (id long primary key, serverid varchar(256) not null, timerkey varchar(256) not null, userid varchar(4096), userinfo varchar(4096), firsttime long not null, period long)";
+    private static final String createTableSQLWithIdentity = "create table timertasks (id INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1), serverid varchar(256) not null, timerkey varchar(256) not null, userid varchar(4096), userinfo varchar(4096), firsttime NUMERIC(18,0) not null, period NUMERIC(18, 0))";
+    private static final String sequenceSQL = "select timertasks_seq.nextval";
+    private static final String identitySQL = "values IDENTITY_VAL_LOCAL()";
+    private static final String insertSQLWithSequence = "insert into timertasks (id, serverid, timerkey, userid, userinfo, firsttime, period) values (?, ?, ?, ?, ?, ?, ?)";
+    private static final String insertSQLWithIdentity = "insert into timertasks (serverid, timerkey, userid, userinfo, firsttime, period) values (?, ?, ?, ?, ?, ?)";
+    private static final String deleteSQL = "delete from timertasks where id=?";
+    private static final String selectSQL = "select id, userid, userinfo, firsttime, period from timertasks where serverid = ? and timerkey=?";
+    private static final String fixedRateUpdateSQL = "update timertasks set firsttime = ? where id = ?";
+
+    private final String serverUniqueId;
+    private final DataSource dataSource;
+    private boolean useSequence = false;
+
+    protected DatabaseTimerStore(String serverUniqueId, DataSource datasource, boolean useSequence) throws SQLException {
+        this.serverUniqueId = serverUniqueId;
+        this.dataSource = datasource;
+        this.useSequence = useSequence;
+        if (this.useSequence) {
+            execSQL(createSequenceSQL);
+            execSQL(createTableSQLWithSequence);
+        } else {
+            execSQL(createTableSQLWithIdentity);
+        }
+    }
+
+    public TimerData getTimer(String deploymentId, long timerId) {
+        // todo not implemented
+        return null;
+    }
+
+    public Collection<TimerData> getTimers(String deploymentId) {
+        // todo not implemented
+        return null;
+    }
+
+    public TimerData createTimer(EjbTimerServiceImpl timerService, String deploymentId, Object primaryKey, Object info, Date expiration, long intervalDuration) throws TimerStoreException {
+        boolean threwException = false;
+        Connection c = getConnection();
+        long id;
+        try {
+            if (useSequence) {
+                PreparedStatement seqStatement = c.prepareStatement(sequenceSQL);
+                try {
+                    ResultSet seqRS = seqStatement.executeQuery();
+                    try {
+                        seqRS.next();
+                        id = seqRS.getLong(1);
+                    } finally {
+                        seqRS.close();
+                    }
+                } finally {
+                    seqStatement.close();
+                }
+                PreparedStatement insertStatement = c.prepareStatement(insertSQLWithSequence);
+                try {
+                    String serializedPrimaryKey = serializeObject(primaryKey);
+                    String serializedInfo = serializeObject(info);
+                    insertStatement.setLong(1, id);
+                    insertStatement.setString(2, serverUniqueId);
+                    insertStatement.setString(3, deploymentId);
+                    insertStatement.setString(4, serializedPrimaryKey);
+                    insertStatement.setString(5, serializedInfo);
+                    insertStatement.setLong(6, expiration.getTime());
+                    insertStatement.setLong(7, intervalDuration);
+                    int result = insertStatement.executeUpdate();
+                    if (result != 1) {
+                        throw new TimerStoreException("Could not insert!");
+                    }
+                } finally {
+                    insertStatement.close();
+                }
+            } else {
+                PreparedStatement insertStatement = c.prepareStatement(insertSQLWithIdentity);
+                try {
+                    String serializedPrimaryKey = serializeObject(primaryKey);
+                    String serializedInfo = serializeObject(info);
+                    insertStatement.setString(1, serverUniqueId);
+                    insertStatement.setString(2, deploymentId);
+                    insertStatement.setString(3, serializedPrimaryKey);
+                    insertStatement.setString(4, serializedInfo);
+                    insertStatement.setLong(5, expiration.getTime());
+                    insertStatement.setLong(6, intervalDuration);
+                    int result = insertStatement.executeUpdate();
+                    if (result != 1) {
+                        throw new TimerStoreException("Could not insert!");
+                    }
+                } finally {
+                    insertStatement.close();
+                }
+                PreparedStatement identityStatement = c.prepareStatement(identitySQL);
+                try {
+                    ResultSet seqRS = identityStatement.executeQuery();
+                    try {
+                        seqRS.next();
+                        id = seqRS.getLong(1);
+                    } finally {
+                        seqRS.close();
+                    }
+                } finally {
+                    identityStatement.close();
+                }
+            }
+        } catch (SQLException e) {
+            threwException = true;
+            throw new TimerStoreException(e);
+        } finally {
+            close(c, !threwException);
+        }
+
+        TimerData timerData = new TimerData(id, timerService, deploymentId, primaryKey, info, expiration, intervalDuration);
+        return timerData;
+    }
+
+    public void removeTimer(long timerId) {
+        boolean threwException = false;
+
+        Connection c = null;
+        try {
+            c = getConnection();
+            PreparedStatement deleteStatement = c.prepareStatement(deleteSQL);
+            try {
+                deleteStatement.setLong(1, timerId);
+                deleteStatement.execute();
+            } finally {
+                deleteStatement.close();
+            }
+        } catch (TimerStoreException e) {
+            log.warning("Unable to remove get a database connection", e);
+        } catch (SQLException e) {
+            threwException = true;
+            log.warning("Unable to remove timer data from database", e);
+        } finally {
+            close(c, !threwException);
+        }
+    }
+
+    public Collection<TimerData> loadTimers(EjbTimerServiceImpl timerService, String deploymentId) throws TimerStoreException {
+        Collection<TimerData> timerDatas = new ArrayList<TimerData>();
+        boolean threwException = false;
+        Connection c = getConnection();
+        try {
+            PreparedStatement selectStatement = c.prepareStatement(selectSQL);
+            selectStatement.setString(1, serverUniqueId);
+            selectStatement.setString(2, deploymentId);
+            try {
+                ResultSet taskRS = selectStatement.executeQuery();
+                try {
+                    while (taskRS.next()) {
+                        long id = taskRS.getLong(1);
+                        String serizalizedUserId = taskRS.getString(2);
+                        Object userId = deserializeObject(serizalizedUserId);
+                        String serializedUserInfo = taskRS.getString(3);
+                        Object userInfo = deserializeObject(serializedUserInfo);
+                        long timeMillis = taskRS.getLong(4);
+                        Date time = new Date(timeMillis);
+                        long period = taskRS.getLong(5);
+
+                        TimerData timerData = new TimerData(id, timerService, deploymentId, userId, userInfo, time, period);
+                        timerDatas.add(timerData);
+                    }
+                } finally {
+                    taskRS.close();
+                }
+            } finally {
+                selectStatement.close();
+            }
+        } catch (SQLException e) {
+            threwException = true;
+            throw new TimerStoreException(e);
+        } finally {
+            close(c, !threwException);
+        }
+        return timerDatas;
+    }
+
+    public void updateIntervalTimer(TimerData timerData) {
+        boolean threwException = false;
+        Connection c = null;
+        try {
+            c = getConnection();
+            PreparedStatement updateStatement = c.prepareStatement(fixedRateUpdateSQL);
+            try {
+                updateStatement.setLong(1, timerData.getExpiration().getTime());
+                updateStatement.setLong(2, timerData.getId());
+                updateStatement.execute();
+            } finally {
+                updateStatement.close();
+            }
+        } catch (TimerStoreException e) {
+            log.warning("Unable to remove get a database connection", e);
+        } catch (SQLException e) {
+            threwException = true;
+            log.warning("Unable to remove timer data from database", e);
+        } finally {
+            close(c, !threwException);
+        }
+    }
+
+    private String serializeObject(Object object) {
+        XStream xstream = new XStream();
+        String serializaedValue = xstream.toXML(object);
+        return serializaedValue;
+    }
+
+    private Object deserializeObject(String serializedValue) {
+        XStream xstream = new XStream();
+        Object object = xstream.fromXML(serializedValue);
+        return object;
+    }
+
+    private void execSQL(String sql) throws SQLException {
+        Connection connection = dataSource.getConnection();
+        try {
+            PreparedStatement updateStatement = connection.prepareStatement(sql);
+            try {
+                updateStatement.execute();
+            } catch (SQLException e) {
+                //ignore... table already exists.
+            } finally {
+                updateStatement.close();
+            }
+        } finally {
+            connection.close();
+        }
+    }
+
+    private Connection getConnection() throws TimerStoreException {
+        try {
+            return dataSource.getConnection();
+        } catch (Exception e) {
+            throw new TimerStoreException(e);
+        }
+    }
+
+    private void close(Connection connection, boolean reportSqlException) {
+        if (connection != null) {
+            try {
+                connection.close();
+            } catch (Exception e) {
+                if (!reportSqlException) {
+                    log.warning("Unable to close database connection", e) ;
+                }
+            }
+        }
+    }
+}

Added: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerService.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerService.java?view=auto&rev=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerService.java (added)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerService.java Tue Mar  6 23:26:43 2007
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.openejb.core.timer;
+
+import org.apache.openejb.OpenEJBException;
+
+import javax.ejb.Timer;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Date;
+
+public interface EjbTimerService {
+    Timer getTimer(long id);
+
+    Collection<Timer> getTimers(Object primaryKey);
+
+    Timer createTimer(Object primaryKey, Date initialExpiration, long intervalDuration, Serializable info);
+
+    Timer createTimer(Object primaryKey, Date expiration, Serializable info);
+
+    Timer createTimer(Object primaryKey, long initialDuration, long intervalDuration, Serializable info);
+
+    Timer createTimer(Object primaryKey, long duration, Serializable info);
+
+    void start() throws OpenEJBException;
+
+    void stop();
+}

Added: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerServiceImpl.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerServiceImpl.java?view=auto&rev=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerServiceImpl.java (added)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/EjbTimerServiceImpl.java Tue Mar  6 23:26:43 2007
@@ -0,0 +1,323 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.openejb.core.timer;
+
+import org.apache.openejb.core.CoreDeploymentInfo;
+import org.apache.openejb.RpcContainer;
+import org.apache.openejb.OpenEJBException;
+import org.apache.openejb.DeploymentInfo;
+import org.apache.openejb.util.Logger;
+import org.apache.openejb.loader.SystemInstance;
+
+import javax.ejb.EJBException;
+import javax.ejb.Timer;
+import javax.transaction.Status;
+import javax.transaction.TransactionManager;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.TimerTask;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class EjbTimerServiceImpl implements EjbTimerService {
+    private static final Logger log = Logger.getInstance("Timer", "org.apache.openejb.util.resources");
+
+    private final TransactionManager transactionManager;
+    private final DeploymentInfo deployment;
+    private final boolean transacted;
+    private final int retryAttempts;
+
+    private final TimerStore timerStore;
+    private final Executor threadPool;
+
+    private java.util.Timer timer;
+
+    public EjbTimerServiceImpl(DeploymentInfo deployment) {
+        this(deployment, getDefaultTransactionManager(), getDefaultExecutor(), new MemoryTimerStore(getDefaultTransactionManager()), 1);
+    }
+
+    public static Executor getDefaultExecutor() {
+        Executor executor = SystemInstance.get().getComponent(Executor.class);
+        if (executor == null) {
+            executor = Executors.newFixedThreadPool(10);
+            SystemInstance.get().setComponent(Executor.class, executor);
+        }
+        return executor;
+    }
+
+    public static TransactionManager getDefaultTransactionManager() {
+        return SystemInstance.get().getComponent(TransactionManager.class);
+    }
+
+    public EjbTimerServiceImpl(DeploymentInfo deployment, TransactionManager transactionManager, Executor threadPool, TimerStore timerStore, int retryAttempts) {
+        if (deployment.getEjbTimeout() == null) throw new IllegalArgumentException("Ejb does not have an ejbTimeout method " + deployment.getDeploymentID());
+
+        this.deployment = deployment;
+        this.transactionManager = transactionManager;
+        this.threadPool = threadPool;
+        this.timerStore = timerStore;
+        byte txAttribute = deployment.getTransactionAttribute(deployment.getEjbTimeout());
+        this.transacted = txAttribute == CoreDeploymentInfo.TX_REQUIRED || txAttribute == CoreDeploymentInfo.TX_REQUIRES_NEW;
+        this.retryAttempts = retryAttempts;
+
+    }
+
+    public void start() throws TimerStoreException {
+        // load saved timers
+        Collection timerDatas = timerStore.loadTimers(this, (String)deployment.getDeploymentID());
+
+        // create a new java.util.Timer
+        timer = new java.util.Timer(true);
+
+        // schedule the saved timers
+        for (Iterator iterator = timerDatas.iterator(); iterator.hasNext();) {
+            TimerData timerData = (TimerData) iterator.next();
+
+            // schedule the timer with the java.util.Timer
+            schedule(timerData);
+        }
+    }
+
+    public void stop() {
+        // stop all timers
+        for (Iterator iterator = timerStore.getTimers((String)deployment.getDeploymentID()).iterator(); iterator.hasNext();) {
+            TimerData timerData = (TimerData) iterator.next();
+            timerData.stop();
+        }
+
+        // stop the java.util.Timer
+        if (timer != null) {
+            timer.cancel();
+            timer = null;
+        }
+    }
+
+    public TransactionManager getTransactionManager() {
+        return transactionManager;
+    }
+
+    /**
+     * Called from TimerData and start when a timer should be scheduled with the java.util.Timer.
+     * @param timerData the timer to schedule
+     */
+    public void schedule(TimerData timerData) {
+        if (timer == null) throw new IllegalStateException("Timer is stopped");
+
+        try {
+            EjbTimeoutTimerTask timerTask = new EjbTimeoutTimerTask(timerData);
+            timerData.setTimerTask(timerTask);
+            if (timerData.isOneTime()) {
+                timer.schedule(timerTask, timerData.getExpiration());
+            } else {
+                timer.scheduleAtFixedRate(timerTask, timerData.getExpiration(), timerData.getIntervalDuration());
+            }
+        } catch (Exception e) {
+            log.warning("Could not schedule timer " + e.getMessage() + " at (now) " + System.currentTimeMillis() + " for " + timerData.getExpiration().getTime());
+        }
+    }
+
+    /**
+     * Call back from TimerData and ejbTimeout when a timer has been cancelled (or is complete) and should be removed from stores.
+     * @param timerData the timer that was cancelled
+     */
+    public void cancelled(TimerData timerData) {
+        // make sure it was removed from the strore
+        timerStore.removeTimer(timerData.getId());
+    }
+
+    public Timer getTimer(long timerId) {
+        TimerData timerData = timerStore.getTimer((String)deployment.getDeploymentID(), timerId);
+        if (timerData != null) {
+            return timerData.getTimer();
+        } else {
+            return null;
+        }
+    }
+
+    public Collection<Timer> getTimers(Object primaryKey) {
+        Collection<Timer> timers = new ArrayList<Timer>();
+        for (Iterator iterator = timerStore.getTimers((String)deployment.getDeploymentID()).iterator(); iterator.hasNext();) {
+            TimerData timerData = (TimerData) iterator.next();
+            Timer timer = timerData.getTimer();
+            timers.add(timer);
+        }
+        return timers;
+    }
+
+    public Timer createTimer(Object primaryKey, long duration, Serializable info) throws IllegalArgumentException, IllegalStateException, EJBException {
+        if (duration < 0) throw new IllegalArgumentException("duration is negative: " + duration);
+
+        Date time = new Date(System.currentTimeMillis() + duration);
+        try {
+            TimerData timerData = createTimerData(primaryKey, time, 0, info);
+            return timerData.getTimer();
+        } catch (TimerStoreException e) {
+            throw new EJBException(e);
+        }
+    }
+
+    public Timer createTimer(Object primaryKey, long initialDuration, long intervalDuration, Serializable info) throws IllegalArgumentException, IllegalStateException, EJBException {
+        if (initialDuration < 0) throw new IllegalArgumentException("initialDuration is negative: " + initialDuration);
+        if (intervalDuration < 0) throw new IllegalArgumentException("intervalDuration is negative: " + intervalDuration);
+
+
+        Date time = new Date(System.currentTimeMillis() + initialDuration);
+        try {
+            TimerData timerData = createTimerData(primaryKey, time, intervalDuration, info);
+            return timerData.getTimer();
+        } catch (TimerStoreException e) {
+            throw new EJBException(e);
+        }
+    }
+
+    public Timer createTimer(Object primaryKey, Date expiration, Serializable info) throws IllegalArgumentException, IllegalStateException, EJBException {
+        if (expiration == null) throw new IllegalArgumentException("expiration is null");
+        if (expiration.getTime() < 0) throw new IllegalArgumentException("expiration is negative: " + expiration.getTime());
+
+        try {
+            TimerData timerData = createTimerData(primaryKey, expiration, 0, info);
+            return timerData.getTimer();
+        } catch (TimerStoreException e) {
+            throw new EJBException(e);
+        }
+    }
+
+    public Timer createTimer(Object primaryKey, Date initialExpiration, long intervalDuration, Serializable info) throws IllegalArgumentException, IllegalStateException, EJBException {
+        if (initialExpiration == null) throw new IllegalArgumentException("initialExpiration is null");
+        if (initialExpiration.getTime() < 0) throw new IllegalArgumentException("initialExpiration is negative: " + initialExpiration.getTime());
+        if (intervalDuration < 0) throw new IllegalArgumentException("intervalDuration is negative: " + intervalDuration);
+
+        try {
+            TimerData timerData = createTimerData(primaryKey, initialExpiration, intervalDuration, info);
+            return timerData.getTimer();
+        } catch (TimerStoreException e) {
+            throw new EJBException(e);
+        }
+    }
+
+    private TimerData createTimerData(Object primaryKey, Date expiration, long intervalDuration, Object info) throws TimerStoreException {
+        TimerData timerData = timerStore.createTimer(this, (String)deployment.getDeploymentID(), primaryKey, info, expiration, intervalDuration);
+
+        // mark this as a new timer... when the transaction completes it will schedule the timer
+        timerData.newTimer();
+
+        return timerData;
+    }
+
+    /**
+     * This method calls the ejbTimeout method and starts a transaction if the timeout is transacted.
+     *
+     * This method will retry failed ejbTimeout calls until retryAttempts is exceeded.
+     *
+     * @param timerData the timer to call.
+     */
+    private void ejbTimeout(TimerData timerData) {
+        try {
+            Timer timer = getTimer(timerData.getId());
+            if (timer == null) {
+                return;
+            }
+
+            for (int tries = 0; tries < retryAttempts; tries++) {
+                // if transacted, begin the transaction
+                if (transacted) {
+                    try {
+                        transactionManager.begin();
+                    } catch (Exception e) {
+                        log.warning("Exception occured while starting container transaction", e);
+                        return;
+                    }
+                }
+
+                // call the timeout method
+                try {
+                    RpcContainer container = (RpcContainer) deployment.getContainer();
+                    container.invoke(deployment.getDeploymentID(), deployment.getEjbTimeout(), new Object[] {timer}, timerData.getPrimaryKey(), null);
+                } catch (RuntimeException e) {
+                    // exception from a timer does not necessairly mean failure
+                    log.warning("RuntimeException from ejbTimeout on " + deployment.getDeploymentID(), e);
+                } catch (OpenEJBException e) {
+                    log.warning("Exception from ejbTimeout on " + deployment.getDeploymentID(), e);
+                } finally {
+                    try {
+                        if (!transacted || transactionManager.getStatus() == Status.STATUS_ACTIVE) {
+                            // clean up the timer store
+                            if (timerData.isOneTime()) {
+                                timerStore.removeTimer(timerData.getId());
+                            } else {
+                                timerData.nextTime();
+                                timerStore.updateIntervalTimer(timerData);
+                            }
+
+                            // commit the tx
+                            if (transacted) {
+                                transactionManager.commit();
+                            }
+
+                            // all is cool
+                            //noinspection ReturnInsideFinallyBlock
+                            return;
+                        } else {
+                            // tx was marked rollback, so roll it back
+                            if (transacted) {
+                                transactionManager.rollback();
+                            }
+                        }
+                    } catch (Exception e) {
+                        log.warning("Exception occured while completing container transaction", e);
+                    }
+                }
+            }
+            log.warning("Failed to execute ejbTimeout on " + timerData.getDeploymentId() + " successfully within " + retryAttempts + " attempts");
+        } catch (RuntimeException e) {
+            log.warning("RuntimeException occured while calling ejbTimeout", e);
+            throw e;
+        } catch (Error e) {
+            log.warning("Error occured while calling ejbTimeout", e);
+            throw e;
+        } finally {
+            // if this is a single action timer, mark it as cancelled
+            if (timerData.isOneTime()) {
+                cancelled(timerData);
+            }
+        }
+    }
+
+    /**
+     * The timer task registered with the java.util.Timer.  The run method of this class
+     * simply adds an execution of the ejbTimeout method to the thread pool.  It is
+     * important to use the thread pool, since the java.util.Timer is single threaded.
+     */
+    private class EjbTimeoutTimerTask extends TimerTask {
+        private final TimerData timerData;
+
+        public EjbTimeoutTimerTask(TimerData timerData) {
+            this.timerData = timerData;
+        }
+
+        public void run() {
+            threadPool.execute(new Runnable() {
+                public void run() {
+                    ejbTimeout(timerData);
+                }
+            });
+        }
+    }
+}

Added: incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/MemoryTimerStore.java
URL: http://svn.apache.org/viewvc/incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/MemoryTimerStore.java?view=auto&rev=515484
==============================================================================
--- incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/MemoryTimerStore.java (added)
+++ incubator/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/timer/MemoryTimerStore.java Tue Mar  6 23:26:43 2007
@@ -0,0 +1,218 @@
+/**
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.openejb.core.timer;
+
+import org.apache.openejb.util.Logger;
+
+import javax.transaction.RollbackException;
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+import javax.transaction.SystemException;
+import javax.transaction.Transaction;
+import javax.transaction.TransactionManager;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.Date;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class MemoryTimerStore implements TimerStore {
+    private static final Logger log = Logger.getInstance("Timer", "org.apache.openejb.util.resources");
+    private final Map<Long,TimerData> taskStore = new ConcurrentHashMap<Long,TimerData>();
+    private final Map<Transaction,TimerDataView> tasksByTransaction = new HashMap<Transaction, TimerDataView>();
+    private final AtomicLong counter = new AtomicLong(0);
+
+    private final TransactionManager transactionManager;
+
+    public MemoryTimerStore(TransactionManager transactionManager) {
+        this.transactionManager = transactionManager;
+    }
+
+    public TimerData getTimer(String deploymentId, long timerId) {
+        try {
+            TimerDataView tasks = getTasks();
+            TimerData timerData = tasks.getTasks().get(new Long(timerId));
+            return timerData;
+        } catch (TimerStoreException e) {
+            return null;
+        }
+    }
+
+    public Collection<TimerData> getTimers(String deploymentId) {
+        try {
+            TimerDataView tasks = getTasks();
+            Collection<TimerData> timerDatas = new ArrayList<TimerData>(tasks.getTasks().values());
+            return timerDatas;
+        } catch (TimerStoreException e) {
+            return Collections.emptySet();
+        }
+    }
+
+    public Collection<TimerData> loadTimers(EjbTimerServiceImpl timerService, String deploymentId) throws TimerStoreException {
+        TimerDataView tasks = getTasks();
+        Collection<TimerData> timerDatas = new ArrayList<TimerData>(tasks.getTasks().values());
+        return timerDatas;
+    }
+
+    public TimerData createTimer(EjbTimerServiceImpl timerService, String deploymentId, Object primaryKey, Object info, Date expiration, long intervalDuration) throws TimerStoreException {
+        long id = counter.incrementAndGet();
+        TimerData timerData = new TimerData(id, timerService, deploymentId, primaryKey, info, expiration, intervalDuration);
+        getTasks().addTimerData(timerData);
+        return timerData;
+    }
+
+    public void removeTimer(long id) {
+        try {
+            getTasks().removeTimerData(new Long(id));
+        } catch (TimerStoreException e) {
+            log.warning("Unable to remove timer data from memory store", e);
+        }
+    }
+
+    public void updateIntervalTimer(TimerData timerData) {
+    }
+
+    private TimerDataView getTasks() throws TimerStoreException {
+        Transaction transaction = null;
+        int status = Status.STATUS_NO_TRANSACTION;
+        try {
+            transaction = transactionManager.getTransaction();
+            if (transaction != null) {
+                status = transaction.getStatus();
+            }
+        } catch (SystemException e) {
+        }
+
+        if (status != Status.STATUS_ACTIVE && status != Status.STATUS_MARKED_ROLLBACK) {
+            return new LiveTimerDataView();
+        }
+
+        TxTimerDataView tasks = (TxTimerDataView) tasksByTransaction.get(transaction);
+        if (tasks == null) {
+            tasks = new TxTimerDataView(transaction);
+            tasksByTransaction.put(transaction, tasks);
+        }
+        return tasks;
+    }
+
+    private interface TimerDataView {
+        Map<Long,TimerData> getTasks();
+
+        void addTimerData(TimerData timerData);
+
+        void removeTimerData(Long timerId);
+    }
+
+    private class LiveTimerDataView implements TimerDataView {
+        public Map<Long,TimerData> getTasks() {
+            return new TreeMap<Long,TimerData>(taskStore);
+        }
+
+        public void addTimerData(TimerData timerData) {
+            taskStore.put(new Long(timerData.getId()), timerData);
+        }
+
+        public void removeTimerData(Long timerId) {
+            taskStore.remove(timerId);
+        }
+    }
+
+    private class TxTimerDataView implements Synchronization, TimerDataView {
+        private final Map<Long,TimerData> tasks;
+        private final Map<Long,TimerData> add = new TreeMap<Long,TimerData>();
+        private final Set<Long> remove = new TreeSet<Long>();
+
+        public TxTimerDataView(Transaction transaction) throws TimerStoreException {
+            try {
+                transaction.registerSynchronization(this);
+            } catch (RollbackException e) {
+                throw new TimerStoreException("Transaction has been rolled back");
+            } catch (SystemException e) {
+                throw new TimerStoreException("Error registering transaction synchronization callback");
+            }
+            this.tasks = new TreeMap<Long,TimerData>(taskStore);
+        }
+
+        public Map<Long,TimerData> getTasks() {
+            return Collections.unmodifiableMap(tasks);
+        }
+
+        public void addTimerData(TimerData timerData) {
+            Long timerId = new Long(timerData.getId());
+
+            // if it was previously removed...
+            if (remove.contains(timerId)) {
+                // remove it from the remove set
+                remove.remove(timerId);
+                // put the work back into the current tasks set
+                tasks.put(timerId, timerData);
+
+            } else {
+                // if it is not in the current tasks
+                if (!tasks.containsKey(timerId)) {
+                    // put it in the add set
+                    add.put(timerId, timerData);
+
+                    // put the work into the current tasks set
+                    tasks.put(timerId, timerData);
+                }
+            }
+        }
+
+        public void removeTimerData(Long timerId) {
+            // if it was previously added...
+            if (add.containsKey(timerId)) {
+                // remove it from the add set
+                add.remove(timerId);
+                // re-remove the work from the current tasks set
+                tasks.remove(timerId);
+
+            } else {
+                // if it is in the current tasks
+                if (tasks.containsKey(timerId)) {
+                    // add it in the remove set
+                    remove.add(timerId);
+
+                    // remove the work from the current tasks set
+                    tasks.remove(timerId);
+                }
+            }
+        }
+
+        public void beforeCompletion() {
+        }
+
+        public void afterCompletion(int status) {
+            // if the tx was not committed, there is nothign to update
+            if (status != Status.STATUS_COMMITTED) return;
+
+            // add the new work
+            taskStore.putAll(add);
+
+            // remove work
+            taskStore.keySet().removeAll(remove);
+
+        }
+    }
+}