Skip to content

Commit 6e83c09

Browse files
committed
Revert "Refactcor custom URL handler to use ServiceLoader"
This reverts commits: 4c70a12. 3a2db66. 6b4d6ba. f949ac1. 0782f0b.
1 parent 8729485 commit 6e83c09

18 files changed

+260
-72
lines changed

bin/catalina.bat

+3
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,9 @@ set "JSSE_OPTS=-Djdk.tls.ephemeralDHKeySize=2048"
203203
:gotJsseOpts
204204
set "JAVA_OPTS=%JAVA_OPTS% %JSSE_OPTS%"
205205

206+
rem Register custom URL handlers
207+
set "JAVA_OPTS=%JAVA_OPTS% -Djava.protocol.handler.pkgs=org.apache.catalina.webresources"
208+
206209
if not "%CATALINA_LOGGING_CONFIG%" == "" goto noJuliConfig
207210
set CATALINA_LOGGING_CONFIG=-Dnop
208211
if not exist "%CATALINA_BASE%\conf\logging.properties" goto noJuliConfig

bin/catalina.sh

+3
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,9 @@ if [ -z "$JSSE_OPTS" ] ; then
251251
fi
252252
JAVA_OPTS="$JAVA_OPTS $JSSE_OPTS"
253253

254+
# Register custom URL handlers
255+
JAVA_OPTS="$JAVA_OPTS -Djava.protocol.handler.pkgs=org.apache.catalina.webresources"
256+
254257
# Set juli LogManager config file if it is present and an override has not been issued
255258
if [ -z "$CATALINA_LOGGING_CONFIG" ]; then
256259
if [ -r "$CATALINA_BASE"/conf/logging.properties ]; then

build.xml

-2
Original file line numberDiff line numberDiff line change
@@ -1169,7 +1169,6 @@
11691169
<jarIt jarfile="${catalina.jar}"
11701170
filesDir="${tomcat.classes}"
11711171
filesId="files.catalina"
1172-
meta-inf="${tomcat.manifests}/catalina.jar"
11731172
addOSGi="true" />
11741173

11751174
<!-- Catalina Cluster/HA JAR File -->
@@ -1699,7 +1698,6 @@
16991698
filesId="files.tomcat-embed-core"
17001699
notice="${tomcat.manifests}/servlet-api.jar.notice"
17011700
license="${tomcat.manifests}/servlet-api.jar.license"
1702-
meta-inf="${tomcat.manifests}/tomcat-embed-core.jar"
17031701
addOSGi="true"
17041702
addGraal="true"
17051703
graalPrefix="org.apache.tomcat.embed/tomcat-embed-core"

java/org/apache/catalina/loader/WebappClassLoaderBase.java

+4
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import org.apache.catalina.LifecycleState;
6262
import org.apache.catalina.WebResource;
6363
import org.apache.catalina.WebResourceRoot;
64+
import org.apache.catalina.webresources.TomcatURLStreamHandlerFactory;
6465
import org.apache.juli.WebappProperties;
6566
import org.apache.juli.logging.Log;
6667
import org.apache.juli.logging.LogFactory;
@@ -1542,6 +1543,9 @@ protected void clearReferences() {
15421543

15431544
// Clear the classloader reference in the VM's bean introspector
15441545
java.beans.Introspector.flushCaches();
1546+
1547+
// Clear any custom URLStreamHandlers
1548+
TomcatURLStreamHandlerFactory.release(this);
15451549
}
15461550

15471551

java/org/apache/catalina/webresources/StandardRoot.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
import org.apache.juli.logging.Log;
4646
import org.apache.juli.logging.LogFactory;
4747
import org.apache.tomcat.util.buf.UriUtil;
48+
import org.apache.tomcat.util.compat.JreCompat;
4849
import org.apache.tomcat.util.http.RequestUtil;
4950
import org.apache.tomcat.util.res.StringManager;
5051

@@ -691,12 +692,12 @@ protected void initInternal() throws LifecycleException {
691692
}
692693
}
693694

694-
/**
695-
* @deprecated Unused. Will be removed in Tomcat 11 onwards.
696-
*/
697-
@Deprecated
698695
protected void registerURLStreamHandlerFactory() {
699-
// NO-OP
696+
if (!JreCompat.isGraalAvailable()) {
697+
// Ensure support for jar:war:file:/ URLs will be available (required
698+
// for resource JARs in packed WAR files).
699+
TomcatURLStreamHandlerFactory.register();
700+
}
700701
}
701702

702703
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.catalina.webresources;
18+
19+
import java.net.URL;
20+
import java.net.URLStreamHandler;
21+
import java.net.URLStreamHandlerFactory;
22+
import java.util.List;
23+
import java.util.concurrent.CopyOnWriteArrayList;
24+
25+
import org.apache.catalina.webresources.war.Handler;
26+
27+
public class TomcatURLStreamHandlerFactory implements URLStreamHandlerFactory {
28+
29+
private static final String WAR_PROTOCOL = "war";
30+
private static final String CLASSPATH_PROTOCOL = "classpath";
31+
32+
// Singleton instance
33+
private static volatile TomcatURLStreamHandlerFactory instance = null;
34+
35+
/**
36+
* Obtain a reference to the singleton instance. It is recommended that
37+
* callers check the value of {@link #isRegistered()} before using the
38+
* returned instance.
39+
*
40+
* @return A reference to the singleton instance
41+
*/
42+
public static TomcatURLStreamHandlerFactory getInstance() {
43+
getInstanceInternal(true);
44+
return instance;
45+
}
46+
47+
48+
private static TomcatURLStreamHandlerFactory getInstanceInternal(boolean register) {
49+
// Double checked locking. OK because instance is volatile.
50+
if (instance == null) {
51+
synchronized (TomcatURLStreamHandlerFactory.class) {
52+
if (instance == null) {
53+
instance = new TomcatURLStreamHandlerFactory(register);
54+
}
55+
}
56+
}
57+
return instance;
58+
}
59+
60+
61+
private final boolean registered;
62+
63+
// List of factories for application defined stream handler factories.
64+
private final List<URLStreamHandlerFactory> userFactories =
65+
new CopyOnWriteArrayList<>();
66+
67+
/**
68+
* Register this factory with the JVM. May be called more than once. The
69+
* implementation ensures that registration only occurs once.
70+
*
71+
* @return <code>true</code> if the factory is already registered with the
72+
* JVM or was successfully registered as a result of this call.
73+
* <code>false</code> if the factory was disabled prior to this
74+
* call.
75+
*/
76+
public static boolean register() {
77+
return getInstanceInternal(true).isRegistered();
78+
}
79+
80+
81+
/**
82+
* Prevent this this factory from registering with the JVM. May be called
83+
* more than once.
84+
*
85+
* @return <code>true</code> if the factory is already disabled or was
86+
* successfully disabled as a result of this call.
87+
* <code>false</code> if the factory was already registered prior
88+
* to this call.
89+
*/
90+
public static boolean disable() {
91+
return !getInstanceInternal(false).isRegistered();
92+
}
93+
94+
95+
/**
96+
* Release references to any user provided factories that have been loaded
97+
* using the provided class loader. Called during web application stop to
98+
* prevent memory leaks.
99+
*
100+
* @param classLoader The class loader to release
101+
*/
102+
public static void release(ClassLoader classLoader) {
103+
if (instance == null) {
104+
return;
105+
}
106+
List<URLStreamHandlerFactory> factories = instance.userFactories;
107+
for (URLStreamHandlerFactory factory : factories) {
108+
ClassLoader factoryLoader = factory.getClass().getClassLoader();
109+
while (factoryLoader != null) {
110+
if (classLoader.equals(factoryLoader)) {
111+
// Implementation note: userFactories is a
112+
// CopyOnWriteArrayList, so items are removed with
113+
// List.remove() instead of usual Iterator.remove()
114+
factories.remove(factory);
115+
break;
116+
}
117+
factoryLoader = factoryLoader.getParent();
118+
}
119+
}
120+
}
121+
122+
123+
private TomcatURLStreamHandlerFactory(boolean register) {
124+
// Hide default constructor
125+
// Singleton pattern to ensure there is only one instance of this
126+
// factory
127+
this.registered = register;
128+
if (register) {
129+
URL.setURLStreamHandlerFactory(this);
130+
}
131+
}
132+
133+
134+
public boolean isRegistered() {
135+
return registered;
136+
}
137+
138+
139+
/**
140+
* Since the JVM only allows a single call to
141+
* {@link URL#setURLStreamHandlerFactory(URLStreamHandlerFactory)} and
142+
* Tomcat needs to register a handler, provide a mechanism to allow
143+
* applications to register their own handlers.
144+
*
145+
* @param factory The user provided factory to add to the factories Tomcat
146+
* has already registered
147+
*/
148+
public void addUserFactory(URLStreamHandlerFactory factory) {
149+
userFactories.add(factory);
150+
}
151+
152+
153+
@Override
154+
public URLStreamHandler createURLStreamHandler(String protocol) {
155+
156+
// Tomcat's handler always takes priority so applications can't override
157+
// it.
158+
if (WAR_PROTOCOL.equals(protocol)) {
159+
return new Handler();
160+
} else if (CLASSPATH_PROTOCOL.equals(protocol)) {
161+
return new ClasspathURLStreamHandler();
162+
}
163+
164+
// Application handlers
165+
for (URLStreamHandlerFactory factory : userFactories) {
166+
URLStreamHandler handler =
167+
factory.createURLStreamHandler(protocol);
168+
if (handler != null) {
169+
return handler;
170+
}
171+
}
172+
173+
// Unknown protocol
174+
return null;
175+
}
176+
}

res/META-INF/catalina.jar/services/java.net.spi.URLStreamHandlerProvider

-16
This file was deleted.

res/META-INF/tomcat-embed-core.jar/services/java.net.spi.URLStreamHandlerProvider

-16
This file was deleted.

test/META-INF/services/java.net.spi.URLStreamHandlerProvider

-17
This file was deleted.

test/org/apache/catalina/webresources/TestClasspathUrlStreamHandler.java

+6
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,16 @@
2222
import java.util.Properties;
2323

2424
import org.junit.Assert;
25+
import org.junit.BeforeClass;
2526
import org.junit.Test;
2627

2728
public class TestClasspathUrlStreamHandler {
2829

30+
@BeforeClass
31+
public static void setup() {
32+
TomcatURLStreamHandlerFactory.getInstance();
33+
}
34+
2935
@Test
3036
public void testClasspathURL01() throws IOException {
3137
URL u = new URL("classpath:/org/apache/catalina/webresources/LocalStrings.properties");

test/org/apache/catalina/webresources/TestJarWarResourceSet.java

+7
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.io.File;
2020

2121
import org.junit.Assert;
22+
import org.junit.Before;
2223
import org.junit.Test;
2324

2425
import org.apache.catalina.Context;
@@ -29,6 +30,12 @@
2930

3031
public class TestJarWarResourceSet extends TomcatBaseTest {
3132

33+
@Before
34+
public void register() {
35+
TomcatURLStreamHandlerFactory.register();
36+
}
37+
38+
3239
@Test
3340
public void testJarWarMetaInf() throws LifecycleException {
3441
Tomcat tomcat = getTomcatInstance();

java/org/apache/catalina/webresources/TomcatURLStreamHandlerProvider.java renamed to test/org/apache/catalina/webresources/TestTomcatURLStreamHandlerFactory.java

+19-16
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,27 @@
1717
package org.apache.catalina.webresources;
1818

1919
import java.net.URLStreamHandler;
20-
import java.net.spi.URLStreamHandlerProvider;
20+
import java.net.URLStreamHandlerFactory;
2121

22-
import org.apache.catalina.webresources.war.Handler;
22+
import org.junit.Before;
23+
import org.junit.Test;
2324

24-
public class TomcatURLStreamHandlerProvider extends URLStreamHandlerProvider {
25-
26-
private static final String WAR_PROTOCOL = "war";
27-
private static final String CLASSPATH_PROTOCOL = "classpath";
28-
29-
@Override
30-
public URLStreamHandler createURLStreamHandler(String protocol) {
31-
if (WAR_PROTOCOL.equals(protocol)) {
32-
return new Handler();
33-
} else if (CLASSPATH_PROTOCOL.equals(protocol)) {
34-
return new ClasspathURLStreamHandler();
35-
}
25+
public class TestTomcatURLStreamHandlerFactory {
3626

27+
@Before
28+
public void register() {
29+
TomcatURLStreamHandlerFactory.register();
30+
}
3731

38-
return null;
32+
@Test
33+
public void testUserFactory() throws Exception {
34+
URLStreamHandlerFactory factory = new URLStreamHandlerFactory() {
35+
@Override
36+
public URLStreamHandler createURLStreamHandler(String protocol) {
37+
return null;
38+
}
39+
};
40+
TomcatURLStreamHandlerFactory.getInstance().addUserFactory(factory);
41+
TomcatURLStreamHandlerFactory.release(factory.getClass().getClassLoader());
3942
}
40-
}
43+
}

0 commit comments

Comments
 (0)