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      */

91     public static boolean disable() {
92         return !getInstanceInternal(false).isRegistered();
93     }
94
95
96     /**
97      * Release references to any user provided factories that have been loaded
98      * using the provided class loader. Called during web application stop to
99      * prevent memory leaks.
100      *
101      * @param classLoader The class loader to release
102      */

103     public static void release(ClassLoader classLoader) {
104         if (instance == null) {
105             return;
106         }
107         List<URLStreamHandlerFactory> factories = instance.userFactories;
108         for (URLStreamHandlerFactory factory : factories) {
109             ClassLoader factoryLoader = factory.getClass().getClassLoader();
110             while (factoryLoader != null) {
111                 if (classLoader.equals(factoryLoader)) {
112                     // Implementation note: userFactories is a
113                     // CopyOnWriteArrayList, so items are removed with
114                     // List.remove() instead of usual Iterator.remove()
115                     factories.remove(factory);
116                     break;
117                 }
118                 factoryLoader = factoryLoader.getParent();
119             }
120         }
121     }
122
123
124     private TomcatURLStreamHandlerFactory(boolean register) {
125         // Hide default constructor
126         // Singleton pattern to ensure there is only one instance of this
127         // factory
128         this.registered = register;
129         if (register) {
130             URL.setURLStreamHandlerFactory(this);
131         }
132     }
133
134
135     public boolean isRegistered() {
136         return registered;
137     }
138
139
140     /**
141      * Since the JVM only allows a single call to
142      * {@link URL#setURLStreamHandlerFactory(URLStreamHandlerFactory)} and
143      * Tomcat needs to register a handler, provide a mechanism to allow
144      * applications to register their own handlers.
145      *
146      * @param factory The user provided factory to add to the factories Tomcat
147      *                has already registered
148      */

149     public void addUserFactory(URLStreamHandlerFactory factory) {
150         userFactories.add(factory);
151     }
152
153
154     @Override
155     public URLStreamHandler createURLStreamHandler(String protocol) {
156
157         // Tomcat's handler always takes priority so applications can't override
158         // it.
159         if (WAR_PROTOCOL.equals(protocol)) {
160             return new Handler();
161         } else if (CLASSPATH_PROTOCOL.equals(protocol)) {
162             return new ClasspathURLStreamHandler();
163         }
164
165         // Application handlers
166         for (URLStreamHandlerFactory factory : userFactories) {
167             URLStreamHandler handler =
168                 factory.createURLStreamHandler(protocol);
169             if (handler != null) {
170                 return handler;
171             }
172         }
173
174         // Unknown protocol
175         return null;
176     }
177 }
178