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/25 19:48:13 UTC
svn commit: r989259 - in /openejb/trunk/openejb3/container/openejb-core/src:
main/java/org/apache/openejb/assembler/classic/
main/java/org/apache/openejb/config/ main/java/org/apache/openejb/core/
main/java/org/apache/openejb/core/stateful/ main/resour...
Author: gawor
Date: Wed Aug 25 17:48:12 2010
New Revision: 989259
URL: http://svn.apache.org/viewvc?rev=989259&view=rev
Log:
OPENEJB-1144: Discover and process @AccessTimeout annotations on methods. Support per-method @AccessTimeout and improve locking in stateful container.
Added:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodBuilder.java (with props)
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodInfo.java (with props)
Modified:
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EjbJarBuilder.java
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanInfo.java
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/EjbJarInfoBuilder.java
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/MethodContext.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/StatefulContainer.java
openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulConcurrencyTest.java
openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTransactionLockingTest.java
Added: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodBuilder.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodBuilder.java?rev=989259&view=auto
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodBuilder.java (added)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodBuilder.java Wed Aug 25 17:48:12 2010
@@ -0,0 +1,108 @@
+/**
+ * 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.assembler.classic;
+
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.ejb.LockType;
+
+import org.apache.openejb.core.CoreDeploymentInfo;
+import org.apache.openejb.core.MethodContext;
+import org.apache.openejb.util.Classes;
+import org.apache.openejb.util.Duration;
+import org.apache.openejb.util.LogCategory;
+import org.apache.openejb.util.Logger;
+import org.apache.openejb.util.SetAccessible;
+
+public class ConcurrentMethodBuilder {
+
+ public static final Logger logger = Logger.getInstance(LogCategory.OPENEJB_STARTUP, ConcurrentMethodBuilder.class.getPackage().getName());
+
+ public void build(CoreDeploymentInfo deploymentInfo, EnterpriseBeanInfo beanInfo) {
+ Class<?> clazz = deploymentInfo.getBeanClass();
+
+ for (ConcurrentMethodInfo info : beanInfo.concurrentMethodInfos) {
+ Method method;
+ try {
+ method = getMethod(clazz, info.method.methodName, toClasses(info.method.methodParams, clazz.getClassLoader()));
+ } catch (NoSuchMethodException e) {
+ // method doesn't exist
+ logger.warning("Concurrent method does not exist: "+info.method.methodName, e);
+ continue;
+ } catch (ClassNotFoundException e) {
+ logger.warning("Concurrent method param cannot be loaded.", e);
+ continue;
+ }
+
+ if (info.method.className == null || method.getDeclaringClass().getName().equals(info.method.className)) {
+ MethodContext methodContext = deploymentInfo.getMethodContext(method);
+
+ if (info.accessTimeout != null) {
+ Duration accessTimeout = new Duration(info.accessTimeout.time, TimeUnit.valueOf(info.accessTimeout.unit));
+ methodContext.setAccessTimeout(accessTimeout);
+ }
+
+ if (info.lockType != null) {
+ methodContext.setLockType(LockType.valueOf(info.lockType));
+ }
+ }
+ }
+ }
+
+ private Class<?>[] toClasses(List<String> params, ClassLoader classLoader) throws ClassNotFoundException {
+ if (params == null) {
+ return null;
+ }
+ Class<?>[] paramsArray = new Class[params.size()];
+ for (int j = 0; j < paramsArray.length; j++) {
+ String methodParam = params.get(j);
+ paramsArray[j] = Classes.forName(methodParam, classLoader);
+
+ }
+ return paramsArray;
+ }
+
+ /**
+ * Finds the nearest java.lang.reflect.Method with the given
+ * name and parameters. Callbacks can be private so class.getMethod() cannot be used. Searching
+ * starts by looking in the specified class, if the method is not found searching continues with
+ * the immediate parent and continues recurssively until the method is found or java.lang.Object
+ * is reached. If the method is not found a NoSuchMethodException is thrown.
+ *
+ * @param clazz
+ * @param methodName
+ * @param parameterTypes
+ * @return
+ * @throws NoSuchMethodException if the method is not found in this class or any of its parent classes
+ */
+ private Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
+ NoSuchMethodException original = null;
+ while (clazz != null){
+ try {
+ Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
+ return SetAccessible.on(method);
+ } catch (NoSuchMethodException e) {
+ if (original == null) original = e;
+ }
+ clazz = clazz.getSuperclass();
+ }
+ throw original;
+ }
+
+}
Propchange: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodBuilder.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodBuilder.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodBuilder.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodInfo.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodInfo.java?rev=989259&view=auto
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodInfo.java (added)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodInfo.java Wed Aug 25 17:48:12 2010
@@ -0,0 +1,27 @@
+/**
+ * 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.assembler.classic;
+
+public class ConcurrentMethodInfo extends InfoObject {
+
+ public TimeoutInfo accessTimeout;
+
+ public NamedMethodInfo method;
+
+ public String lockType;
+
+}
Propchange: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodInfo.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodInfo.java
------------------------------------------------------------------------------
svn:keywords = Date Revision
Propchange: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/ConcurrentMethodInfo.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EjbJarBuilder.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EjbJarBuilder.java?rev=989259&r1=989258&r2=989259&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EjbJarBuilder.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EjbJarBuilder.java Wed Aug 25 17:48:12 2010
@@ -58,6 +58,8 @@ public class EjbJarBuilder {
InterceptorBindingBuilder interceptorBindingBuilder = new InterceptorBindingBuilder(context.getClassLoader(), ejbJar);
MethodScheduleBuilder methodScheduleBuilder = new MethodScheduleBuilder();
+
+ ConcurrentMethodBuilder concurrentMethodBuilder = new ConcurrentMethodBuilder();
for (EnterpriseBeanInfo ejbInfo : ejbJar.enterpriseBeans) {
try {
@@ -67,6 +69,8 @@ public class EjbJarBuilder {
interceptorBindingBuilder.build(deployment, ejbInfo);
methodScheduleBuilder.build(deployment, ejbInfo);
+
+ concurrentMethodBuilder.build(deployment, ejbInfo);
deployments.put(ejbInfo.ejbDeploymentId, deployment);
Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanInfo.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanInfo.java?rev=989259&r1=989258&r2=989259&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanInfo.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/assembler/classic/EnterpriseBeanInfo.java Wed Aug 25 17:48:12 2010
@@ -83,4 +83,5 @@ public abstract class EnterpriseBeanInfo
public TimeoutInfo statefulTimeout;
public TimeoutInfo accessTimeout;
public List<MethodScheduleInfo> methodScheduleInfos = new ArrayList<MethodScheduleInfo>();
+ public final List<ConcurrentMethodInfo> concurrentMethodInfos = new ArrayList<ConcurrentMethodInfo>();
}
Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java?rev=989259&r1=989258&r2=989259&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/AnnotationDeployer.java Wed Aug 25 17:48:12 2010
@@ -65,6 +65,8 @@ import javax.ejb.Init;
import javax.ejb.Local;
import javax.ejb.LocalBean;
import javax.ejb.LocalHome;
+import javax.ejb.Lock;
+import javax.ejb.LockType;
import javax.ejb.MessageDriven;
import javax.ejb.PostActivate;
import javax.ejb.PrePassivate;
@@ -108,6 +110,7 @@ import org.apache.openejb.jee.AroundTime
import org.apache.openejb.jee.AssemblyDescriptor;
import org.apache.openejb.jee.ConcurrencyManagementType;
import org.apache.openejb.jee.ConcurrentLockType;
+import org.apache.openejb.jee.ConcurrentMethod;
import org.apache.openejb.jee.ContainerConcurrency;
import org.apache.openejb.jee.ContainerTransaction;
import org.apache.openejb.jee.EjbJar;
@@ -1098,7 +1101,7 @@ public class AnnotationDeployer implemen
// Merge AccessTimeout value from XML - XML value takes precedence
if(sessionBean.getAccessTimeout() == null) {
- final AccessTimeout annotation = clazz.getAnnotation(AccessTimeout.class);
+ final AccessTimeout annotation = getInheritableAnnotation(clazz, AccessTimeout.class);
if(annotation != null) {
final Timeout timeout = new Timeout();
timeout.setTimeout(annotation.value());
@@ -1109,7 +1112,7 @@ public class AnnotationDeployer implemen
// Merge StatefulTimeout value from XML - XML value takes precedence
if(sessionBean.getStatefulTimeout() == null) {
- final StatefulTimeout annotation = clazz.getAnnotation(StatefulTimeout.class);
+ final StatefulTimeout annotation = getInheritableAnnotation(clazz, StatefulTimeout.class);
if(annotation != null) {
final Timeout timeout = new Timeout();
timeout.setTimeout(annotation.value());
@@ -1117,6 +1120,12 @@ public class AnnotationDeployer implemen
sessionBean.setStatefulTimeout(timeout);
}
}
+
+ /*
+ * @AccessTimeout
+ * @Lock
+ */
+ processAccessTimeoutAndLock(sessionBean, inheritedClassFinder);
}
@@ -2253,6 +2262,64 @@ public class AnnotationDeployer implemen
}
}
+ private ConcurrentMethod findConcurrentMethod(Map<NamedMethod, ConcurrentMethod> methodMap, Method method) {
+ // step 1: lookup by method name and parameters
+ NamedMethod lookup = new NamedMethod(method);
+ ConcurrentMethod concurrentMethod = methodMap.get(lookup);
+ if (concurrentMethod == null) {
+ // step 2: lookup by method name only
+ lookup.setMethodParams(null);
+ concurrentMethod = methodMap.get(lookup);
+ }
+ return concurrentMethod;
+ }
+
+ private void processAccessTimeoutAndLock(SessionBean bean, ClassFinder classFinder) {
+ Map<NamedMethod, ConcurrentMethod> methodMap = new HashMap<NamedMethod, ConcurrentMethod>();
+ for (ConcurrentMethod concurrentMethod : bean.getConcurrentMethod()) {
+ methodMap.put(concurrentMethod.getMethod(), concurrentMethod);
+ }
+
+ for (Method method : classFinder.findAnnotatedMethods(AccessTimeout.class)) {
+ ConcurrentMethod concurrentMethod = findConcurrentMethod(methodMap, method);
+ if (concurrentMethod == null) {
+ // create new and add to the list
+ concurrentMethod = new ConcurrentMethod();
+ concurrentMethod.setMethod(new NamedMethod(method));
+ bean.getConcurrentMethod().add(concurrentMethod);
+ }
+
+ AccessTimeout annotation = method.getAnnotation(AccessTimeout.class);
+
+ if (concurrentMethod.getAccessTimeout() == null) {
+ Timeout timeout = new Timeout();
+ timeout.setTimeout(annotation.value());
+ timeout.setUnit(annotation.unit());
+ concurrentMethod.setAccessTimeout(timeout);
+ }
+ }
+
+ for (Method method : classFinder.findAnnotatedMethods(Lock.class)) {
+ ConcurrentMethod concurrentMethod = findConcurrentMethod(methodMap, method);
+ if (concurrentMethod == null) {
+ // create new and add to the list
+ concurrentMethod = new ConcurrentMethod();
+ concurrentMethod.setMethod(new NamedMethod(method));
+ bean.getConcurrentMethod().add(concurrentMethod);
+ }
+
+ Lock annotation = method.getAnnotation(Lock.class);
+
+ if (concurrentMethod.getLock() == null) {
+ if (LockType.READ.equals(annotation.value())) {
+ concurrentMethod.setLock(ConcurrentLockType.READ);
+ } else if (LockType.WRITE.equals(annotation.value())) {
+ concurrentMethod.setLock(ConcurrentLockType.WRITE);
+ }
+ }
+ }
+ }
+
private void processCallbacks(Lifecycle bean, ClassFinder classFinder) {
/*
* @PostConstruct
Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/EjbJarInfoBuilder.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/EjbJarInfoBuilder.java?rev=989259&r1=989258&r2=989259&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/EjbJarInfoBuilder.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/config/EjbJarInfoBuilder.java Wed Aug 25 17:48:12 2010
@@ -25,10 +25,14 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+
+import javax.ejb.LockType;
+
import org.apache.openejb.OpenEJBException;
import org.apache.openejb.assembler.classic.ApplicationExceptionInfo;
import org.apache.openejb.assembler.classic.CallbackInfo;
import org.apache.openejb.assembler.classic.CmrFieldInfo;
+import org.apache.openejb.assembler.classic.ConcurrentMethodInfo;
import org.apache.openejb.assembler.classic.EjbJarInfo;
import org.apache.openejb.assembler.classic.EnterpriseBeanInfo;
import org.apache.openejb.assembler.classic.EntityBeanInfo;
@@ -60,6 +64,8 @@ import org.apache.openejb.jee.CallbackMe
import org.apache.openejb.jee.CmpField;
import org.apache.openejb.jee.CmpVersion;
import org.apache.openejb.jee.ConcurrencyManagementType;
+import org.apache.openejb.jee.ConcurrentLockType;
+import org.apache.openejb.jee.ConcurrentMethod;
import org.apache.openejb.jee.ContainerConcurrency;
import org.apache.openejb.jee.ContainerTransaction;
import org.apache.openejb.jee.EjbRelation;
@@ -525,6 +531,8 @@ public class EjbJarInfoBuilder {
remove.retainIfException = removeMethod.getRetainIfException();
stateful.removeMethods.add(remove);
}
+
+ copyConcurrentMethods(s.getConcurrentMethod(), bean.concurrentMethodInfos);
} else if (s.getSessionType() == SessionType.MANAGED) {
bean = new ManagedBeanInfo();
@@ -550,6 +558,8 @@ public class EjbJarInfoBuilder {
copySchedules(s.getTimer(), bean.methodScheduleInfos);
// See JndiEncInfoBuilder.buildDependsOnRefs for processing of DependsOn
// bean.dependsOn.addAll(s.getDependsOn());
+
+ copyConcurrentMethods(s.getConcurrentMethod(), bean.concurrentMethodInfos);
} else {
bean = new StatelessBeanInfo();
copySchedules(s.getTimer(), bean.methodScheduleInfos);
@@ -595,23 +605,21 @@ public class EjbJarInfoBuilder {
bean.serviceEndpoint = s.getServiceEndpoint();
bean.properties.putAll(d.getProperties());
- final Timeout statefulTimeout = s.getStatefulTimeout();
- if(statefulTimeout != null) {
- bean.statefulTimeout = new TimeoutInfo();
- bean.statefulTimeout.time = statefulTimeout.getTimeout();
- bean.statefulTimeout.unit = statefulTimeout.getUnit().toString();
- }
-
- final Timeout accessTimeout = s.getAccessTimeout();
- if(accessTimeout != null) {
- bean.accessTimeout = new TimeoutInfo();
- bean.accessTimeout.time = accessTimeout.getTimeout();
- bean.accessTimeout.unit = accessTimeout.getUnit().toString();
- }
+ bean.statefulTimeout = toInfo(s.getStatefulTimeout());
+ bean.accessTimeout = toInfo(s.getAccessTimeout());
return bean;
}
+ private TimeoutInfo toInfo(Timeout timeout) {
+ if (timeout == null) return null;
+
+ TimeoutInfo accessTimeout = new TimeoutInfo();
+ accessTimeout.time = timeout.getTimeout();
+ accessTimeout.unit = timeout.getUnit().toString();
+ return accessTimeout;
+ }
+
private EnterpriseBeanInfo initMessageBean(MessageDrivenBean mdb, Map m) throws OpenEJBException {
MessageDrivenBeanInfo bean = new MessageDrivenBeanInfo();
@@ -781,4 +789,16 @@ public class EjbJarInfoBuilder {
}
return bean;
}
+
+ private void copyConcurrentMethods(List<ConcurrentMethod> from, List<ConcurrentMethodInfo> to) {
+ for (ConcurrentMethod method : from) {
+ ConcurrentMethodInfo methodInfo = new ConcurrentMethodInfo();
+ methodInfo.accessTimeout = toInfo(method.getAccessTimeout());
+ methodInfo.method = toInfo(method.getMethod());
+ if (method.getLock() != null) {
+ methodInfo.lockType = method.getLock().value().toUpperCase();
+ }
+ to.add(methodInfo);
+ }
+ }
}
Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/MethodContext.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/MethodContext.java?rev=989259&r1=989258&r2=989259&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/MethodContext.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/MethodContext.java Wed Aug 25 17:48:12 2010
@@ -48,7 +48,7 @@ public class MethodContext {
}
public Duration getAccessTimeout() {
- return accessTimeout != null? accessTimeout: beanContext.getAccessTimeout();
+ return accessTimeout;
}
public CoreDeploymentInfo getBeanContext() {
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=989259&r1=989258&r2=989259&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 Wed Aug 25 17:48:12 2010
@@ -46,7 +46,7 @@ public class Instance implements Seriali
private boolean inUse;
private SuspendedTransaction beanTransaction;
private Stack<Transaction> transaction = new Stack<Transaction>();
- private final Lock lock = new ReentrantLock();
+ private final ReentrantLock lock = new ReentrantLock();
// todo if we keyed by an entity manager factory id we would not have to make this transient and rebuild the index below
// This would require that we crete an id and that we track it
@@ -112,9 +112,14 @@ public class Instance implements Seriali
} else if (transaction != null){
this.transaction.push(transaction);
}
-
}
+ public synchronized void releaseLock() {
+ if (lock.isHeldByCurrentThread()) {
+ lock.unlock();
+ }
+ }
+
public synchronized Map<EntityManagerFactory, EntityManager> getEntityManagers(Index<EntityManagerFactory, Map> factories) {
if (entityManagers == null && entityManagerArray != null) {
entityManagers = new HashMap<EntityManagerFactory, EntityManager>();
Modified: openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/StatefulContainer.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/StatefulContainer.java?rev=989259&r1=989258&r2=989259&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/StatefulContainer.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/core/stateful/StatefulContainer.java Wed Aug 25 17:48:12 2010
@@ -61,6 +61,8 @@ import org.apache.openejb.core.CoreDeplo
import org.apache.openejb.core.ExceptionType;
import static org.apache.openejb.core.ExceptionType.APPLICATION_ROLLBACK;
import static org.apache.openejb.core.ExceptionType.SYSTEM;
+
+import org.apache.openejb.core.MethodContext;
import org.apache.openejb.core.Operation;
import org.apache.openejb.core.ThreadContext;
import org.apache.openejb.core.InstanceContext;
@@ -107,7 +109,7 @@ public class StatefulContainer implement
private final SessionContext sessionContext;
public StatefulContainer(Object id, SecurityService securityService, Cache<Object, Instance> cache) {
- this(id, securityService, cache, new Duration(0, TimeUnit.MILLISECONDS));
+ this(id, securityService, cache, new Duration(-1, TimeUnit.MILLISECONDS));
}
public StatefulContainer(Object id, SecurityService securityService, Cache<Object, Instance> cache, Duration accessTimeout) {
@@ -468,7 +470,7 @@ public class StatefulContainer implement
Method runMethod = null;
try {
// Obtain instance
- instance = obtainInstance(primKey, callContext);
+ instance = obtainInstance(primKey, callContext, callMethod);
// Resume previous Bean transaction if there was one
if (txPolicy instanceof BeanTransactionPolicy){
@@ -568,7 +570,7 @@ public class StatefulContainer implement
Instance instance = null;
try {
// Obtain instance
- instance = obtainInstance(primKey, callContext);
+ instance = obtainInstance(primKey, callContext, callMethod);
// Resume previous Bean transaction if there was one
if (txPolicy instanceof BeanTransactionPolicy){
@@ -610,7 +612,7 @@ public class StatefulContainer implement
}
}
- private Instance obtainInstance(Object primaryKey, ThreadContext callContext) throws OpenEJBException {
+ private Instance obtainInstance(Object primaryKey, ThreadContext callContext, Method callMethod) throws OpenEJBException {
if (primaryKey == null) {
throw new SystemException(new NullPointerException("Cannot obtain an instance of the stateful session bean with a null session id"));
}
@@ -618,37 +620,42 @@ public class StatefulContainer implement
Transaction currentTransaction = getTransaction(callContext);
// Find the instance
- Instance instance = checkedOutInstances.get(primaryKey);
- if (instance == null) {
- try {
- instance = cache.checkOut(primaryKey);
- } catch (OpenEJBException e) {
- throw e;
- } catch (Exception e) {
- throw new SystemException("Unexpected load exception", e);
- }
-
- // Did we find the instance?
+ Instance instance;
+ synchronized (primaryKey) {
+ instance = checkedOutInstances.get(primaryKey);
if (instance == null) {
- throw new InvalidateReferenceException(new NoSuchObjectException("Not Found"));
- }
-
- // remember instance until it is returned to the cache
- checkedOutInstances.put(primaryKey, instance);
- }
+ try {
+ instance = cache.checkOut(primaryKey);
+ } catch (OpenEJBException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new SystemException("Unexpected load exception", e);
+ }
+ // Did we find the instance?
+ if (instance == null) {
+ throw new InvalidateReferenceException(new NoSuchObjectException("Not Found"));
+ }
- Duration accessTimeout = instance.deploymentInfo.getAccessTimeout();
- if (accessTimeout == null) accessTimeout = this.accessTimeout;
+
+ // remember instance until it is returned to the cache
+ checkedOutInstances.put(primaryKey, instance);
+ }
+ }
+
+ Duration accessTimeout = getAccessTimeout(instance.deploymentInfo, callMethod);
final Lock currLock = instance.getLock();
- final boolean lockAcquired;
- if(accessTimeout == null) {
- // returns immediately true if the lock is available
+ final boolean lockAcquired;
+ if (accessTimeout == null || accessTimeout.getTime() < 0) {
+ // wait indefinitely for a lock
+ currLock.lock();
+ lockAcquired = true;
+ } else if (accessTimeout.getTime() == 0) {
+ // concurrent calls are not allowed, lock only once
lockAcquired = currLock.tryLock();
} else {
- // AccessTimeout annotation found.
- // Trying to get the lock within the specified period.
+ // try to get a lock within the specified period.
try {
lockAcquired = currLock.tryLock(accessTimeout.getTime(), accessTimeout.getUnit());
} catch (InterruptedException e) {
@@ -657,14 +664,14 @@ public class StatefulContainer implement
}
// Did we acquire the lock to the current execution?
if (!lockAcquired) {
- throw new ApplicationException(new ConcurrentAccessTimeoutException("Unable to get lock."));
+ throw new ApplicationException(new ConcurrentAccessTimeoutException("Unable to get lock."));
}
- if (instance.getTransaction() != null){
+ if (instance.getTransaction() != null) {
if (!instance.getTransaction().equals(currentTransaction) && !instance.getLock().tryLock()) {
throw new ApplicationException(new RemoteException("Instance is in a transaction and cannot be invoked outside that transaction. See EJB 3.0 Section 4.4.4"));
}
- } else {
+ } else {
instance.setTransaction(currentTransaction);
}
@@ -673,6 +680,19 @@ public class StatefulContainer implement
return instance;
}
+ private Duration getAccessTimeout(CoreDeploymentInfo deploymentInfo, Method callMethod) {
+ Duration accessTimeout = null;
+ callMethod = deploymentInfo.getMatchingBeanMethod(callMethod);
+ MethodContext methodContext = deploymentInfo.getMethodContext(callMethod);
+ if (methodContext != null) {
+ accessTimeout = methodContext.getAccessTimeout();
+ }
+ if (accessTimeout == null) {
+ accessTimeout = deploymentInfo.getAccessTimeout();
+ }
+ return (accessTimeout == null) ? this.accessTimeout : accessTimeout;
+ }
+
private Transaction getTransaction(ThreadContext callContext) {
TransactionPolicy policy = callContext.getTransactionPolicy();
@@ -698,11 +718,13 @@ public class StatefulContainer implement
instance.setInUse(false);
if (instance.getTransaction() == null) {
- // return to cache
- cache.checkIn(instance.primaryKey);
+ synchronized (instance.primaryKey) {
+ // return to cache
+ cache.checkIn(instance.primaryKey);
- // no longer checked out
- checkedOutInstances.remove(instance.primaryKey);
+ // no longer checked out
+ checkedOutInstances.remove(instance.primaryKey);
+ }
}
}
@@ -757,6 +779,9 @@ public class StatefulContainer implement
instance.setInUse(false);
}
EjbTransactionUtil.afterInvoke(txPolicy, callContext);
+ if (instance != null) {
+ instance.releaseLock();
+ }
}
}
Modified: openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml?rev=989259&r1=989258&r2=989259&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/main/resources/META-INF/org.apache.openejb/service-jar.xml Wed Aug 25 17:48:12 2010
@@ -353,7 +353,7 @@
# seconds, minutes, hours, days. Or any combination such as
# "1 hour and 27 minutes and 10 seconds"
- AccessTimeout = 0 milliseconds
+ AccessTimeout = -1 milliseconds
# The cache is responsible for managing stateful bean
# instances. The cache can page instances to disk as memory
Modified: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulConcurrencyTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulConcurrencyTest.java?rev=989259&r1=989258&r2=989259&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulConcurrencyTest.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulConcurrencyTest.java Wed Aug 25 17:48:12 2010
@@ -16,7 +16,16 @@
*/
package org.apache.openejb.core.stateful;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import javax.ejb.ConcurrentAccessTimeoutException;
+import javax.ejb.Local;
+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;
@@ -24,16 +33,9 @@ import org.apache.openejb.assembler.clas
import org.apache.openejb.assembler.classic.TransactionServiceInfo;
import org.apache.openejb.client.LocalInitialContextFactory;
import org.apache.openejb.config.ConfigurationFactory;
-import org.apache.openejb.jee.Timeout;
import org.apache.openejb.jee.EjbJar;
import org.apache.openejb.jee.StatefulBean;
-
-import javax.ejb.ConcurrentAccessTimeoutException;
-import javax.ejb.Local;
-import javax.ejb.Stateful;
-import javax.naming.InitialContext;
-import java.util.concurrent.CyclicBarrier;
-import java.util.concurrent.TimeUnit;
+import org.apache.openejb.jee.Timeout;
public class StatefulConcurrencyTest extends TestCase {
@@ -51,108 +53,104 @@ public class StatefulConcurrencyTest ext
assembler.createSecurityService(config.configureService(SecurityServiceInfo.class));
final StatefulSessionContainerInfo statefulContainerInfo = config.configureService(StatefulSessionContainerInfo.class);
- statefulContainerInfo.properties.setProperty("BulkPassivate", "1");
assembler.createContainer(statefulContainerInfo);
final EjbJar ejbJar = new EjbJar();
- final StatefulBean bean = new StatefulBean(MyLocalBeanImpl.class);
-
- final Timeout timeout = new Timeout();
- timeout.setTimeout(1000);
- timeout.setUnit(TimeUnit.MILLISECONDS);
- bean.setAccessTimeout(timeout);
+
+ StatefulBean bean1 = new StatefulBean(MyLocalBeanImpl.class);
+ Timeout timeout1 = new Timeout();
+ timeout1.setTimeout(1000);
+ timeout1.setUnit(TimeUnit.MILLISECONDS);
+ bean1.setAccessTimeout(timeout1);
+
+ StatefulBean bean2 = new StatefulBean("BeanNegative", MyLocalBeanImpl.class);
+ Timeout timeout2 = new Timeout();
+ timeout2.setTimeout(-1);
+ timeout2.setUnit(TimeUnit.MILLISECONDS);
+ bean2.setAccessTimeout(timeout2);
- ejbJar.addEnterpriseBean(bean);
+ ejbJar.addEnterpriseBean(bean1);
+ ejbJar.addEnterpriseBean(bean2);
assembler.createApplication(config.configureApplication(ejbJar));
}
public void testConcurrentMethodCall() throws Exception {
+ MyLocalBeanImpl.semaphore = new Semaphore(0);
+
InitialContext ctx = new InitialContext();
MyLocalBean bean = (MyLocalBean) ctx.lookup("MyLocalBeanImplLocal");
MyLocalBean bean2 = (MyLocalBean) ctx.lookup("MyLocalBeanImplLocal");
+
+ CallRentrantThread call = new CallRentrantThread(bean, 3000);
+ (new Thread(call)).start();
+
+ // ensure the call on thread came in
+ assertTrue(MyLocalBeanImpl.semaphore.tryAcquire(1, 30, TimeUnit.SECONDS));
+
+ try {
+ bean2.callRentrant(bean, 0);
+ fail("Expected exception");
+ } catch (Exception e) {
+ if (e.getCause() instanceof ConcurrentAccessTimeoutException) {
+ // that's what we want
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ public void testNegativeAccessTimeout() throws Exception {
+ MyLocalBeanImpl.semaphore = new Semaphore(0);
+
+ InitialContext ctx = new InitialContext();
+ MyLocalBean bean = (MyLocalBean) ctx.lookup("BeanNegativeLocal");
+
+ CallRentrantThread call = new CallRentrantThread(bean, 3000);
+ (new Thread(call)).start();
+
+ // ensure the call on thread came in
+ assertTrue(MyLocalBeanImpl.semaphore.tryAcquire(1, 30, TimeUnit.SECONDS));
- boolean error = bean.method1(bean, 2000);
- assertTrue(error);
-
- error = bean2.method1(bean, 500);
- assertFalse(error);
+ bean.callRentrant(bean, 0);
}
@Local
public static interface MyLocalBean {
- boolean method1(MyLocalBean bean, long sleep);
-
- void method2(CyclicBarrier barrier);
+ void callRentrant(MyLocalBean myself, long sleep);
+ void sleep(long sleep);
}
@Stateful
public static class MyLocalBeanImpl implements MyLocalBean {
+ public static Semaphore semaphore;
+
+ public void callRentrant(MyLocalBean myself, long sleep) {
+ semaphore.release();
+ myself.sleep(sleep);
+ }
- public boolean method1(MyLocalBean bean, long sleep) {
- System.out.println("Method 1 invoked! Thread: " + Thread.currentThread().getName());
-
- CyclicBarrier barrier = new CyclicBarrier(1);
- MyRunningMethod method = new MyRunningMethod(barrier, bean);
- Thread thread = new Thread(method);
- thread.setName("MyRunningMethodThread");
- thread.start();
+ public void sleep(long sleep) {
try {
Thread.sleep(sleep);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
-
- try {
- barrier.await();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
-
- return method.isError();
- }
-
- public void method2(CyclicBarrier barrier) {
- System.out.println("Method 2 invoked! Thread: "
- + Thread.currentThread().getName());
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- try {
- barrier.await();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
}
}
-
- public static class MyRunningMethod implements Runnable {
+
+ public class CallRentrantThread implements Runnable {
private final MyLocalBean bean;
- private final CyclicBarrier barrier;
-
- private boolean error;
-
- public MyRunningMethod(CyclicBarrier barrier, MyLocalBean bean) {
- super();
+ private final long sleep;
+
+ public CallRentrantThread(MyLocalBean bean, long sleep) {
this.bean = bean;
- this.barrier = barrier;
- }
-
- public boolean isError() {
- return error;
+ this.sleep = sleep;
}
public void run() {
- try {
- bean.method2(barrier);
- error = false;
- } catch (ConcurrentAccessTimeoutException e) {
- error = true;
- }
+ bean.callRentrant(bean, sleep);
}
}
-
}
\ No newline at end of file
Modified: openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTransactionLockingTest.java
URL: http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTransactionLockingTest.java?rev=989259&r1=989258&r2=989259&view=diff
==============================================================================
--- openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTransactionLockingTest.java (original)
+++ openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/core/stateful/StatefulTransactionLockingTest.java Wed Aug 25 17:48:12 2010
@@ -59,9 +59,12 @@ public class StatefulTransactionLockingT
assembler.createTransactionManager(config.configureService(TransactionServiceInfo.class));
assembler.createSecurityService(config.configureService(SecurityServiceInfo.class));
+
+ StatefulSessionContainerInfo statefulContainerInfo = config.configureService(StatefulSessionContainerInfo.class);
+ statefulContainerInfo.properties.setProperty("AccessTimeout", "0 milliseconds");
// containers
- assembler.createContainer(config.configureService(StatefulSessionContainerInfo.class));
+ assembler.createContainer(statefulContainerInfo);
// Setup the descriptor information