Better class loader control when embedding
authormarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Wed, 12 Jan 2011 18:13:11 +0000 (18:13 +0000)
committermarkt <markt@13f79535-47bb-0310-9956-ffa450edef68>
Wed, 12 Jan 2011 18:13:11 +0000 (18:13 +0000)
git-svn-id: https://svn.apache.org/repos/asf/tomcat/trunk@1058259 13f79535-47bb-0310-9956-ffa450edef68

java/org/apache/catalina/Container.java
java/org/apache/catalina/Server.java
java/org/apache/catalina/Service.java
java/org/apache/catalina/core/StandardServer.java
java/org/apache/catalina/core/StandardService.java
test/org/apache/catalina/startup/TestTomcatClassLoader.java [new file with mode: 0644]
webapps/docs/changelog.xml

index 113030f..37102db 100644 (file)
@@ -282,14 +282,16 @@ public interface Container extends Lifecycle {
 
 
     /**
-     * Return the parent class loader (if any) for web applications.
+     * Return the parent class loader for this component. If not set, return
+     * {@link Container#getParent()#getParentClassLoader()}. If no parent has
+     * been set, return the system class loader.
      */
     public ClassLoader getParentClassLoader();
 
 
     /**
-     * Set the parent class loader (if any) for web applications.
-     * This call is meaningful only <strong>before</strong> a Loader has
+     * Set the parent class loader for this component. For {@link Context}s
+     * this call is meaningful only <strong>before</strong> a Loader has
      * been configured, and the specified value (if non-null) should be
      * passed as an argument to the class loader constructor.
      *
index f01ffa1..c6f8d2b 100644 (file)
@@ -118,10 +118,20 @@ public interface Server extends Lifecycle {
 
     
     /**
-     * Return the parent class loader.
+     * Return the parent class loader for this component. If not set, return
+     * {@link Server#getCatalina()#getParentClassLoader(). If no catalina has
+     * been set, return the system class loader.
      */
     public ClassLoader getParentClassLoader();
 
+
+    /**
+     * Set the parent class loader for this server.
+     *
+     * @param parent The new parent class loader
+     */
+    public void setParentClassLoader(ClassLoader parent);
+
     
     /**
      * Return the outer Catalina startup/shutdown component if present.
index a31f19e..2ce5036 100644 (file)
@@ -85,10 +85,19 @@ public interface Service extends Lifecycle {
     public void setServer(Server server);
 
     /**
-     * Return the parent class loader.
+     * Return the parent class loader for this component. If not set, return
+     * {@link Service#getServer()#getParentClassLoader(). If no server has
+     * been set, return the system class loader.
      */
     public ClassLoader getParentClassLoader();
-    
+
+    /**
+     * Set the parent class loader for this service.
+     *
+     * @param parent The new parent class loader
+     */
+    public void setParentClassLoader(ClassLoader parent);
+
     // --------------------------------------------------------- Public Methods
 
 
index 28a21d6..7b3d39e 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-
 package org.apache.catalina.core;
 
-
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
 import java.io.IOException;
@@ -56,8 +53,7 @@ import org.apache.tomcat.util.res.StringManager;
  * @author Craig R. McClanahan
  * @version $Id$
  */
-public final class StandardServer extends LifecycleMBeanBase
-        implements Server {
+public final class StandardServer extends LifecycleMBeanBase implements Server {
 
     private static final Log log = LogFactory.getLog(StandardServer.class);
    
@@ -159,6 +155,8 @@ public final class StandardServer extends LifecycleMBeanBase
     
     private Catalina catalina = null;
 
+    private ClassLoader parentClassLoader = null;
+
     // ------------------------------------------------------------- Properties
 
 
@@ -751,12 +749,28 @@ public final class StandardServer extends LifecycleMBeanBase
      */
     @Override
     public ClassLoader getParentClassLoader() {
+        if (parentClassLoader != null)
+            return (parentClassLoader);
         if (catalina != null) {
             return (catalina.getParentClassLoader());
         }
         return (ClassLoader.getSystemClassLoader());
     }
 
+    /**
+     * Set the parent class loader for this server.
+     *
+     * @param parent The new parent class loader
+     */
+    @Override
+    public void setParentClassLoader(ClassLoader parent) {
+        ClassLoader oldParentClassLoader = this.parentClassLoader;
+        this.parentClassLoader = parent;
+        support.firePropertyChange("parentClassLoader", oldParentClassLoader,
+                                   this.parentClassLoader);
+    }
+
+    
     private ObjectName onameStringCache;
     private ObjectName onameMBeanFactory;
     private ObjectName onameNamingResoucres;
index 216a7f8..8c8383f 100644 (file)
@@ -103,6 +103,7 @@ public class StandardService extends LifecycleMBeanBase implements Service {
      */
     protected Container container = null;
 
+    private ClassLoader parentClassLoader = null;
 
     // ------------------------------------------------------------- Properties
 
@@ -600,12 +601,26 @@ public class StandardService extends LifecycleMBeanBase implements Service {
      */
     @Override
     public ClassLoader getParentClassLoader() {
+        if (parentClassLoader != null)
+            return (parentClassLoader);
         if (server != null) {
             return (server.getParentClassLoader());
         }
         return (ClassLoader.getSystemClassLoader());
     }
 
+    /**
+     * Set the parent class loader for this server.
+     *
+     * @param parent The new parent class loader
+     */
+    @Override
+    public void setParentClassLoader(ClassLoader parent) {
+        ClassLoader oldParentClassLoader = this.parentClassLoader;
+        this.parentClassLoader = parent;
+        support.firePropertyChange("parentClassLoader", oldParentClassLoader,
+                                   this.parentClassLoader);
+    }
     @Override
     protected String getDomainInternal() {
         
diff --git a/test/org/apache/catalina/startup/TestTomcatClassLoader.java b/test/org/apache/catalina/startup/TestTomcatClassLoader.java
new file mode 100644 (file)
index 0000000..f80c92b
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * 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.catalina.startup;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.loader.WebappClassLoader;
+import org.apache.tomcat.util.buf.ByteChunk;
+
+public class TestTomcatClassLoader extends TomcatBaseTest {
+
+    public void testDefaultClassLoader() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        // Must have a real docBase - just use temp
+        Context ctx =
+            tomcat.addContext("", System.getProperty("java.io.tmpdir"));
+        
+        Tomcat.addServlet(ctx, "ClassLoaderReport", new ClassLoaderReport(null));
+        ctx.addServletMapping("/", "ClassLoaderReport");
+        
+        tomcat.start();
+        
+        ByteChunk res = getUrl("http://localhost:" + getPort() + "/");
+        assertEquals("WEBAPP,SYSTEM,OTHER,", res.toString());
+    }
+    
+    public void testNonDefaultClassLoader() throws Exception {
+        
+        ClassLoader cl = new URLClassLoader(new URL[0],
+                Thread.currentThread().getContextClassLoader());
+
+        Thread.currentThread().setContextClassLoader(cl);
+        
+        Tomcat tomcat = getTomcatInstance();
+        tomcat.getServer().setParentClassLoader(cl);
+
+        // Must have a real docBase - just use temp
+        Context ctx =
+            tomcat.addContext("", System.getProperty("java.io.tmpdir"));
+        
+        Tomcat.addServlet(ctx, "ClassLoaderReport", new ClassLoaderReport(cl));
+        ctx.addServletMapping("/", "ClassLoaderReport");
+        
+        tomcat.start();
+        
+        ByteChunk res = getUrl("http://localhost:" + getPort() + "/");
+        assertEquals("WEBAPP,CUSTOM,SYSTEM,OTHER,", res.toString());
+    }
+    
+    private static final class ClassLoaderReport extends HttpServlet {
+        private static final long serialVersionUID = 1L;
+
+        ClassLoader custom;
+
+        public ClassLoaderReport(ClassLoader custom) {
+            this.custom = custom;
+        }
+
+        @Override
+        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+                throws ServletException, IOException {
+            resp.setContentType("text/plain");
+            PrintWriter out = resp.getWriter();
+
+            ClassLoader system = ClassLoader.getSystemClassLoader();
+            
+            ClassLoader cl = Thread.currentThread().getContextClassLoader();
+            while (cl != null) {
+                if (system == cl) {
+                    out.print("SYSTEM,");
+                } else if (custom == cl) {
+                    out.print("CUSTOM,");
+                } else if (cl instanceof WebappClassLoader) {
+                    out.print("WEBAPP,");
+                } else {
+                    out.print("OTHER,");
+                }
+                cl = cl.getParent();
+            }
+        }
+    }
+}
index a9db70d..a442424 100644 (file)
         Improve fix for <bug>50205</bug> to trigger an error earlier if invalid
         configuration is used. (markt)
       </update>
+      <add>
+        Provide additional control over component class loaders, primarily for
+        use when embedding. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Jasper">