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 00:16:54 UTC

svn commit: r1243737 - in /commons/proper/pool/branches/POOL_1_X/src: changes/ java/org/apache/commons/pool/impl/ test/org/apache/commons/pool/impl/

Author: markt
Date: Mon Feb 13 23:16:54 2012
New Revision: 1243737

URL: http://svn.apache.org/viewvc?rev=1243737&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/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/TestGenericObjectPoolClassLoaders.java
    commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/test1
    commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/test2
Modified:
    commons/proper/pool/branches/POOL_1_X/src/changes/changes.xml
    commons/proper/pool/branches/POOL_1_X/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java
    commons/proper/pool/branches/POOL_1_X/src/java/org/apache/commons/pool/impl/GenericObjectPool.java

Modified: commons/proper/pool/branches/POOL_1_X/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/pool/branches/POOL_1_X/src/changes/changes.xml?rev=1243737&r1=1243736&r2=1243737&view=diff
==============================================================================
--- commons/proper/pool/branches/POOL_1_X/src/changes/changes.xml (original)
+++ commons/proper/pool/branches/POOL_1_X/src/changes/changes.xml Mon Feb 13 23:16:54 2012
@@ -22,6 +22,10 @@
   </properties>
   <body>
   <release version="1.6.1" date="?" description="">
+    <action dev="markt" type="fix" issue="POOL-161" due-to="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.
+    </action>
     <action dev="ggregory" type="fix" issue="POOL-210">
       Jar manifest.mf does not contain SVN information for Implementation-Build.
     </action>

Modified: commons/proper/pool/branches/POOL_1_X/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java
URL: http://svn.apache.org/viewvc/commons/proper/pool/branches/POOL_1_X/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java?rev=1243737&r1=1243736&r2=1243737&view=diff
==============================================================================
--- commons/proper/pool/branches/POOL_1_X/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java (original)
+++ commons/proper/pool/branches/POOL_1_X/src/java/org/apache/commons/pool/impl/GenericKeyedObjectPool.java Mon Feb 13 23:16:54 2012
@@ -609,6 +609,8 @@ public class GenericKeyedObjectPool<K, V
             long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis,
             boolean testWhileIdle, boolean lifo) {
         _factory = factory;
+        // save the current CCL to be used later by the evictor Thread
+        _factoryClassLoader = Thread.currentThread().getContextClassLoader();
         _maxActive = maxActive;
         _lifo = lifo;
         switch (whenExhaustedAction) {
@@ -1865,6 +1867,8 @@ public class GenericKeyedObjectPool<K, V
                     }
                 }
                 _factory = factory;
+                // save the current CCL to be used later by the evictor Thread
+                _factoryClassLoader = Thread.currentThread().getContextClassLoader();
             }
         }
         destroy(toDestroy, oldFactory);
@@ -2379,24 +2383,36 @@ public class GenericKeyedObjectPool<K, V
         /**
          * 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
+            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);
-            }
-            //Re-create idle instances.
-            try {
-                ensureMinIdle();
-            } catch (Exception e) {
-                // ignored
+                //set the classloader 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);
             }
         }
     }
@@ -2720,6 +2736,14 @@ public class GenericKeyedObjectPool<K, V
     private KeyedPoolableObjectFactory<K, V> _factory = null;
 
     /**
+     * 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 idle object eviction {@link TimerTask}, if any.
      */
     private Evictor _evictor = null;

Modified: commons/proper/pool/branches/POOL_1_X/src/java/org/apache/commons/pool/impl/GenericObjectPool.java
URL: http://svn.apache.org/viewvc/commons/proper/pool/branches/POOL_1_X/src/java/org/apache/commons/pool/impl/GenericObjectPool.java?rev=1243737&r1=1243736&r2=1243737&view=diff
==============================================================================
--- commons/proper/pool/branches/POOL_1_X/src/java/org/apache/commons/pool/impl/GenericObjectPool.java (original)
+++ commons/proper/pool/branches/POOL_1_X/src/java/org/apache/commons/pool/impl/GenericObjectPool.java Mon Feb 13 23:16:54 2012
@@ -573,6 +573,8 @@ public class GenericObjectPool<T> extend
             int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle,
             long softMinEvictableIdleTimeMillis, boolean lifo) {
         _factory = factory;
+        // save the current CCL to be used later by the evictor Thread
+        _factoryClassLoader = Thread.currentThread().getContextClassLoader();
         _maxActive = maxActive;
         _lifo = lifo;
         switch(whenExhaustedAction) {
@@ -1533,6 +1535,8 @@ public class GenericObjectPool<T> extend
                 _pool.clear();
             }
             _factory = factory;
+            // save the current CCL to be used later by the evictor Thread
+            _factoryClassLoader = Thread.currentThread().getContextClassLoader();
         }
         destroy(toDestroy, oldFactory);
     }
@@ -1768,22 +1772,34 @@ 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 classloader 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) {
+                    // ignored
+                }
+            } finally {
+                //restore the previous CCL
+                Thread.currentThread().setContextClassLoader(savedClassLoader);
             }
         }
     }
@@ -2066,6 +2082,13 @@ public class GenericObjectPool<T> extend
     private PoolableObjectFactory<T> _factory = null;
 
     /**
+     * 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;
+
+    /**
      * The number of objects {@link #borrowObject} borrowed
      * from the pool, but not yet returned.
      */

Added: commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/TestGenericObjectPoolClassLoaders.java
URL: http://svn.apache.org/viewvc/commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/TestGenericObjectPoolClassLoaders.java?rev=1243737&view=auto
==============================================================================
--- commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/TestGenericObjectPoolClassLoaders.java (added)
+++ commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/TestGenericObjectPoolClassLoaders.java Mon Feb 13 23:16:54 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.pool.impl;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import junit.framework.TestCase;
+
+import org.apache.commons.pool.BasePoolableObjectFactory;
+
+public class TestGenericObjectPoolClassLoaders extends TestCase {
+
+	private static final URL BASE_URL =
+	        TestGenericObjectPoolClassLoaders.class.getResource(
+	                "/org/apache/commons/pool/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);
+		}
+	}
+}

Added: commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/test1
URL: http://svn.apache.org/viewvc/commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/test1?rev=1243737&view=auto
==============================================================================
--- commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/test1 (added)
+++ commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/test1 Mon Feb 13 23:16:54 2012
@@ -0,0 +1 @@
+Hello world !
\ No newline at end of file

Added: commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/test2
URL: http://svn.apache.org/viewvc/commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/test2?rev=1243737&view=auto
==============================================================================
--- commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/test2 (added)
+++ commons/proper/pool/branches/POOL_1_X/src/test/org/apache/commons/pool/impl/test2 Mon Feb 13 23:16:54 2012
@@ -0,0 +1 @@
+Hello world !
\ No newline at end of file