[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