You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by ga...@apache.org on 2010/08/23 20:58:18 UTC
svn commit: r988248 - in /openejb/trunk/openejb3/container/openejb-core/src:
main/java/org/apache/openejb/core/stateful/
test/java/org/apache/openejb/core/stateful/
Author: gawor
Date: Mon Aug 23 18:58:17 2010
New Revision: 988248
URL: http://svn.apache.org/viewvc?rev=988248&view=rev
Log:
OPENEJB-1146: Support for stateful timeout. Periodically check for expired beans. Based on patch from Thiago Veronezi.
Added:
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTimeoutTest.java (with props)
Modified:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/Cache.java
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/Instance.java
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/SimpleCache.java
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/StatefulContainerFactory.java
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/Compat3to2Test.java
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulContainerTest.java
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSessionBeanTest.java
Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/Cache.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/Cache.java?rev=988248&r1=988247&r2=988248&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/Cache.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/Cache.java Mon Aug 23 18:58:17 2010
@@ -17,6 +17,8 @@
*/
package org.apache.openejb.core.stateful;
+import org.apache.openejb.util.Duration;
+
public interface Cache<K, V> {
/**
* Gets the listener for cache events.
@@ -60,9 +62,19 @@ public interface Cache<K, V> {
V remove(K key);
/**
- * Removes all of th entries that match the specified filter.
+ * Removes all of the entries that match the specified filter.
*/
void removeAll(CacheFilter<V> filter);
+
+ /**
+ * Initialize the cache.
+ */
+ void init();
+
+ /**
+ * Destroy the cache.
+ */
+ void destroy();
/**
* Callback listener for cache events.
@@ -99,4 +111,14 @@ public interface Cache<K, V> {
*/
boolean matches(V v);
}
+
+ /**
+ * A cache entry can implement this interface
+ * to provide its timeout.
+ */
+ public interface TimeOut {
+
+ Duration getTimeOut();
+
+ }
}
Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/Instance.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/Instance.java?rev=988248&r1=988247&r2=988248&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/Instance.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/Instance.java Mon Aug 23 18:58:17 2010
@@ -32,10 +32,11 @@ import org.apache.openejb.core.CoreDeplo
import org.apache.openejb.core.transaction.BeanTransactionPolicy.SuspendedTransaction;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.spi.ContainerSystem;
+import org.apache.openejb.util.Duration;
import org.apache.openejb.util.Index;
import org.apache.openejb.util.PojoSerialization;
-public class Instance implements Serializable {
+public class Instance implements Serializable, Cache.TimeOut {
private static final long serialVersionUID = 2862563626506556542L;
public final CoreDeploymentInfo deploymentInfo;
public final Object primaryKey;
@@ -73,6 +74,10 @@ public class Instance implements Seriali
this.entityManagerArray = entityManagerArray;
}
+ public Duration getTimeOut() {
+ return deploymentInfo.getStatefulTimeout();
+ }
+
public synchronized boolean isInUse() {
return inUse;
}
Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/SimpleCache.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/SimpleCache.java?rev=988248&r1=988247&r2=988248&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/SimpleCache.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/SimpleCache.java Mon Aug 23 18:58:17 2010
@@ -24,7 +24,11 @@ import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@@ -69,9 +73,20 @@ public class SimpleCache<K, V> implement
/**
* A bean may be destroyed if it isn't used in this length of time (in
* milliseconds).
+ * A time out of value -1 means a bean will never be destroyed due to time out.
+ * A time out of value 0 means a bean can be immediately destroyed.
*/
- private long timeOut;
+ private long timeOut = -1;
+ private ScheduledExecutorService executor;
+
+ /**
+ * Specifies how often the cache is checked for timed out beans.
+ */
+ private long frequency = 60 * 1000;
+
+ private ScheduledFuture future;
+
public SimpleCache() {
}
@@ -83,6 +98,36 @@ public class SimpleCache<K, V> implement
this.timeOut = timeOut.getTime(TimeUnit.MILLISECONDS);
}
+ public synchronized void init() {
+ if (frequency > 0 && future == null) {
+ initScheduledExecutorService();
+
+ future = executor.scheduleWithFixedDelay(new Runnable() {
+ public void run() {
+ processLRU();
+ }
+ }, frequency, frequency, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ public synchronized void destroy() {
+ if (future != null) {
+ future.cancel(false);
+ }
+ }
+
+ private synchronized void initScheduledExecutorService() {
+ if (executor == null) {
+ executor = Executors.newScheduledThreadPool(1, new ThreadFactory() {
+ public Thread newThread(Runnable runable) {
+ Thread t = new Thread(runable, "Stateful cache");
+ t.setDaemon(true);
+ return t;
+ }
+ });
+ }
+ }
+
public synchronized CacheListener<V> getListener() {
return listener;
}
@@ -132,6 +177,22 @@ public class SimpleCache<K, V> implement
this.timeOut = timeOut * 60 * 1000;
}
+ public void setScheduledExecutorService(ScheduledExecutorService executor) {
+ this.executor = executor;
+ }
+
+ public ScheduledExecutorService getScheduledExecutorService() {
+ return executor;
+ }
+
+ public void setFrequency(long frequency) {
+ this.frequency = frequency * 1000;
+ }
+
+ public long getFrequency() {
+ return frequency;
+ }
+
public void add(K key, V value) {
// find the existing entry
Entry entry = cache.get(key);
@@ -183,7 +244,7 @@ public class SimpleCache<K, V> implement
// Entry has been removed between get and lock (most likely by undeploying the EJB), simply drop the instance
return null;
}
-
+
// mark entry as in-use
entry.setState(EntryState.CHECKED_OUT);
@@ -236,7 +297,9 @@ public class SimpleCache<K, V> implement
entry.lock.unlock();
}
- processLRU();
+ if (frequency == 0) {
+ processLRU();
+ }
}
public V remove(K key) {
@@ -291,6 +354,8 @@ public class SimpleCache<K, V> implement
CacheListener<V> listener = this.getListener();
// check for timed out entries
+ // go through all lru entries since even though entries are in
+ // least recently used order they might have different timeouts.
Iterator<Entry> iterator = lru.iterator();
while (iterator.hasNext()) {
Entry entry = iterator.next();
@@ -326,10 +391,6 @@ public class SimpleCache<K, V> implement
logger.error("An unexpected exception occured from timedOut callback", e);
}
}
- } else {
- // entries are in order of last updates, so if this bean isn't timed out
- // no further entries will be timed out
- break;
}
} finally {
entry.lock.unlock();
@@ -380,7 +441,7 @@ public class SimpleCache<K, V> implement
// there is a race condition where the item could get added back into the lru
lru.remove(entry);
- // if the entry is actually timed out we just destroy it; othewise it is written to disk
+ // if the entry is actually timed out we just destroy it; otherwise it is written to disk
if (entry.isTimedOut()) {
entry.setState(EntryState.REMOVED);
if (listener != null) {
@@ -480,12 +541,21 @@ public class SimpleCache<K, V> implement
private final ReentrantLock lock = new ReentrantLock();
private EntryState state;
private long lastAccess;
+ private long timeOut;
private Entry(K key, V value, EntryState state) {
this.key = key;
this.value = value;
this.state = state;
- lastAccess = System.currentTimeMillis();
+
+ if (value instanceof Cache.TimeOut) {
+ Duration duration = ((Cache.TimeOut) value).getTimeOut();
+ this.timeOut = (duration != null) ? duration.getTime(TimeUnit.MILLISECONDS) : getTimeOut();
+ } else {
+ this.timeOut = getTimeOut();
+ }
+
+ lastAccess = System.currentTimeMillis();
}
private K getKey() {
@@ -511,18 +581,20 @@ public class SimpleCache<K, V> implement
private boolean isTimedOut() {
assertLockHeld();
- long timeOut = getTimeOut();
- if (timeOut == 0) {
+ if (timeOut < 0) {
return false;
+ } else if (timeOut == 0) {
+ return true;
+ } else {
+ long now = System.currentTimeMillis();
+ return (now - lastAccess) > timeOut;
}
- long now = System.currentTimeMillis();
- return (now - lastAccess) > timeOut;
}
private void resetTimeOut() {
assertLockHeld();
- if (getTimeOut() > 0) {
+ if (timeOut > 0) {
lastAccess = System.currentTimeMillis();
}
}
@@ -533,4 +605,5 @@ public class SimpleCache<K, V> implement
}
}
}
+
}
Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/StatefulContainerFactory.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/StatefulContainerFactory.java?rev=988248&r1=988247&r2=988248&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/StatefulContainerFactory.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/StatefulContainerFactory.java Mon Aug 23 18:58:17 2010
@@ -85,6 +85,10 @@ public class StatefulContainerFactory {
properties.put("BulkPassivate", s);
}
+ public void setFrequency(String s) {
+ properties.put("Frequency", s);
+ }
+
public Properties getProperties() {
return properties;
}
@@ -98,6 +102,7 @@ public class StatefulContainerFactory {
if (cache == null) {
buildCache();
}
+ cache.init();
return new StatefulContainer(id, securityService, cache, accessTimeout);
}
Modified: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/Compat3to2Test.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/Compat3to2Test.java?rev=988248&r1=988247&r2=988248&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/Compat3to2Test.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/Compat3to2Test.java Mon Aug 23 18:58:17 2010
@@ -63,6 +63,7 @@ public class Compat3to2Test extends Test
StatefulSessionContainerInfo statefulContainerInfo = config.configureService(StatefulSessionContainerInfo.class);
statefulContainerInfo.properties.setProperty("PoolSize", "0");
statefulContainerInfo.properties.setProperty("BulkPassivate", "1");
+ statefulContainerInfo.properties.setProperty("Frequency", "0");
assembler.createContainer(statefulContainerInfo);
EjbJar ejbJar = new EjbJar();
Modified: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulContainerTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulContainerTest.java?rev=988248&r1=988247&r2=988248&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulContainerTest.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulContainerTest.java Mon Aug 23 18:58:17 2010
@@ -224,6 +224,7 @@ public class StatefulContainerTest exten
StatefulSessionContainerInfo statefulContainerInfo = config.configureService(StatefulSessionContainerInfo.class);
statefulContainerInfo.properties.setProperty("PoolSize", "0");
statefulContainerInfo.properties.setProperty("BulkPassivate", "1");
+ statefulContainerInfo.properties.setProperty("Frequency", "0");
assembler.createContainer(statefulContainerInfo);
// Setup the descriptor information
Modified: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSessionBeanTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSessionBeanTest.java?rev=988248&r1=988247&r2=988248&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSessionBeanTest.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulSessionBeanTest.java Mon Aug 23 18:58:17 2010
@@ -59,6 +59,7 @@ public class StatefulSessionBeanTest ext
StatefulSessionContainerInfo statefulContainerInfo = config.configureService(StatefulSessionContainerInfo.class);
statefulContainerInfo.properties.setProperty("PoolSize", "0");
statefulContainerInfo.properties.setProperty("BulkPassivate", "1");
+ statefulContainerInfo.properties.setProperty("Frequency", "0");
assembler.createContainer(statefulContainerInfo);
assembler.createApplication(config.configureApplication(buildTestApp()));
Added: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTimeoutTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTimeoutTest.java?rev=988248&view=auto
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTimeoutTest.java (added)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTimeoutTest.java Mon Aug 23 18:58:17 2010
@@ -0,0 +1,160 @@
+/**
+ * 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.stateful;
+
+import java.util.concurrent.TimeUnit;
+
+import javax.ejb.Local;
+import javax.ejb.NoSuchEJBException;
+import javax.ejb.Stateful;
+import javax.naming.InitialContext;
+
+import junit.framework.TestCase;
+
+import org.apache.openejb.assembler.classic.Assembler;
+import org.apache.openejb.assembler.classic.ProxyFactoryInfo;
+import org.apache.openejb.assembler.classic.SecurityServiceInfo;
+import org.apache.openejb.assembler.classic.StatefulSessionContainerInfo;
+import org.apache.openejb.assembler.classic.TransactionServiceInfo;
+import org.apache.openejb.client.LocalInitialContextFactory;
+import org.apache.openejb.config.ConfigurationFactory;
+import org.apache.openejb.jee.EjbJar;
+import org.apache.openejb.jee.StatefulBean;
+import org.apache.openejb.jee.Timeout;
+
+public class StatefulTimeoutTest extends TestCase {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ System.setProperty(javax.naming.Context.INITIAL_CONTEXT_FACTORY,
+ LocalInitialContextFactory.class.getName());
+
+ final ConfigurationFactory config = new ConfigurationFactory();
+ final Assembler assembler = new Assembler();
+
+ assembler.createProxyFactory(config.configureService(ProxyFactoryInfo.class));
+ assembler.createTransactionManager(config.configureService(TransactionServiceInfo.class));
+ assembler.createSecurityService(config.configureService(SecurityServiceInfo.class));
+
+ final StatefulSessionContainerInfo statefulContainerInfo = config
+ .configureService(StatefulSessionContainerInfo.class);
+ statefulContainerInfo.properties.setProperty("BulkPassivate", "1");
+ // clear cache every 3 seconds
+ statefulContainerInfo.properties.setProperty("Frequency", "3");
+ assembler.createContainer(statefulContainerInfo);
+
+ EjbJar ejbJar = new EjbJar();
+ Timeout timeout;
+
+ StatefulBean bean1 = new StatefulBean("BeanNegative", MyLocalBeanImpl.class);
+ timeout = new Timeout();
+ timeout.setTimeout(-1);
+ timeout.setUnit(TimeUnit.SECONDS);
+ bean1.setStatefulTimeout(timeout);
+
+ StatefulBean bean0 = new StatefulBean("BeanZero", MyLocalBeanImpl.class);
+ timeout = new Timeout();
+ timeout.setTimeout(0);
+ timeout.setUnit(TimeUnit.SECONDS);
+ bean0.setStatefulTimeout(timeout);
+
+ StatefulBean bean5 = new StatefulBean("Bean", MyLocalBeanImpl.class);
+ timeout = new Timeout();
+ timeout.setTimeout(5);
+ timeout.setUnit(TimeUnit.SECONDS);
+ bean5.setStatefulTimeout(timeout);
+
+ ejbJar.addEnterpriseBean(bean1);
+ ejbJar.addEnterpriseBean(bean0);
+ ejbJar.addEnterpriseBean(bean5);
+
+ assembler.createApplication(config.configureApplication(ejbJar));
+ }
+
+ public void testZeroTimeout() throws Exception {
+ InitialContext ctx = new InitialContext();
+ MyLocalBean bean;
+
+ // cache is cleared ever 3 seconds and bean timeout is 0 seconds
+ bean = (MyLocalBean) ctx.lookup("BeanZeroLocal");
+ bean.doNothing(0);
+
+ // cache should be cleared by now and the bean should be removed
+ Thread.sleep(5 * 1000);
+ try {
+ bean.doNothing(0);
+ fail("Did not throw expected exception");
+ } catch (NoSuchEJBException e) {
+ // that's what we expect
+ }
+ }
+
+ public void testTimeout() throws Exception {
+ InitialContext ctx = new InitialContext();
+ MyLocalBean bean;
+
+ // cache is cleared ever 3 seconds and bean timeout is 5 seconds
+ bean = (MyLocalBean) ctx.lookup("BeanLocal");
+ bean.doNothing(0);
+
+ // cache should be cleared once by now but the bean is not expired yet
+ Thread.sleep(5 * 1000);
+ bean.doNothing(0);
+
+ // cache should be cleared again and our bean should be removed
+ // since the bean was idle for more than 5 seconds.
+ Thread.sleep(10 * 1000);
+ try {
+ bean.doNothing(0);
+ fail("Did not throw expected exception");
+ } catch (NoSuchEJBException e) {
+ // that's what we expect
+ }
+ }
+
+ public void testNegativeTimeout() throws Exception {
+ InitialContext ctx = new InitialContext();
+ MyLocalBean bean;
+
+ // cache is cleared ever 3 seconds and bean timeout is -1 seconds
+ bean = (MyLocalBean) ctx.lookup("BeanNegativeLocal");
+ bean.doNothing(0);
+
+ // cache should be cleared by now a few times but bean should remain
+ // available.
+ Thread.sleep(10 * 1000);
+ bean.doNothing(0);
+ }
+
+ @Local
+ public static interface MyLocalBean {
+ void doNothing(long sleep);
+ }
+
+ @Stateful
+ public static class MyLocalBeanImpl implements MyLocalBean {
+ public void doNothing(long sleep) {
+ try {
+ Thread.sleep(sleep);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
Propchange: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTimeoutTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTimeoutTest.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTimeoutTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain