All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.felix.framework.URLHandlers Maven / Gradle / Ivy

There is a newer version: 8.1.2
Show newest version
/*
 * 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.felix.framework;

import java.net.ContentHandler;
import java.net.ContentHandlerFactory;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.framework.util.SecureAction;
import org.apache.felix.framework.util.SecurityManagerEx;
import org.osgi.service.url.URLStreamHandlerService;

/**
 * 

* This class is a singleton and implements the stream and content handler * factories for all framework instances executing within the JVM. Any * calls to retrieve stream or content handlers is routed through this class * and it acts as a multiplexer for all framework instances. To achieve this, * all framework instances register with this class when they are created so * that it can maintain a centralized registry of instances. *

*

* When this class receives a request for a stream or content handler, it * always returns a proxy handler instead of only returning a proxy if a * handler currently exists. This approach is used for three reasons: *

*
    *
  1. Potential caching behavior by the JVM of stream handlers does not give * you a second chance to provide a handler. *
  2. *
  3. Due to the dynamic nature of OSGi services, handlers may appear at * any time, so always creating a proxy makes sense. *
  4. *
  5. Since these handler factories service all framework instances, * some instances may have handlers and others may not, so returning * a proxy is the only answer that makes sense. *
  6. *
*

* It is possible to disable the URL Handlers service by setting the * framework.service.urlhandlers configuration property to false. * When multiple framework instances are in use, if no framework instances enable * the URL Handlers service, then the singleton stream and content factories will * never be set (i.e., URL.setURLStreamHandlerFactory() and * URLConnection.setContentHandlerFactory()). However, if one instance * enables URL Handlers service, then the factory methods will be invoked. In * that case, framework instances that disable the URL Handlers service will * simply not provide that services to their contained bundles, while framework * instances with the service enabled will. *

**/ class URLHandlers implements URLStreamHandlerFactory, ContentHandlerFactory { private static final Class[] CLASS_TYPE = new Class[]{Class.class}; private static final Class URLHANDLERS_CLASS = URLHandlers.class; private static final SecureAction m_secureAction = new SecureAction(); private static volatile SecurityManagerEx m_sm = null; private static volatile URLHandlers m_handler = null; // This maps classloaders of URLHandlers in other classloaders to lists of // their frameworks. private final static Map m_classloaderToFrameworkLists = new HashMap(); // The list to hold all enabled frameworks registered with this handlers private static final List m_frameworks = new ArrayList(); private static int m_counter = 0; private static Map m_contentHandlerCache = null; private static Map m_streamHandlerCache = null; private static URLStreamHandlerFactory m_streamHandlerFactory; private static ContentHandlerFactory m_contentHandlerFactory; private static final String STREAM_HANDLER_PACKAGE_PROP = "java.protocol.handler.pkgs"; private static final String DEFAULT_STREAM_HANDLER_PACKAGE = "sun.net.www.protocol|com.ibm.oti.net.www.protocol|gnu.java.net.protocol|wonka.net|com.acunia.wonka.net|org.apache.harmony.luni.internal.net.www.protocol|weblogic.utils|weblogic.net|javax.net.ssl|COM.newmonics.www.protocols"; private static Object m_rootURLHandlers; private static final String m_streamPkgs; private static final Map m_builtIn = new HashMap(); private static final boolean m_loaded; static { String pkgs = new SecureAction().getSystemProperty(STREAM_HANDLER_PACKAGE_PROP, ""); m_streamPkgs = (pkgs.equals("")) ? DEFAULT_STREAM_HANDLER_PACKAGE : pkgs + "|" + DEFAULT_STREAM_HANDLER_PACKAGE; m_loaded = (null != URLHandlersStreamHandlerProxy.class) && (null != URLHandlersContentHandlerProxy.class) && (null != URLStreamHandlerService.class); } private static final Map m_handlerToURL = new HashMap(); private void init(String protocol, URLStreamHandlerFactory factory) { try { URLStreamHandler handler = getBuiltInStreamHandler(protocol, factory); if (handler != null) { URL url = new URL(protocol, null, -1, "", handler); m_handlerToURL.put(handler, url); } } catch (Throwable ex) { // Ignore, this is a best effort (maybe log it or something). } } /** *

* Only one instance of this class is created per classloader * and that one instance is registered as the stream and content handler * factories for the JVM. Unless, we already register one from a different * classloader. In this case we attach to this root. *

**/ private URLHandlers() { m_sm = new SecurityManagerEx(); synchronized (URL.class) { URLStreamHandlerFactory currentFactory = null; try { currentFactory = (URLStreamHandlerFactory) m_secureAction.swapStaticFieldIfNotClass(URL.class, URLStreamHandlerFactory.class, URLHANDLERS_CLASS, "streamHandlerLock"); } catch (Throwable ex) { // Ignore, this is a best effort (maybe log it or something) } init("file", currentFactory); init("ftp", currentFactory); init("http", currentFactory); init("https", currentFactory); try { getBuiltInStreamHandler("jar", currentFactory); } catch (Throwable ex) { // Ignore, this is a best effort (maybe log it or something) } if (currentFactory != null) { try { URL.setURLStreamHandlerFactory(currentFactory); } catch (Throwable ex) { // Ignore, this is a best effort (maybe log it or something) } } try { URL.setURLStreamHandlerFactory(this); m_streamHandlerFactory = this; m_rootURLHandlers = this; // try to flush the cache (gnu/classpath doesn't do it itself) try { m_secureAction.flush(URL.class, URL.class); } catch (Throwable t) { // Not much we can do } } catch (Error err) { try { // there already is a factory set so try to swap it with ours. m_streamHandlerFactory = (URLStreamHandlerFactory) m_secureAction.swapStaticFieldIfNotClass(URL.class, URLStreamHandlerFactory.class, URLHANDLERS_CLASS, "streamHandlerLock"); if (m_streamHandlerFactory == null) { throw err; } if (!m_streamHandlerFactory.getClass().getName().equals(URLHANDLERS_CLASS.getName())) { URL.setURLStreamHandlerFactory(this); m_rootURLHandlers = this; } else if (URLHANDLERS_CLASS != m_streamHandlerFactory.getClass()) { try { m_secureAction.invoke( m_secureAction.getDeclaredMethod(m_streamHandlerFactory.getClass(), "registerFrameworkListsForContextSearch", new Class[]{ClassLoader.class, List.class}), m_streamHandlerFactory, new Object[]{ URLHANDLERS_CLASS.getClassLoader(), m_frameworks }); m_rootURLHandlers = m_streamHandlerFactory; } catch (Exception ex) { throw new RuntimeException(ex.getMessage()); } } } catch (Exception e) { throw err; } } try { URLConnection.setContentHandlerFactory(this); m_contentHandlerFactory = this; // try to flush the cache (gnu/classpath doesn't do it itself) try { m_secureAction.flush(URLConnection.class, URLConnection.class); } catch (Throwable t) { // Not much we can do } } catch (Error err) { // there already is a factory set so try to swap it with ours. try { m_contentHandlerFactory = (ContentHandlerFactory) m_secureAction.swapStaticFieldIfNotClass( URLConnection.class, ContentHandlerFactory.class, URLHANDLERS_CLASS, null); if (m_contentHandlerFactory == null) { throw err; } if (!m_contentHandlerFactory.getClass().getName().equals( URLHANDLERS_CLASS.getName())) { URLConnection.setContentHandlerFactory(this); } } catch (Exception ex) { throw err; } } } // are we not the new root? if (!((m_streamHandlerFactory == this) || !URLHANDLERS_CLASS.getName().equals( m_streamHandlerFactory.getClass().getName()))) { m_sm = null; m_handlerToURL.clear(); m_builtIn.clear(); } } static void registerFrameworkListsForContextSearch(ClassLoader index, List frameworkLists) { synchronized (URL.class) { synchronized (m_classloaderToFrameworkLists) { m_classloaderToFrameworkLists.put(index, frameworkLists); } } } static void unregisterFrameworkListsForContextSearch(ClassLoader index) { synchronized (URL.class) { synchronized (m_classloaderToFrameworkLists) { m_classloaderToFrameworkLists.remove(index); if (m_classloaderToFrameworkLists.isEmpty() ) { synchronized (m_frameworks) { if (m_frameworks.isEmpty()) { try { m_secureAction.swapStaticFieldIfNotClass(URL.class, URLStreamHandlerFactory.class, null, "streamHandlerLock"); } catch (Exception ex) { // TODO log this ex.printStackTrace(); } if (m_streamHandlerFactory.getClass() != URLHANDLERS_CLASS) { URL.setURLStreamHandlerFactory(m_streamHandlerFactory); } try { m_secureAction.swapStaticFieldIfNotClass( URLConnection.class, ContentHandlerFactory.class, null, null); } catch (Exception ex) { // TODO log this ex.printStackTrace(); } if (m_contentHandlerFactory.getClass() != URLHANDLERS_CLASS) { URLConnection.setContentHandlerFactory(m_contentHandlerFactory); } } } } } } } private URLStreamHandler getBuiltInStreamHandler(String protocol, URLStreamHandlerFactory factory) { synchronized (m_builtIn) { if (m_builtIn.containsKey(protocol)) { return (URLStreamHandler) m_builtIn.get(protocol); } } if (factory != null) { URLStreamHandler result = factory.createURLStreamHandler(protocol); if (result != null) { return addToCache(protocol, result); } } // Check for built-in handlers for the mime type. // Iterate over built-in packages. URLStreamHandler handler = loadBuiltInStreamHandler(protocol, null); if (handler == null) { handler = loadBuiltInStreamHandler(protocol, ClassLoader.getSystemClassLoader()); } return addToCache(protocol, handler); } private URLStreamHandler loadBuiltInStreamHandler(String protocol, ClassLoader classLoader) { StringTokenizer pkgTok = new StringTokenizer(m_streamPkgs, "| "); while (pkgTok.hasMoreTokens()) { String pkg = pkgTok.nextToken().trim(); String className = pkg + "." + protocol + ".Handler"; try { // If a built-in handler is found then cache and return it Class handler = m_secureAction.forName(className, classLoader); if (handler != null) { return (URLStreamHandler) handler.newInstance(); } } catch (Throwable ex) { // This could be a class not found exception or an // instantiation exception, not much we can do in either // case other than ignore it. } } // This is a workaround for android - Starting with 4.1 the built-in core handler // are not following the normal naming nore package schema :-( String androidHandler = null; if ("file".equalsIgnoreCase(protocol)) { androidHandler = "libcore.net.url.FileHandler"; } else if ("ftp".equalsIgnoreCase(protocol)) { androidHandler = "libcore.net.url.FtpHandler"; } else if ("http".equalsIgnoreCase(protocol)) { androidHandler = "libcore.net.http.HttpHandler"; } else if ("https".equalsIgnoreCase(protocol)) { androidHandler = "libcore.net.http.HttpsHandler"; } else if ("jar".equalsIgnoreCase(protocol)) { androidHandler = "libcore.net.url.JarHandler"; } if (androidHandler != null) { try { // If a built-in handler is found then cache and return it Class handler = m_secureAction.forName(androidHandler, classLoader); if (handler != null) { return (URLStreamHandler) handler.newInstance(); } } catch (Throwable ex) { // This could be a class not found exception or an // instantiation exception, not much we can do in either // case other than ignore it. } } return null; } private synchronized URLStreamHandler addToCache(String protocol, URLStreamHandler result) { if (!m_builtIn.containsKey(protocol)) { m_builtIn.put(protocol, result); return result; } return (URLStreamHandler) m_builtIn.get(protocol); } /** *

* This is a method implementation for the URLStreamHandlerFactory * interface. It simply creates a stream handler proxy object for the * specified protocol. It caches the returned proxy; therefore, subsequent * requests for the same protocol will receive the same handler proxy. *

* @param protocol the protocol for which a stream handler should be returned. * @return a stream handler proxy for the specified protocol. **/ public URLStreamHandler createURLStreamHandler(String protocol) { // See if there is a cached stream handler. // IMPLEMENTATION NOTE: Caching is not strictly necessary for // stream handlers since the Java runtime caches them. Caching is // performed for code consistency between stream and content // handlers and also because caching behavior may not be guaranteed // across different JRE implementations. URLStreamHandler handler = getFromStreamCache(protocol); if (handler != null) { return handler; } // If this is the framework's "bundle:" protocol, then return // a handler for that immediately, since no one else can be // allowed to deal with it. if (protocol.equals(FelixConstants.BUNDLE_URL_PROTOCOL)) { return addToStreamCache(protocol, new URLHandlersBundleStreamHandler(m_secureAction)); } handler = getBuiltInStreamHandler(protocol, (m_streamHandlerFactory != this) ? m_streamHandlerFactory : null); // If built-in content handler, then create a proxy handler. return addToStreamCache(protocol, new URLHandlersStreamHandlerProxy(protocol, m_secureAction, handler, (URL) m_handlerToURL.get(handler))); } /** *

* This is a method implementation for the ContentHandlerFactory * interface. It simply creates a content handler proxy object for the * specified mime type. It caches the returned proxy; therefore, subsequent * requests for the same content type will receive the same handler proxy. *

* @param mimeType the mime type for which a content handler should be returned. * @return a content handler proxy for the specified mime type. **/ public ContentHandler createContentHandler(String mimeType) { // See if there is a cached stream handler. // IMPLEMENTATION NOTE: Caching is not strictly necessary for // stream handlers since the Java runtime caches them. Caching is // performed for code consistency between stream and content // handlers and also because caching behavior may not be guaranteed // across different JRE implementations. ContentHandler handler = getFromContentCache(mimeType); if (handler != null) { return handler; } return addToContentCache(mimeType, new URLHandlersContentHandlerProxy(mimeType, m_secureAction, (m_contentHandlerFactory != this) ? m_contentHandlerFactory : null)); } private synchronized ContentHandler addToContentCache(String mimeType, ContentHandler handler) { if (m_contentHandlerCache == null) { m_contentHandlerCache = new HashMap(); } return (ContentHandler) addToCache(m_contentHandlerCache, mimeType, handler); } private synchronized ContentHandler getFromContentCache(String mimeType) { return (ContentHandler) ((m_contentHandlerCache != null) ? m_contentHandlerCache.get(mimeType) : null); } private synchronized URLStreamHandler addToStreamCache(String protocol, URLStreamHandler handler) { if (m_streamHandlerCache == null) { m_streamHandlerCache = new HashMap(); } return (URLStreamHandler) addToCache(m_streamHandlerCache, protocol, handler); } private synchronized URLStreamHandler getFromStreamCache(String protocol) { return (URLStreamHandler) ((m_streamHandlerCache != null) ? m_streamHandlerCache.get(protocol) : null); } private Object addToCache(Map cache, String key, Object value) { if (value == null) { return null; } Object result = cache.get(key); if (result == null) { cache.put(key, value); result = value; } return result; } /** *

* Static method that adds a framework instance to the centralized * instance registry. *

* @param framework the framework instance to be added to the instance * registry. * @param enable a flag indicating whether or not the framework wants to * enable the URL Handlers service. **/ public static void registerFrameworkInstance(Object framework, boolean enable) { boolean register = false; synchronized (m_frameworks) { // If the URL Handlers service is not going to be enabled, // then return immediately. if (enable) { // We need to create an instance if this is the first // time this method is called, which will set the handler // factories. if (m_handler == null ) { register = true; } else { m_frameworks.add(framework); m_counter++; } } else { m_counter++; } } if (register) { synchronized (URL.class) { synchronized (m_classloaderToFrameworkLists) { synchronized (m_frameworks) { if (m_handler == null ) { m_handler = new URLHandlers(); } m_frameworks.add(framework); m_counter++; } } } } } /** *

* Static method that removes a framework instance from the centralized * instance registry. *

* @param framework the framework instance to be removed from the instance * registry. **/ public static void unregisterFrameworkInstance(Object framework) { boolean unregister = false; synchronized (m_frameworks) { if (m_frameworks.contains(framework)) { if (m_frameworks.size() == 1 && m_handler != null) { unregister = true; } else { m_frameworks.remove(framework); m_counter--; } } else { m_counter--; } } if (unregister) { synchronized (URL.class) { synchronized (m_classloaderToFrameworkLists) { synchronized (m_frameworks) { m_frameworks.remove(framework); m_counter--; if (m_frameworks.isEmpty() && m_handler != null) { m_handler = null; try { m_secureAction.invoke(m_secureAction.getDeclaredMethod( m_rootURLHandlers.getClass(), "unregisterFrameworkListsForContextSearch", new Class[]{ ClassLoader.class}), m_rootURLHandlers, new Object[] {URLHANDLERS_CLASS.getClassLoader()}); } catch (Exception e) { // This should not happen e.printStackTrace(); } } } } } } } /** *

* This method returns the system bundle context for the caller. * It determines the appropriate system bundle by retrieving the * class call stack and find the first class that is loaded from * a bundle. It then checks to see which of the registered framework * instances owns the class and returns its system bundle context. *

* @return the system bundle context associated with the caller or * null if no associated framework was found. **/ public static Object getFrameworkFromContext() { // This is a hack. The idea is to return the only registered framework synchronized (m_classloaderToFrameworkLists) { if (m_classloaderToFrameworkLists.isEmpty()) { synchronized (m_frameworks) { if ((m_counter == 1) && (m_frameworks.size() == 1)) { return m_frameworks.get(0); } } } } // get the current class call stack. Class[] stack = m_sm.getClassContext(); // Find the first class that is loaded from a bundle. Class targetClass = null; for (int i = 0; i < stack.length; i++) { if (stack[i].getClassLoader() != null) { String name = stack[i].getClassLoader().getClass().getName(); if (name.startsWith("org.apache.felix.framework.ModuleImpl$ModuleClassLoader") || name.equals("org.apache.felix.framework.searchpolicy.ContentClassLoader") || name.startsWith("org.apache.felix.framework.BundleWiringImpl$BundleClassLoader")) { targetClass = stack[i]; break; } } } // If we found a class loaded from a bundle, then iterate // over the framework instances and see which framework owns // the bundle that loaded the class. if (targetClass != null) { synchronized (m_classloaderToFrameworkLists) { ClassLoader index = targetClass.getClassLoader().getClass().getClassLoader(); List frameworks = (List) m_classloaderToFrameworkLists.get( index); if ((frameworks == null) && (index == URLHANDLERS_CLASS.getClassLoader())) { frameworks = m_frameworks; } if (frameworks != null) { synchronized (frameworks) { // Check the registry of framework instances for (int i = 0; i < frameworks.size(); i++) { Object framework = frameworks.get(i); try { if (m_secureAction.invoke( m_secureAction.getDeclaredMethod(framework.getClass(), "getBundle", CLASS_TYPE), framework, new Object[]{targetClass}) != null) { return framework; } } catch (Exception ex) { // This should not happen but if it does there is // not much we can do other then ignore it. // Maybe log this or something. ex.printStackTrace(); } } } } } } return null; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy