Author: markt Date: Tue Sep 8 13:46:34 2015 New Revision: 1701805 URL: http://svn.apache.org/r1701805 Log: Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=57681 Add a web application class loader implementation that supports the parallel loading of web application classes. Use of this feature requires a Java 7 or later JRE. Based on a patch by Huxing Zhang.
Added: tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/ParallelWebappClassLoader.java (with props) tomcat/tc7.0.x/trunk/test/org/apache/catalina/loader/TestParallelWebappClassLoader.java (with props) Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/LocalStrings.properties tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/mbeans-descriptors.xml tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml tomcat/tc7.0.x/trunk/webapps/docs/config/loader.xml Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/LocalStrings.properties?rev=1701805&r1=1701804&r2=1701805&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/LocalStrings.properties (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/LocalStrings.properties Tue Sep 8 13:46:34 2015 @@ -66,6 +66,7 @@ webappClassLoader.addTransformer.duplica webappClassLoader.addTransformer=Added class file transformer [{0}] to web application [{1}]. webappClassLoader.removeTransformer=Removed class file transformer [{0}] from web application [{1}]. webappClassLoader.transformError=Instrumentation error: could not transform class [{0}] because its class file format is not legal. +webappClassLoaderParallel.registrationFailed=Registration of org.apache.catalina.loader.ParallelWebappClassLoader as capable of loading classes in parallel failed webappLoader.addRepository=Adding repository {0} webappLoader.deploy=Deploying class repositories to work directory {0} webappLoader.jarDeploy=Deploy JAR {0} to {1} Added: tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/ParallelWebappClassLoader.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/ParallelWebappClassLoader.java?rev=1701805&view=auto ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/ParallelWebappClassLoader.java (added) +++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/ParallelWebappClassLoader.java Tue Sep 8 13:46:34 2015 @@ -0,0 +1,100 @@ +/* + * 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.loader; + +import java.lang.reflect.Method; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import org.apache.catalina.LifecycleException; +import org.apache.juli.logging.Log; +import org.apache.juli.logging.LogFactory; +import org.apache.tomcat.util.compat.JreCompat; + +/** + * Parallel class loading implementation of WebappClassLoaderBase. Parallel + * class loading is only available when using a Java 7+ JRE. + */ +public class ParallelWebappClassLoader extends WebappClassLoaderBase { + + private static final Log log = LogFactory.getLog(ParallelWebappClassLoader.class); + + static { + try { + if (JreCompat.isJre7Available()) { + // parallel class loading capable + final Method registerParallel = + ClassLoader.class.getDeclaredMethod("registerAsParallelCapable"); + AccessController.doPrivileged(new PrivilegedAction<Object>() { + @Override + public Object run() { + registerParallel.setAccessible(true); + return null; + } + }); + Boolean result = (Boolean)registerParallel.invoke(null); + if (!result.booleanValue()) { + log.warn(sm.getString("webappClassLoaderParallel.registrationFailed")); + } + } + } catch (Exception e) { + // ignore + } + } + + + public ParallelWebappClassLoader() { + super(); + } + + + public ParallelWebappClassLoader(ClassLoader parent) { + super(parent); + } + + + /** + * Returns a copy of this class loader without any class file + * transformers. This is a tool often used by Java Persistence API + * providers to inspect entity classes in the absence of any + * instrumentation, something that can't be guaranteed within the + * context of a {@link java.lang.instrument.ClassFileTransformer}'s + * {@link java.lang.instrument.ClassFileTransformer#transform(ClassLoader, + * String, Class, java.security.ProtectionDomain, byte[]) transform} method. + * <p> + * The returned class loader's resource cache will have been cleared + * so that classes already instrumented will not be retained or + * returned. + * + * @return the transformer-free copy of this class loader. + */ + @Override + public ParallelWebappClassLoader copyWithoutTransformers() { + + ParallelWebappClassLoader result = new ParallelWebappClassLoader(getParent()); + + super.copyStateWithoutTransformers(result); + + try { + result.start(); + } catch (LifecycleException e) { + throw new IllegalStateException(e); + } + + return result; + } +} \ No newline at end of file Propchange: tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/ParallelWebappClassLoader.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/mbeans-descriptors.xml URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/mbeans-descriptors.xml?rev=1701805&r1=1701804&r2=1701805&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/mbeans-descriptors.xml (original) +++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/loader/mbeans-descriptors.xml Tue Sep 8 13:46:34 2015 @@ -171,4 +171,48 @@ </mbean> + <mbean name="ParallelWebappClassLoader" + description="Classloader implementation which is specialized for handling web applications" + domain="Catalina" + group="Loader" + type="org.apache.catalina.loader.ParallelWebappClassLoader"> + + <attribute name="className" + description="Fully qualified class name of the managed object" + type="java.lang.String" + writeable="false"/> + + <attribute name="contextName" + description="Name of the webapp context" + type="java.lang.String" + writeable="false"/> + + <attribute name="delegate" + description="The 'follow standard delegation model' flag that will be used to configure our ClassLoader" + type="boolean"/> + + <attribute name="antiJARLocking" + description="The antiJARLocking flag for this Loader" + type="boolean"/> + + <attribute name="searchExternalFirst" + description="The searchExternalFirst flag for this Loader" + type="boolean"/> + + <attribute name="stateName" + description="The name of the LifecycleState that this component is currently in" + type="java.lang.String" + writeable="false"/> + + <attribute name="URLs" + description="The URLs of this loader" + type="[Ljava.net.URL;"/> + + <attribute name="jarPath" + description="The jarPath of this loader" + writeable="false" + type="java.lang.String"/> + + </mbean> + </mbeans-descriptors> Added: tomcat/tc7.0.x/trunk/test/org/apache/catalina/loader/TestParallelWebappClassLoader.java URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/test/org/apache/catalina/loader/TestParallelWebappClassLoader.java?rev=1701805&view=auto ============================================================================== --- tomcat/tc7.0.x/trunk/test/org/apache/catalina/loader/TestParallelWebappClassLoader.java (added) +++ tomcat/tc7.0.x/trunk/test/org/apache/catalina/loader/TestParallelWebappClassLoader.java Tue Sep 8 13:46:34 2015 @@ -0,0 +1,134 @@ +/* + * 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.loader; + +import java.io.IOException; +import java.lang.reflect.Method; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.Context; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.startup.TomcatBaseTest; +import org.apache.tomcat.util.compat.JreCompat; + +public class TestParallelWebappClassLoader extends TomcatBaseTest { + + private static final String PARALLEL_CLASSLOADER = + "org.apache.catalina.loader.ParallelWebappClassLoader"; + private static final String DUMMY_SERVLET = + "org.apache.catalina.loader.DummyServlet"; + + @Test + public void testParallelCapableOnJre7() { + if (!JreCompat.isJre7Available()) { + // ignore on Jre6 or lower + return; + } + try { + Tomcat tomcat = getTomcatInstance(); + Context ctx = tomcat.addContext("", null); + + WebappLoader webappLoader = new WebappLoader(); + webappLoader.setLoaderClass(PARALLEL_CLASSLOADER); + ctx.setLoader(webappLoader); + + tomcat.start(); + + ClassLoader classloader = ctx.getLoader().getClassLoader(); + + Assert.assertTrue(classloader instanceof ParallelWebappClassLoader); + + // parallel class loading capable + Method getClassLoadingLock = + getDeclaredMethod(classloader.getClass(), "getClassLoadingLock", String.class); + // make sure we have getClassLoadingLock on JRE7. + Assert.assertNotNull(getClassLoadingLock); + // give us permission to access protected method + getClassLoadingLock.setAccessible(true); + + Object lock = getClassLoadingLock.invoke(classloader, DUMMY_SERVLET); + // make sure it is not a ParallelWebappClassLoader object lock + Assert.assertNotEquals(lock, classloader); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail("testParallelCapableOnJre7 fails."); + } + } + + @Test + public void testParallelIncapableOnJre6() { + if (JreCompat.isJre7Available()) { + // ignore on Jre7 or above + return; + } + try { + Tomcat tomcat = getTomcatInstance(); + // Must have a real docBase - just use temp + Context ctx = tomcat.addContext("", + System.getProperty("java.io.tmpdir")); + + WebappLoader webappLoader = new WebappLoader(); + webappLoader.setLoaderClass(PARALLEL_CLASSLOADER); + ctx.setLoader(webappLoader); + + tomcat.start(); + + ClassLoader classloader = ctx.getLoader().getClassLoader(); + + Assert.assertTrue(classloader instanceof ParallelWebappClassLoader); + + // parallel class loading capable + Method getClassLoadingLock = + getDeclaredMethod(classloader.getClass(), "getClassLoadingLock", String.class); + // make sure we don't have getClassLoadingLock on JRE6. + Assert.assertNull(getClassLoadingLock); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail("testParallelIncapableOnJre6 fails."); + } + } + + private Method getDeclaredMethod(Class<?> clazz, String name, Class<?>... parameterTypes) { + if (clazz == null) return null; + for (Method method: clazz.getDeclaredMethods()) { + if (method.getName().equals(name)) { + return method; + } + } + // find from super class + return getDeclaredMethod(clazz.getSuperclass(), name, parameterTypes); + } + + @SuppressWarnings("unused") + private static final class DummyServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) + throws ServletException, IOException { + // do nothing + } + } +} \ No newline at end of file Propchange: tomcat/tc7.0.x/trunk/test/org/apache/catalina/loader/TestParallelWebappClassLoader.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml?rev=1701805&r1=1701804&r2=1701805&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml (original) +++ tomcat/tc7.0.x/trunk/webapps/docs/changelog.xml Tue Sep 8 13:46:34 2015 @@ -60,6 +60,12 @@ <section name="Tomcat 7.0.65 (violetagg)"> <subsection name="Catalina"> <changelog> + <add> + <bug>57681</bug>: Add a web application class loader implementation that + supports the parallel loading of web application classes. Use of this + feature requires a Java 7 or later JRE. Based on a patch by Huxing + Zhang. (markt) + </add> <fix> <bug>58187</bug>: Correct a regression in the fix for <bug>57765</bug> that meant that deployment of web applications deployed via the Manager Modified: tomcat/tc7.0.x/trunk/webapps/docs/config/loader.xml URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/docs/config/loader.xml?rev=1701805&r1=1701804&r2=1701805&view=diff ============================================================================== --- tomcat/tc7.0.x/trunk/webapps/docs/config/loader.xml (original) +++ tomcat/tc7.0.x/trunk/webapps/docs/config/loader.xml Tue Sep 8 13:46:34 2015 @@ -125,7 +125,11 @@ implementation class to use. If not specified, the default value is <code>org.apache.catalina.loader.WebappClassLoader</code>. Custom <strong>loaderClass</strong> implementations must extend - <code>org.apache.catalina.loader.WebappClassLoaderBase</code>.</p> + <code>org.apache.catalina.loader.WebappClassLoaderBase</code>.The + default <strong>loaderClass</strong> is not parallel capable. A parallel + capable <strong>loaderClass</strong> is available when running on a Java + 7 or higher JRE and can be used by specifying + <code>org.apache.catalina.loader.ParallelWebappClassLoader</code>.</p> </attribute> <attribute name="searchExternalFirst" required="false"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org