[jbpm-commits] JBoss JBPM SVN: r6866 - jbpm3/branches/jbpm-3.2-soa/core/src/main/java/org/jbpm.
do-not-reply at jboss.org
do-not-reply at jboss.org
Mon Dec 13 23:58:11 EST 2010
Author: alex.guizar at jboss.com
Date: 2010-12-13 23:58:10 -0500 (Mon, 13 Dec 2010)
New Revision: 6866
Modified:
jbpm3/branches/jbpm-3.2-soa/core/src/main/java/org/jbpm/JbpmConfiguration.java
Log:
JBPM-2919 release JbpmConfiguration thread-local stacks to avoid memory leak
Modified: jbpm3/branches/jbpm-3.2-soa/core/src/main/java/org/jbpm/JbpmConfiguration.java
===================================================================
--- jbpm3/branches/jbpm-3.2-soa/core/src/main/java/org/jbpm/JbpmConfiguration.java 2010-12-14 01:36:17 UTC (rev 6865)
+++ jbpm3/branches/jbpm-3.2-soa/core/src/main/java/org/jbpm/JbpmConfiguration.java 2010-12-14 04:58:10 UTC (rev 6866)
@@ -25,6 +25,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -240,20 +242,14 @@
private static ObjectFactory defaultObjectFactory;
private static final Map instances = new HashMap();
- private static final ThreadLocal threadLocalConfigurationStack = new ThreadLocalStack();
+ private static final ThreadLocal threadLocalConfigurationStack = new ThreadLocal();
private final ObjectFactory objectFactory;
private final String resourceName;
- private transient final ThreadLocal threadLocalContextStack = new ThreadLocalStack();
+ private transient final ThreadLocal threadLocalContextStack = new ThreadLocal();
private JobExecutor jobExecutor;
private volatile boolean isClosed;
- static class ThreadLocalStack extends ThreadLocal {
- protected Object initialValue() {
- return new ArrayList();
- }
- }
-
public JbpmConfiguration(ObjectFactory objectFactory) {
this(objectFactory, null);
}
@@ -425,13 +421,11 @@
}
private DbPersistenceServiceFactory getPersistenceServiceFactory(String jbpmContextName) {
- return (DbPersistenceServiceFactory) getServiceFactory(Services.SERVICENAME_PERSISTENCE,
- jbpmContextName);
+ return (DbPersistenceServiceFactory) getServiceFactory(Services.SERVICENAME_PERSISTENCE, jbpmContextName);
}
public static ClassLoader getProcessClassLoader(ProcessDefinition processDefinition) {
- ProcessClassLoaderFactory factory =
- (ProcessClassLoaderFactory) Configs.getObject("process.class.loader.factory");
+ ProcessClassLoaderFactory factory = (ProcessClassLoaderFactory) Configs.getObject("process.class.loader.factory");
return factory.getProcessClassLoader(processDefinition);
}
@@ -538,9 +532,11 @@
}
// close remaining contexts
- List contextStack = getJbpmContextStack();
- if (!contextStack.isEmpty()) {
- log.warn("closing " + contextStack.size() + " open contexts;"
+ List contextStack = (List) threadLocalContextStack.get();
+ if (contextStack != null && !contextStack.isEmpty()) {
+ log.warn("closing "
+ + contextStack.size()
+ + " open contexts;"
+ " make sure to close JbpmContext after use");
// copy to array because JbpmContext.close() pops the context off the stack
@@ -581,14 +577,11 @@
}
static JbpmConfiguration getCurrentJbpmConfiguration() {
- List stack = getJbpmConfigurationStack();
- return stack.isEmpty() ? null : (JbpmConfiguration) stack.get(stack.size() - 1);
+ List stack = (List) threadLocalConfigurationStack.get();
+ return (stack == null || stack.isEmpty()) ? null
+ : (JbpmConfiguration) stack.get(stack.size() - 1);
}
- private static List getJbpmConfigurationStack() {
- return (List) threadLocalConfigurationStack.get();
- }
-
static void clearInstances() {
synchronized (instances) {
instances.clear();
@@ -598,29 +591,63 @@
public JbpmContext getCurrentJbpmContext() {
ensureOpen();
- List stack = getJbpmContextStack();
- return stack.isEmpty() ? null : (JbpmContext) stack.get(stack.size() - 1);
+ List stack = (List) threadLocalContextStack.get();
+ return (stack == null || stack.isEmpty()) ? null
+ : (JbpmContext) stack.get(stack.size() - 1);
}
- private List getJbpmContextStack() {
- return (List) threadLocalContextStack.get();
- }
-
void pushJbpmContext(JbpmContext jbpmContext) {
// first push the configuration
- getJbpmConfigurationStack().add(this);
+ List configStack = (List) threadLocalConfigurationStack.get();
+ if (configStack == null) {
+ configStack = new ArrayList();
+ threadLocalConfigurationStack.set(configStack);
+ }
+ configStack.add(this);
// then push the context
- getJbpmContextStack().add(jbpmContext);
+ List contextStack = (List) threadLocalContextStack.get();
+ if (contextStack == null) {
+ contextStack = new ArrayList();
+ threadLocalContextStack.set(contextStack);
+ }
+ contextStack.add(jbpmContext);
}
+ private static void remove(ThreadLocal threadLocal) {
+ try {
+ // ThreadLocal.remove does not exist in JDK 1.4.2
+ // invoke via reflection if available
+ Method removeMethod = ThreadLocal.class.getMethod("remove", null);
+ try {
+ removeMethod.invoke(threadLocal, null);
+ }
+ catch (IllegalAccessException e) {
+ // method should be public, otherwise Class.getMethod would not return it
+ throw new JbpmException(JbpmConfiguration.class + " has no access to " + removeMethod);
+ }
+ catch (InvocationTargetException e) {
+ // if remove method threw an exception, rethrow to client
+ Throwable cause = e.getCause();
+ if (cause instanceof RuntimeException) throw (RuntimeException) cause;
+ if (cause instanceof Error) throw (Error) cause;
+ throw new JbpmException(removeMethod + " threw exception", cause);
+ }
+ }
+ catch (NoSuchMethodException e) {
+ // method unavailable; just set thread local to null
+ // this will still leak the ThreadLocal itself but not the value
+ threadLocal.set(null);
+ }
+ }
+
void popJbpmContext(JbpmContext jbpmContext) {
boolean threadSafetyFlag = false;
boolean creationOrderFlag = false;
// first pop the context
- List contextStack = getJbpmContextStack();
- int contextIndex = contextStack.lastIndexOf(jbpmContext);
- if (contextIndex == -1) {
+ List contextStack = (List) threadLocalContextStack.get();
+ int contextIndex;
+ if (contextStack == null || (contextIndex = contextStack.lastIndexOf(jbpmContext)) == -1) {
threadSafetyFlag = true;
}
else {
@@ -629,12 +656,14 @@
}
// prevent context from remaining in the stack, no matter what
contextStack.remove(contextIndex);
+ // if context stack gets empty, remove thread-local variable
+ if (contextStack.isEmpty()) remove(threadLocalContextStack);
}
// then pop the configuration
- List configStack = getJbpmConfigurationStack();
- int configIndex = configStack.lastIndexOf(this);
- if (configIndex == -1) {
+ List configStack = (List) threadLocalConfigurationStack.get();
+ int configIndex;
+ if (configStack == null || (configIndex = configStack.lastIndexOf(this)) == -1) {
threadSafetyFlag = true;
}
else {
@@ -643,14 +672,18 @@
}
// prevent configuration from remaining in the stack, no matter what
configStack.remove(configIndex);
+ // if configuration stack gets empty, remove thread-local variable
+ if (configStack.isEmpty()) remove(threadLocalConfigurationStack);
}
if (threadSafetyFlag) {
- log.warn(jbpmContext + " was not closed in the thread that created it;"
+ log.warn(jbpmContext
+ + " was not closed in the thread that created it;"
+ " JbpmContext is not safe for access from multiple threads!");
}
else if (creationOrderFlag) {
- log.warn(jbpmContext + " was not closed in a block-structured manner;"
+ log.warn(jbpmContext
+ + " was not closed in a block-structured manner;"
+ " check try-finally clauses around JbpmContext blocks");
}
}
More information about the jbpm-commits
mailing list