This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 6e83c093f2b674dfcdb06151392d0651a8b880ff Author: Mark Thomas <ma...@apache.org> AuthorDate: Mon Feb 27 16:01:22 2023 +0000 Revert "Refactcor custom URL handler to use ServiceLoader" This reverts commits: 4c70a12e8aac4081a11f200a7e70f0db30a6c79b. 3a2db66ffd4efb9f9d8f5c7b916477f62f32441c. 6b4d6ba0fad9f233e492618ea0bbcbaa307605fc. f949ac1cd08004d1a149d3a411b079c7712e44bb. 0782f0b919b5131a9423c91333f7c557954336a3. --- bin/catalina.bat | 3 + bin/catalina.sh | 3 + build.xml | 2 - .../catalina/loader/WebappClassLoaderBase.java | 4 + .../apache/catalina/webresources/StandardRoot.java | 11 +- .../TomcatURLStreamHandlerFactory.java | 176 +++++++++++++++++++++ .../services/java.net.spi.URLStreamHandlerProvider | 16 -- .../services/java.net.spi.URLStreamHandlerProvider | 16 -- .../services/java.net.spi.URLStreamHandlerProvider | 17 -- .../TestClasspathUrlStreamHandler.java | 6 + .../webresources/TestJarWarResourceSet.java | 7 + .../TestTomcatURLStreamHandlerFactory.java | 35 ++-- .../catalina/webresources/war/TestHandler.java | 9 ++ .../webresources/war/TestWarURLConnection.java | 9 ++ .../apache/tomcat/util/buf/TesterUriUtilBase.java | 3 + .../tomcat/util/file/TestConfigFileLoader.java | 2 + .../util/scan/TestAbstractInputStreamJar.java | 8 + webapps/docs/changelog.xml | 5 + 18 files changed, 260 insertions(+), 72 deletions(-) diff --git a/bin/catalina.bat b/bin/catalina.bat index 068cd283bc..ce73b88044 100755 --- a/bin/catalina.bat +++ b/bin/catalina.bat @@ -203,6 +203,9 @@ set "JSSE_OPTS=-Djdk.tls.ephemeralDHKeySize=2048" :gotJsseOpts set "JAVA_OPTS=%JAVA_OPTS% %JSSE_OPTS%" +rem Register custom URL handlers +set "JAVA_OPTS=%JAVA_OPTS% -Djava.protocol.handler.pkgs=org.apache.catalina.webresources" + if not "%CATALINA_LOGGING_CONFIG%" == "" goto noJuliConfig set CATALINA_LOGGING_CONFIG=-Dnop if not exist "%CATALINA_BASE%\conf\logging.properties" goto noJuliConfig diff --git a/bin/catalina.sh b/bin/catalina.sh index a7b14b6957..d987738b27 100755 --- a/bin/catalina.sh +++ b/bin/catalina.sh @@ -251,6 +251,9 @@ if [ -z "$JSSE_OPTS" ] ; then fi JAVA_OPTS="$JAVA_OPTS $JSSE_OPTS" +# Register custom URL handlers +JAVA_OPTS="$JAVA_OPTS -Djava.protocol.handler.pkgs=org.apache.catalina.webresources" + # Set juli LogManager config file if it is present and an override has not been issued if [ -z "$CATALINA_LOGGING_CONFIG" ]; then if [ -r "$CATALINA_BASE"/conf/logging.properties ]; then diff --git a/build.xml b/build.xml index d51f3045cf..9070c52281 100644 --- a/build.xml +++ b/build.xml @@ -1169,7 +1169,6 @@ <jarIt jarfile="${catalina.jar}" filesDir="${tomcat.classes}" filesId="files.catalina" - meta-inf="${tomcat.manifests}/catalina.jar" addOSGi="true" /> <!-- Catalina Cluster/HA JAR File --> @@ -1699,7 +1698,6 @@ filesId="files.tomcat-embed-core" notice="${tomcat.manifests}/servlet-api.jar.notice" license="${tomcat.manifests}/servlet-api.jar.license" - meta-inf="${tomcat.manifests}/tomcat-embed-core.jar" addOSGi="true" addGraal="true" graalPrefix="org.apache.tomcat.embed/tomcat-embed-core" diff --git a/java/org/apache/catalina/loader/WebappClassLoaderBase.java b/java/org/apache/catalina/loader/WebappClassLoaderBase.java index 2addcb83c8..b637fdd323 100644 --- a/java/org/apache/catalina/loader/WebappClassLoaderBase.java +++ b/java/org/apache/catalina/loader/WebappClassLoaderBase.java @@ -61,6 +61,7 @@ import org.apache.catalina.LifecycleListener; import org.apache.catalina.LifecycleState; import org.apache.catalina.WebResource; import org.apache.catalina.WebResourceRoot; +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; import org.apache.juli.WebappProperties; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; @@ -1542,6 +1543,9 @@ public abstract class WebappClassLoaderBase extends URLClassLoader // Clear the classloader reference in the VM's bean introspector java.beans.Introspector.flushCaches(); + + // Clear any custom URLStreamHandlers + TomcatURLStreamHandlerFactory.release(this); } diff --git a/java/org/apache/catalina/webresources/StandardRoot.java b/java/org/apache/catalina/webresources/StandardRoot.java index 3609e2c368..2cb98e277a 100644 --- a/java/org/apache/catalina/webresources/StandardRoot.java +++ b/java/org/apache/catalina/webresources/StandardRoot.java @@ -45,6 +45,7 @@ import org.apache.catalina.util.LifecycleMBeanBase; import org.apache.juli.logging.Log; import org.apache.juli.logging.LogFactory; import org.apache.tomcat.util.buf.UriUtil; +import org.apache.tomcat.util.compat.JreCompat; import org.apache.tomcat.util.http.RequestUtil; import org.apache.tomcat.util.res.StringManager; @@ -691,12 +692,12 @@ public class StandardRoot extends LifecycleMBeanBase implements WebResourceRoot } } - /** - * @deprecated Unused. Will be removed in Tomcat 11 onwards. - */ - @Deprecated protected void registerURLStreamHandlerFactory() { - // NO-OP + if (!JreCompat.isGraalAvailable()) { + // Ensure support for jar:war:file:/ URLs will be available (required + // for resource JARs in packed WAR files). + TomcatURLStreamHandlerFactory.register(); + } } @Override diff --git a/java/org/apache/catalina/webresources/TomcatURLStreamHandlerFactory.java b/java/org/apache/catalina/webresources/TomcatURLStreamHandlerFactory.java new file mode 100644 index 0000000000..d58f9d3fef --- /dev/null +++ b/java/org/apache/catalina/webresources/TomcatURLStreamHandlerFactory.java @@ -0,0 +1,176 @@ +/* + * 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.webresources; + +import java.net.URL; +import java.net.URLStreamHandler; +import java.net.URLStreamHandlerFactory; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.apache.catalina.webresources.war.Handler; + +public class TomcatURLStreamHandlerFactory implements URLStreamHandlerFactory { + + private static final String WAR_PROTOCOL = "war"; + private static final String CLASSPATH_PROTOCOL = "classpath"; + + // Singleton instance + private static volatile TomcatURLStreamHandlerFactory instance = null; + + /** + * Obtain a reference to the singleton instance. It is recommended that + * callers check the value of {@link #isRegistered()} before using the + * returned instance. + * + * @return A reference to the singleton instance + */ + public static TomcatURLStreamHandlerFactory getInstance() { + getInstanceInternal(true); + return instance; + } + + + private static TomcatURLStreamHandlerFactory getInstanceInternal(boolean register) { + // Double checked locking. OK because instance is volatile. + if (instance == null) { + synchronized (TomcatURLStreamHandlerFactory.class) { + if (instance == null) { + instance = new TomcatURLStreamHandlerFactory(register); + } + } + } + return instance; + } + + + private final boolean registered; + + // List of factories for application defined stream handler factories. + private final List<URLStreamHandlerFactory> userFactories = + new CopyOnWriteArrayList<>(); + + /** + * Register this factory with the JVM. May be called more than once. The + * implementation ensures that registration only occurs once. + * + * @return <code>true</code> if the factory is already registered with the + * JVM or was successfully registered as a result of this call. + * <code>false</code> if the factory was disabled prior to this + * call. + */ + public static boolean register() { + return getInstanceInternal(true).isRegistered(); + } + + + /** + * Prevent this this factory from registering with the JVM. May be called + * more than once. + * + * @return <code>true</code> if the factory is already disabled or was + * successfully disabled as a result of this call. + * <code>false</code> if the factory was already registered prior + * to this call. + */ + public static boolean disable() { + return !getInstanceInternal(false).isRegistered(); + } + + + /** + * Release references to any user provided factories that have been loaded + * using the provided class loader. Called during web application stop to + * prevent memory leaks. + * + * @param classLoader The class loader to release + */ + public static void release(ClassLoader classLoader) { + if (instance == null) { + return; + } + List<URLStreamHandlerFactory> factories = instance.userFactories; + for (URLStreamHandlerFactory factory : factories) { + ClassLoader factoryLoader = factory.getClass().getClassLoader(); + while (factoryLoader != null) { + if (classLoader.equals(factoryLoader)) { + // Implementation note: userFactories is a + // CopyOnWriteArrayList, so items are removed with + // List.remove() instead of usual Iterator.remove() + factories.remove(factory); + break; + } + factoryLoader = factoryLoader.getParent(); + } + } + } + + + private TomcatURLStreamHandlerFactory(boolean register) { + // Hide default constructor + // Singleton pattern to ensure there is only one instance of this + // factory + this.registered = register; + if (register) { + URL.setURLStreamHandlerFactory(this); + } + } + + + public boolean isRegistered() { + return registered; + } + + + /** + * Since the JVM only allows a single call to + * {@link URL#setURLStreamHandlerFactory(URLStreamHandlerFactory)} and + * Tomcat needs to register a handler, provide a mechanism to allow + * applications to register their own handlers. + * + * @param factory The user provided factory to add to the factories Tomcat + * has already registered + */ + public void addUserFactory(URLStreamHandlerFactory factory) { + userFactories.add(factory); + } + + + @Override + public URLStreamHandler createURLStreamHandler(String protocol) { + + // Tomcat's handler always takes priority so applications can't override + // it. + if (WAR_PROTOCOL.equals(protocol)) { + return new Handler(); + } else if (CLASSPATH_PROTOCOL.equals(protocol)) { + return new ClasspathURLStreamHandler(); + } + + // Application handlers + for (URLStreamHandlerFactory factory : userFactories) { + URLStreamHandler handler = + factory.createURLStreamHandler(protocol); + if (handler != null) { + return handler; + } + } + + // Unknown protocol + return null; + } +} diff --git a/res/META-INF/catalina.jar/services/java.net.spi.URLStreamHandlerProvider b/res/META-INF/catalina.jar/services/java.net.spi.URLStreamHandlerProvider deleted file mode 100644 index 60438bdb23..0000000000 --- a/res/META-INF/catalina.jar/services/java.net.spi.URLStreamHandlerProvider +++ /dev/null @@ -1,16 +0,0 @@ -# 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. - -org.apache.catalina.webresources.TomcatURLStreamHandlerProvider \ No newline at end of file diff --git a/res/META-INF/tomcat-embed-core.jar/services/java.net.spi.URLStreamHandlerProvider b/res/META-INF/tomcat-embed-core.jar/services/java.net.spi.URLStreamHandlerProvider deleted file mode 100644 index 60438bdb23..0000000000 --- a/res/META-INF/tomcat-embed-core.jar/services/java.net.spi.URLStreamHandlerProvider +++ /dev/null @@ -1,16 +0,0 @@ -# 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. - -org.apache.catalina.webresources.TomcatURLStreamHandlerProvider \ No newline at end of file diff --git a/test/META-INF/services/java.net.spi.URLStreamHandlerProvider b/test/META-INF/services/java.net.spi.URLStreamHandlerProvider deleted file mode 100644 index e0b27f914f..0000000000 --- a/test/META-INF/services/java.net.spi.URLStreamHandlerProvider +++ /dev/null @@ -1,17 +0,0 @@ -# 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. - -# Handler required when running tests -org.apache.catalina.webresources.TomcatURLStreamHandlerProvider \ No newline at end of file diff --git a/test/org/apache/catalina/webresources/TestClasspathUrlStreamHandler.java b/test/org/apache/catalina/webresources/TestClasspathUrlStreamHandler.java index 9df2b90fa3..f1bb097b37 100644 --- a/test/org/apache/catalina/webresources/TestClasspathUrlStreamHandler.java +++ b/test/org/apache/catalina/webresources/TestClasspathUrlStreamHandler.java @@ -22,10 +22,16 @@ import java.net.URL; import java.util.Properties; import org.junit.Assert; +import org.junit.BeforeClass; import org.junit.Test; public class TestClasspathUrlStreamHandler { + @BeforeClass + public static void setup() { + TomcatURLStreamHandlerFactory.getInstance(); + } + @Test public void testClasspathURL01() throws IOException { URL u = new URL("classpath:/org/apache/catalina/webresources/LocalStrings.properties"); diff --git a/test/org/apache/catalina/webresources/TestJarWarResourceSet.java b/test/org/apache/catalina/webresources/TestJarWarResourceSet.java index ed5b29fd2f..be5d98f87a 100644 --- a/test/org/apache/catalina/webresources/TestJarWarResourceSet.java +++ b/test/org/apache/catalina/webresources/TestJarWarResourceSet.java @@ -19,6 +19,7 @@ package org.apache.catalina.webresources; import java.io.File; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.apache.catalina.Context; @@ -29,6 +30,12 @@ import org.apache.catalina.startup.TomcatBaseTest; public class TestJarWarResourceSet extends TomcatBaseTest { + @Before + public void register() { + TomcatURLStreamHandlerFactory.register(); + } + + @Test public void testJarWarMetaInf() throws LifecycleException { Tomcat tomcat = getTomcatInstance(); diff --git a/java/org/apache/catalina/webresources/TomcatURLStreamHandlerProvider.java b/test/org/apache/catalina/webresources/TestTomcatURLStreamHandlerFactory.java similarity index 56% rename from java/org/apache/catalina/webresources/TomcatURLStreamHandlerProvider.java rename to test/org/apache/catalina/webresources/TestTomcatURLStreamHandlerFactory.java index 6be9781506..b999c8be71 100644 --- a/java/org/apache/catalina/webresources/TomcatURLStreamHandlerProvider.java +++ b/test/org/apache/catalina/webresources/TestTomcatURLStreamHandlerFactory.java @@ -17,24 +17,27 @@ package org.apache.catalina.webresources; import java.net.URLStreamHandler; -import java.net.spi.URLStreamHandlerProvider; +import java.net.URLStreamHandlerFactory; -import org.apache.catalina.webresources.war.Handler; +import org.junit.Before; +import org.junit.Test; -public class TomcatURLStreamHandlerProvider extends URLStreamHandlerProvider { - - private static final String WAR_PROTOCOL = "war"; - private static final String CLASSPATH_PROTOCOL = "classpath"; - - @Override - public URLStreamHandler createURLStreamHandler(String protocol) { - if (WAR_PROTOCOL.equals(protocol)) { - return new Handler(); - } else if (CLASSPATH_PROTOCOL.equals(protocol)) { - return new ClasspathURLStreamHandler(); - } +public class TestTomcatURLStreamHandlerFactory { + @Before + public void register() { + TomcatURLStreamHandlerFactory.register(); + } - return null; + @Test + public void testUserFactory() throws Exception { + URLStreamHandlerFactory factory = new URLStreamHandlerFactory() { + @Override + public URLStreamHandler createURLStreamHandler(String protocol) { + return null; + } + }; + TomcatURLStreamHandlerFactory.getInstance().addUserFactory(factory); + TomcatURLStreamHandlerFactory.release(factory.getClass().getClassLoader()); } -} +} \ No newline at end of file diff --git a/test/org/apache/catalina/webresources/war/TestHandler.java b/test/org/apache/catalina/webresources/war/TestHandler.java index 0e3ac88a48..c4b72bee15 100644 --- a/test/org/apache/catalina/webresources/war/TestHandler.java +++ b/test/org/apache/catalina/webresources/war/TestHandler.java @@ -21,10 +21,19 @@ import java.net.URL; import java.net.URLConnection; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; + public class TestHandler { + @Before + public void register() { + TomcatURLStreamHandlerFactory.register(); + } + + @Test public void testUrlFileInJarInWar() throws Exception { doTestUrl("jar:war:", "*/WEB-INF/lib/test.jar!/META-INF/resources/index.html"); diff --git a/test/org/apache/catalina/webresources/war/TestWarURLConnection.java b/test/org/apache/catalina/webresources/war/TestWarURLConnection.java index 1d82530ce9..f005115824 100644 --- a/test/org/apache/catalina/webresources/war/TestWarURLConnection.java +++ b/test/org/apache/catalina/webresources/war/TestWarURLConnection.java @@ -21,10 +21,19 @@ import java.net.URL; import java.net.URLConnection; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; + public class TestWarURLConnection { + @Before + public void register() { + TomcatURLStreamHandlerFactory.register(); + } + + @Test public void testContentLength() throws Exception { File f = new File("test/webresources/war-url-connection.war"); diff --git a/test/org/apache/tomcat/util/buf/TesterUriUtilBase.java b/test/org/apache/tomcat/util/buf/TesterUriUtilBase.java index bad8b2f685..9980669aa2 100644 --- a/test/org/apache/tomcat/util/buf/TesterUriUtilBase.java +++ b/test/org/apache/tomcat/util/buf/TesterUriUtilBase.java @@ -23,12 +23,15 @@ import java.net.URL; import org.junit.Assert; import org.junit.Test; +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; + public abstract class TesterUriUtilBase { private final String separator; protected TesterUriUtilBase(String separator) { this.separator = separator; + TomcatURLStreamHandlerFactory.register(); System.setProperty("org.apache.tomcat.util.buf.UriUtil.WAR_SEPARATOR", separator); } diff --git a/test/org/apache/tomcat/util/file/TestConfigFileLoader.java b/test/org/apache/tomcat/util/file/TestConfigFileLoader.java index d87c3de17f..5271e778da 100644 --- a/test/org/apache/tomcat/util/file/TestConfigFileLoader.java +++ b/test/org/apache/tomcat/util/file/TestConfigFileLoader.java @@ -27,11 +27,13 @@ import org.junit.BeforeClass; import org.junit.Test; import org.apache.catalina.startup.CatalinaBaseConfigurationSource; +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; public class TestConfigFileLoader { @BeforeClass public static void setup() { + TomcatURLStreamHandlerFactory.getInstance(); System.setProperty("catalina.base", ""); ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(new File(System.getProperty("catalina.base")), null)); } diff --git a/test/org/apache/tomcat/util/scan/TestAbstractInputStreamJar.java b/test/org/apache/tomcat/util/scan/TestAbstractInputStreamJar.java index 278ea23046..b6f96e2b17 100644 --- a/test/org/apache/tomcat/util/scan/TestAbstractInputStreamJar.java +++ b/test/org/apache/tomcat/util/scan/TestAbstractInputStreamJar.java @@ -22,13 +22,21 @@ import java.io.InputStream; import java.net.URL; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.apache.catalina.util.IOTools; +import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory; import org.apache.tomcat.Jar; public class TestAbstractInputStreamJar { + @Before + public void register() { + TomcatURLStreamHandlerFactory.register(); + } + + @Test public void testNestedJarGetInputStream() throws Exception { File f = new File("test/webresources/war-url-connection.war"); diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 3f3e4e70e2..feaabd8985 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -129,6 +129,11 @@ so that the provider configuration file required by Tomcat's custom URL protocol for WAR files is retained when adding OSGi meta-data. (markt) </fix> + <update> + Revert switch to using the ServiceLoader mechanism to load the custom URL + protocol handlers that Tomcat uses. The original system property based + approach has been restored. (markt) + </update> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org