You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ma...@apache.org on 2012/02/14 20:25:43 UTC

svn commit: r1244181 - in /commons/proper/pool/trunk/src: java/org/apache/commons/pool2/impl/ test/org/apache/commons/pool2/impl/

Author: markt
Date: Tue Feb 14 19:25:42 2012
New Revision: 1244181

URL: http://svn.apache.org/viewvc?rev=1244181&view=rev
Log:
Fix POOL-161. Patch provided by Sylvain Laurent.
Ensure the evictor thread runs with the same class loader that was in use when the pool was created to ensure that the factory class is visible.

Added:
    commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java   (with props)
    commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/test1
    commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/test2
Modified:
    commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
    commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPool.java

Modified: commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java
URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java?rev=1244181&r1=1244180&r2=1244181&view=diff
==============================================================================
--- commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java (original)
+++ commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericKeyedObjectPool.java Tue Feb 14 19:25:42 2012
@@ -236,6 +236,9 @@ public class GenericKeyedObjectPool<K,T>
             GenericKeyedObjectPoolConfig config) {
         // Copy the settings from the config
         this.factory = factory;
+        // save the current CCL to be used later by the evictor Thread
+        factoryClassLoader = Thread.currentThread().getContextClassLoader();
+
         setConfig(config);
 
         startEvictor(getMinEvictableIdleTimeMillis());
@@ -2081,24 +2084,37 @@ public class GenericKeyedObjectPool<K,T>
         /**
          * Run pool maintenance.  Evict objects qualifying for eviction and then
          * invoke {@link GenericKeyedObjectPool#ensureMinIdle()}.
+         * Since the Timer that invokes Evictors is shared for all Pools, we try
+         * to restore the ContextClassLoader that created the pool.
          */
         @Override
         public void run() {
-            //Evict from the pool
-            try {
-                evict();
-            } catch(Exception e) {
-                // ignored
-            } catch(OutOfMemoryError oome) {
-                // Log problem but give evictor thread a chance to continue in
-                // case error is recoverable
-                oome.printStackTrace(System.err);
-            }
-            //Re-create idle instances.
+            ClassLoader savedClassLoader =
+                    Thread.currentThread().getContextClassLoader();
             try {
-                ensureMinIdle();
-            } catch (Exception e) {
-                // ignored
+                //  set the class loader for the factory
+                Thread.currentThread().setContextClassLoader(
+                        factoryClassLoader);
+                
+                //Evict from the pool
+                try {
+                    evict();
+                } catch(Exception e) {
+                    // ignored
+                } catch(OutOfMemoryError oome) {
+                    // Log problem but give evictor thread a chance to continue in
+                    // case error is recoverable
+                    oome.printStackTrace(System.err);
+                }
+                //Re-create idle instances.
+                try {
+                    ensureMinIdle();
+                } catch (Exception e) {
+                    // ignored
+                }
+            } finally {
+                // restore the previous CCL
+                Thread.currentThread().setContextClassLoader(savedClassLoader);
             }
         }
     }
@@ -2265,6 +2281,13 @@ public class GenericKeyedObjectPool<K,T>
     final private KeyedPoolableObjectFactory<K,T> factory;
 
     /**
+     * Class loader for evictor thread to use since in a J2EE or similar
+     * environment the context class loader for the evictor thread may have
+     * visibility of the correct factory. See POOL-161.
+     */
+    private ClassLoader factoryClassLoader = null;
+
+    /**
      * My hash of pools (ObjectQueue). The list of keys <b>must</b> be kept in
      * step with {@link #poolKeyList} using {@link #keyLock} to ensure any
      * changes to the list of current keys is made in a thread-safe manner.

Modified: commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPool.java
URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPool.java?rev=1244181&r1=1244180&r2=1244181&view=diff
==============================================================================
--- commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPool.java (original)
+++ commons/proper/pool/trunk/src/java/org/apache/commons/pool2/impl/GenericObjectPool.java Tue Feb 14 19:25:42 2012
@@ -189,6 +189,9 @@ public class GenericObjectPool<T> extend
     public GenericObjectPool(PoolableObjectFactory<T> factory,
             GenericObjectPoolConfig config) {
         this.factory = factory;
+        // save the current CCL to be used later by the evictor Thread
+        factoryClassLoader = Thread.currentThread().getContextClassLoader();
+        
         setConfig(config);
 
         startEvictor(getTimeBetweenEvictionRunsMillis());
@@ -1417,22 +1420,35 @@ public class GenericObjectPool<T> extend
         /**
          * Run pool maintenance. Evict objects qualifying for eviction and then
          * invoke {@link GenericObjectPool#ensureMinIdle()}.
+         * Since the Timer that invokes Evictors is shared for all Pools, we try
+         * to restore the ContextClassLoader that created the pool.
          */
         @Override
         public void run() {
+            ClassLoader savedClassLoader =
+                    Thread.currentThread().getContextClassLoader();
             try {
-                evict();
-            } catch (Exception e) {
-                // ignored
-            } catch (OutOfMemoryError oome) {
-                // Log problem but give evictor thread a chance to continue in
-                // case error is recoverable
-                oome.printStackTrace(System.err);
-            }
-            try {
-                ensureMinIdle();
-            } catch (Exception e) {
-                // ignored
+                //  set the class loader for the factory
+                Thread.currentThread().setContextClassLoader(
+                        factoryClassLoader);
+                try {
+                    evict();
+                } catch (Exception e) {
+                    // ignored
+                } catch (OutOfMemoryError oome) {
+                    // Log problem but give evictor thread a chance to continue
+                    // in case error is recoverable
+                    oome.printStackTrace(System.err);
+                }
+                try {
+                    ensureMinIdle();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    // ignored
+                }
+            } finally {
+                // restore the previous CCL
+                Thread.currentThread().setContextClassLoader(savedClassLoader);
             }
         }
     }
@@ -1589,6 +1605,13 @@ public class GenericObjectPool<T> extend
     final private PoolableObjectFactory<T> factory;
 
     /**
+     * Class loader for evictor thread to use since in a J2EE or similar
+     * environment the context class loader for the evictor thread may have
+     * visibility of the correct factory. See POOL-161.
+     */
+    private ClassLoader factoryClassLoader = null;
+
+    /**
      * All of the objects currently associated with this pool in any state. It
      * excludes objects that have been destroyed. The size of
      * {@link #allObjects} will always be less than or equal to {@link

Added: commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java
URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java?rev=1244181&view=auto
==============================================================================
--- commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java (added)
+++ commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java Tue Feb 14 19:25:42 2012
@@ -0,0 +1,110 @@
+/*
+ * 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.commons.pool2.impl;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.pool2.BasePoolableObjectFactory;
+
+public class TestGenericObjectPoolClassLoaders extends TestCase {
+
+	private static final URL BASE_URL =
+	        TestGenericObjectPoolClassLoaders.class.getResource(
+	                "/org/apache/commons/pool2/impl/");
+
+	public void testContextClassLoader() throws Exception {
+
+		ClassLoader savedClassloader = Thread.currentThread()
+				.getContextClassLoader();
+
+		try {
+			CustomClassLoader cl1 = new CustomClassLoader(1);
+			Thread.currentThread().setContextClassLoader(cl1);
+			CustomClassLoaderObjectFactory factory1 =
+			        new CustomClassLoaderObjectFactory(1);
+			GenericObjectPool<URL> pool1 = new GenericObjectPool<URL>(factory1);
+			pool1.setMinIdle(1);
+			pool1.setTimeBetweenEvictionRunsMillis(100);
+			Thread.sleep(200);
+			assertEquals("Wrong number of idle objects in pool1", 1,
+			        pool1.getNumIdle());
+
+			// ---------------
+			CustomClassLoader cl2 = new CustomClassLoader(2);
+			Thread.currentThread().setContextClassLoader(cl2);
+			CustomClassLoaderObjectFactory factory2 = new CustomClassLoaderObjectFactory(
+					2);
+			GenericObjectPool<URL> pool2 = new GenericObjectPool<URL>(factory2);
+			pool2.setMinIdle(1);
+
+			pool2.addObject();
+			assertEquals("Wrong number of idle objects in pool2", 1,
+			        pool2.getNumIdle());
+			pool2.clear();
+
+			pool2.setTimeBetweenEvictionRunsMillis(100);
+			Thread.sleep(200);
+
+			assertEquals("Wrong number of  idle objects in pool2", 1,
+			        pool2.getNumIdle());
+
+		} finally {
+			Thread.currentThread().setContextClassLoader(savedClassloader);
+		}
+	}
+
+	private class CustomClassLoaderObjectFactory extends
+			BasePoolableObjectFactory<URL> {
+		private int n;
+
+		CustomClassLoaderObjectFactory(int n) {
+			this.n = n;
+		}
+
+		@Override
+        public URL makeObject() throws Exception {
+			URL url = Thread.currentThread().getContextClassLoader()
+					.getResource("test" + n);
+			if (url == null) {
+				throw new IllegalStateException("Object should not be null");
+			}
+			return url;
+		}
+
+	}
+
+	private static class CustomClassLoader extends URLClassLoader {
+		private int n;
+
+		CustomClassLoader(int n) {
+			super(new URL[] { BASE_URL });
+			this.n = n;
+		}
+
+		@Override
+        public URL findResource(String name) {
+			if (!name.endsWith(String.valueOf(n))) {
+				return null;
+			}
+
+			return super.findResource(name);
+		}
+	}
+}

Propchange: commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/TestGenericObjectPoolClassLoaders.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/test1
URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/test1?rev=1244181&view=auto
==============================================================================
--- commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/test1 (added)
+++ commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/test1 Tue Feb 14 19:25:42 2012
@@ -0,0 +1 @@
+Hello world !
\ No newline at end of file

Added: commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/test2
URL: http://svn.apache.org/viewvc/commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/test2?rev=1244181&view=auto
==============================================================================
--- commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/test2 (added)
+++ commons/proper/pool/trunk/src/test/org/apache/commons/pool2/impl/test2 Tue Feb 14 19:25:42 2012
@@ -0,0 +1 @@
+Hello world !
\ No newline at end of file