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