1 /*******************************************************************************
2  * Copyright (c) 2004, 2016 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 
15 package org.eclipse.osgi.internal.url;
16 
17 import java.lang.reflect.Method;
18 import java.net.*;
19 import java.security.AccessController;
20 import java.util.*;
21 import org.eclipse.osgi.framework.log.FrameworkLogEntry;
22 import org.eclipse.osgi.framework.util.SecureAction;
23 import org.eclipse.osgi.internal.framework.EquinoxContainer;
24 import org.eclipse.osgi.internal.location.EquinoxLocations;
25 import org.eclipse.osgi.internal.messages.Msg;
26 import org.eclipse.osgi.storage.url.BundleResourceHandler;
27 import org.eclipse.osgi.util.NLS;
28 import org.osgi.framework.BundleContext;
29 import org.osgi.framework.ServiceReference;
30 import org.osgi.service.url.URLConstants;
31 import org.osgi.service.url.URLStreamHandlerService;
32 import org.osgi.util.tracker.ServiceTracker;
33 
34 /**
35  * This class contains the URL stream handler factory for the OSGi framework.
36  */
37 public class URLStreamHandlerFactoryImpl extends MultiplexingFactory implements URLStreamHandlerFactory {
38 	protected static final String URLSTREAMHANDLERCLASS = "org.osgi.service.url.URLStreamHandlerService"; //$NON-NLS-1$
39 	protected static final String PROTOCOL_HANDLER_PKGS = "java.protocol.handler.pkgs"; //$NON-NLS-1$
40 	public static final String PROTOCOL_REFERENCE = "reference"; //$NON-NLS-1$
41 	static final SecureAction secureAction = AccessController.doPrivileged(SecureAction.createSecureAction());
42 
43 	private ServiceTracker<URLStreamHandlerService, URLStreamHandlerService> handlerTracker;
44 
45 	private static final List<Class<?>> ignoredClasses = Arrays.asList(new Class<?>[] {MultiplexingURLStreamHandler.class, URLStreamHandlerFactoryImpl.class, URL.class});
46 	private Map<String, URLStreamHandler> proxies;
47 	private URLStreamHandlerFactory parentFactory;
48 	private ThreadLocal<List<String>> creatingProtocols = new ThreadLocal<>();
49 
50 	/**
51 	 * Create the factory.
52 	 *
53 	 * @param context BundleContext for the system bundle
54 	 */
URLStreamHandlerFactoryImpl(BundleContext context, EquinoxContainer container)55 	public URLStreamHandlerFactoryImpl(BundleContext context, EquinoxContainer container) {
56 		super(context, container);
57 
58 		proxies = new Hashtable<>(15);
59 		handlerTracker = new ServiceTracker<>(context, URLSTREAMHANDLERCLASS, null);
60 		handlerTracker.open();
61 	}
62 
getBuiltIn(String protocol, String builtInHandlers)63 	private Class<?> getBuiltIn(String protocol, String builtInHandlers) {
64 		if (builtInHandlers == null)
65 			return null;
66 		Class<?> clazz;
67 		StringTokenizer tok = new StringTokenizer(builtInHandlers, "|"); //$NON-NLS-1$
68 		while (tok.hasMoreElements()) {
69 			StringBuilder name = new StringBuilder();
70 			name.append(tok.nextToken());
71 			name.append("."); //$NON-NLS-1$
72 			name.append(protocol);
73 			name.append(".Handler"); //$NON-NLS-1$
74 			try {
75 				clazz = secureAction.loadSystemClass(name.toString());
76 				if (clazz != null)
77 					return clazz; //this class exists, it is a built in handler
78 			} catch (ClassNotFoundException ex) {
79 				// keep looking
80 			}
81 		}
82 		return null;
83 	}
84 
85 	/**
86 	 * Creates a new URLStreamHandler instance for the specified
87 	 * protocol.
88 	 *
89 	 * @param protocol The desired protocol
90 	 * @return a URLStreamHandler for the specific protocol.
91 	 */
92 	@Override
createURLStreamHandler(String protocol)93 	public URLStreamHandler createURLStreamHandler(String protocol) {
94 		// Check if we are recursing
95 		if (isRecursive(protocol))
96 			return null;
97 		try {
98 			//first check for built in handlers
99 			String builtInHandlers = secureAction.getProperty(PROTOCOL_HANDLER_PKGS);
100 			Class<?> clazz = getBuiltIn(protocol, builtInHandlers);
101 			if (clazz != null)
102 				return null; // let the VM handle it
103 			URLStreamHandler result = null;
104 			if (isMultiplexing()) {
105 				URLStreamHandler authorized = findAuthorizedURLStreamHandler(protocol);
106 				if (authorized != null)
107 					result = new MultiplexingURLStreamHandler(protocol, this, authorized);
108 			} else {
109 				result = createInternalURLStreamHandler(protocol);
110 			}
111 			// if parent is present do parent lookup
112 			if (result == null && parentFactory != null)
113 				result = parentFactory.createURLStreamHandler(protocol);
114 			return result; //result may be null; let the VM handle it (consider sun.net.protocol.www.*)
115 		} catch (Throwable t) {
116 			container.getLogServices().log(URLStreamHandlerFactoryImpl.class.getName(), FrameworkLogEntry.ERROR, "Unexpected error in factory.", t); //$NON-NLS-1$
117 			return null;
118 		} finally {
119 			releaseRecursive(protocol);
120 		}
121 	}
122 
isRecursive(String protocol)123 	private boolean isRecursive(String protocol) {
124 		List<String> protocols = creatingProtocols.get();
125 		if (protocols == null) {
126 			protocols = new ArrayList<>(1);
127 			creatingProtocols.set(protocols);
128 		}
129 		if (protocols.contains(protocol))
130 			return true;
131 		protocols.add(protocol);
132 		return false;
133 	}
134 
releaseRecursive(String protocol)135 	private void releaseRecursive(String protocol) {
136 		List<String> protocols = creatingProtocols.get();
137 		protocols.remove(protocol);
138 	}
139 
getFrameworkHandler(String protocol)140 	private URLStreamHandler getFrameworkHandler(String protocol) {
141 		if (BundleResourceHandler.OSGI_ENTRY_URL_PROTOCOL.equals(protocol)) {
142 			return new org.eclipse.osgi.storage.url.bundleentry.Handler(container.getStorage().getModuleContainer(), null);
143 		} else if (BundleResourceHandler.OSGI_RESOURCE_URL_PROTOCOL.equals(protocol)) {
144 			return new org.eclipse.osgi.storage.url.bundleresource.Handler(container.getStorage().getModuleContainer(), null);
145 		} else if (PROTOCOL_REFERENCE.equals(protocol)) {
146 			return new org.eclipse.osgi.storage.url.reference.Handler(container.getConfiguration().getConfiguration(EquinoxLocations.PROP_INSTALL_AREA));
147 		}
148 		return null;
149 	}
150 
createInternalURLStreamHandler(String protocol)151 	public URLStreamHandler createInternalURLStreamHandler(String protocol) {
152 		//internal protocol handlers
153 		URLStreamHandler frameworkHandler = getFrameworkHandler(protocol);
154 		if (frameworkHandler != null) {
155 			return frameworkHandler;
156 		}
157 
158 		//Now we check the service registry
159 		//first check to see if the handler is in the cache
160 		URLStreamHandlerProxy handler = (URLStreamHandlerProxy) proxies.get(protocol);
161 		if (handler != null)
162 			return (handler);
163 		//look through the service registry for a URLStramHandler registered for this protocol
164 		ServiceReference<URLStreamHandlerService>[] serviceReferences = handlerTracker.getServiceReferences();
165 		if (serviceReferences == null)
166 			return null;
167 		for (ServiceReference<URLStreamHandlerService> serviceReference : serviceReferences) {
168 			Object prop = serviceReference.getProperty(URLConstants.URL_HANDLER_PROTOCOL);
169 			if (prop instanceof String)
170 				prop = new String[] {(String) prop}; // TODO should this be a warning?
171 			if (!(prop instanceof String[])) {
172 				String message = NLS.bind(Msg.URL_HANDLER_INCORRECT_TYPE, new Object[]{URLConstants.URL_HANDLER_PROTOCOL, URLSTREAMHANDLERCLASS, serviceReference.getBundle()});
173 				container.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.WARNING, message, null);
174 				continue;
175 			}
176 			String[] protocols = (String[]) prop;
177 			for (String candidateProtocol : protocols) {
178 				if (candidateProtocol.equals(protocol)) {
179 					handler = new URLStreamHandlerProxy(protocol, serviceReference, context);
180 					proxies.put(protocol, handler);
181 					return (handler);
182 				}
183 			}
184 		}
185 		return null;
186 	}
187 
findAuthorizedURLStreamHandler(String protocol)188 	protected URLStreamHandler findAuthorizedURLStreamHandler(String protocol) {
189 		Object factory = findAuthorizedFactory(ignoredClasses);
190 		if (factory == null)
191 			return null;
192 
193 		if (factory == this)
194 			return createInternalURLStreamHandler(protocol);
195 
196 		try {
197 			Method createInternalURLStreamHandlerMethod = factory.getClass().getMethod("createInternalURLStreamHandler", new Class[] {String.class}); //$NON-NLS-1$
198 			return (URLStreamHandler) createInternalURLStreamHandlerMethod.invoke(factory, new Object[] {protocol});
199 		} catch (Exception e) {
200 			container.getLogServices().log(URLStreamHandlerFactoryImpl.class.getName(), FrameworkLogEntry.ERROR, "findAuthorizedURLStreamHandler-loop", e); //$NON-NLS-1$
201 			throw new RuntimeException(e.getMessage(), e);
202 		}
203 	}
204 
205 	@Override
getParentFactory()206 	public Object getParentFactory() {
207 		return parentFactory;
208 	}
209 
210 	@Override
setParentFactory(Object parentFactory)211 	public void setParentFactory(Object parentFactory) {
212 		if (this.parentFactory == null) // only allow it to be set once
213 			this.parentFactory = (URLStreamHandlerFactory) parentFactory;
214 	}
215 }
216