Refactor the URLHandlers implementation to address several issue. 

First, there is a Linkage-Error when felix is embedded inside tomcat due to the WebAppClassloader creating urls while creating url handlers which gets us into a recursive attempt to create a handler resulting in the error (see FELIX-842). This patch address this by creating a couple of known built-in handlers up front (i.e., http, https, ftp, jar, file) which we can fallback to in case we get a Linkage-Error while trying to create the handler. Furthermore, a couple of classes are forced to be loaded eagerly. 

Second, setting a security manager when the framework already was started was causing a recursive lookup of handlers as well as a) not all security sensitive calls where done from inside a doPriv and b) invoking built-in handlers was causing a recursive attempt to create the url to the policy file (see FELIX-837). This patch adds the doPrivs and does make sure to only use setAccessible where needed and then (if possible) before the URLHandlers are registered. Additionally, we create urls for the known built-in handler up front to be able to set the handler of a new url without causing a security check. 

Third, rmi is sending the urls inside the URLclassloader that loaded the class of the object to be send together with the object. This creates a problem when the server doesn't have it's own rmi codesource set because then the urls are deserialized and one of them is our extension url (i.e., the url we added to the classloader that loaded felix to enable extension bundles). The problem is that the server doesn't have a handler for the protocol of that url (see FELIX-844). The fix is to use an http: url which points to an invalid address and port 9 (Discard). This way the url can be created on the server (it just doesn't work but thats ok). 

Finally, the URLHandlers didn't restore the previous URLStreamHandlerFactory after stopping Felix. This was a design limitation which should now be fixed (see FELIX-827).


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@728167 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
index f5da891..a35e106 100644
--- a/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
+++ b/framework/src/main/java/org/apache/felix/framework/ExtensionManager.java
@@ -21,6 +21,7 @@
 import java.io.IOException;
 import java.net.InetAddress;
 import java.io.InputStream;
+import java.net.JarURLConnection;
 import java.net.URL;
 import java.net.URLConnection;
 import java.net.URLStreamHandler;
@@ -37,6 +38,7 @@
 import java.util.Set;
 
 import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.SecurityManagerEx;
 import org.apache.felix.framework.util.Util;
 import org.apache.felix.framework.util.manifestparser.Capability;
 import org.apache.felix.framework.util.manifestparser.ManifestParser;
@@ -95,8 +97,8 @@
         try
         {
             Felix.m_secureAction.addURLToURLClassLoader(Felix.m_secureAction.createURL(
-                Felix.m_secureAction.createURL(null, "felix:", extensionManager),
-                "felix://extensions/", extensionManager),
+                Felix.m_secureAction.createURL(null, "http:", extensionManager),
+                "http://felix.extensions:9/", extensionManager),
                 Felix.class.getClassLoader());
         }
         catch (Exception ex)
diff --git a/framework/src/main/java/org/apache/felix/framework/URLHandlers.java b/framework/src/main/java/org/apache/felix/framework/URLHandlers.java
index 5b4e797..a0be37c 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlers.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlers.java
@@ -19,11 +19,25 @@
 package org.apache.felix.framework;
 
 import java.io.IOException;
-import java.net.*;
-import java.util.*;
+import java.net.ContentHandler;
+import java.net.ContentHandlerFactory;
+import java.net.MalformedURLException;
+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.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
 
 import org.apache.felix.framework.searchpolicy.ContentClassLoader;
-import org.apache.felix.framework.util.*;
+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;
 
 /**
  * <p>
@@ -83,7 +97,39 @@
     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)
+    {
+        try
+        {
+            m_handlerToURL.put(getBuiltInStreamHandler(protocol, null), new URL(protocol + ":") );
+        }
+        catch (MalformedURLException ex)
+        {
+            ex.printStackTrace();
+            // Ignore, this is a best effort (maybe log it or something).
+        }
+    }
+    
     /**
      * <p>
      * Only one instance of this class is created per classloader 
@@ -94,12 +140,19 @@
     **/
     private URLHandlers()
     {
+        init("file");
+        init("ftp");
+        init("http");
+        init("https");
+        getBuiltInStreamHandler("jar", null);
+        m_sm = new SecurityManagerEx();
         synchronized (URL.class)
         {
             try
             {
                 URL.setURLStreamHandlerFactory(this);
                 m_streamHandlerFactory = this;
+                m_rootURLHandlers = this;
             }
             catch (Error err)
             {
@@ -117,6 +170,7 @@
                     if (!m_streamHandlerFactory.getClass().getName().equals(URLHandlers.class.getName()))
                     {
                         URL.setURLStreamHandlerFactory(this);
+                        m_rootURLHandlers = this;
                     }
                     else if (URLHandlers.class != m_streamHandlerFactory.getClass())
                     {
@@ -128,6 +182,7 @@
                                 new Class[]{ClassLoader.class, List.class}), 
                                 m_streamHandlerFactory, new Object[]{ URLHandlers.class.getClassLoader(), 
                                     m_frameworks });
+                            m_rootURLHandlers = m_streamHandlerFactory;
                         }
                         catch (Exception ex)
                         {
@@ -172,11 +227,12 @@
             }
         }
         // are we not the new root?
-        if ((m_streamHandlerFactory == this) || !URLHandlers.class.getName().equals(
-            m_streamHandlerFactory.getClass().getName()))
+        if (!((m_streamHandlerFactory == this) || !URLHandlers.class.getName().equals(
+            m_streamHandlerFactory.getClass().getName())))
         {
-            // we only need a security manager in the root
-            m_sm = new SecurityManagerEx();
+            m_sm = null;
+            m_handlerToURL.clear();
+            m_builtIn.clear();
         }
     }
 
@@ -243,6 +299,60 @@
         }
     }
 
+    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.
+        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); 
+                if (handler != null)
+                {
+                    return addToCache(protocol, 
+                        (URLStreamHandler) handler.newInstance());
+                }
+            }
+            catch (Exception 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 addToCache(protocol, 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);
+    }
+    
     /**
      * <p>
      * This is a method implementation for the <tt>URLStreamHandlerFactory</tt>
@@ -275,42 +385,14 @@
             return addToStreamCache(protocol, 
                 new URLHandlersBundleStreamHandler(m_secureAction));
         }
-    
-        // If this is the framework's "felix:" extension protocol, then
-        // return the ExtensionManager.m_extensionManager handler for 
-        // that immediately - this is a workaround for certain jvms that
-        // do a toString() on the extension url we add to the global
-        // URLClassloader.
-        if (protocol.equals("felix"))
-        {
-            return addToStreamCache(protocol, new URLStreamHandler()
-            {
-                protected URLConnection openConnection(URL url)
-                    throws IOException
-                {
-                    Object framework = getFrameworkFromContext();
-                    
-                    try
-                    {
-                        Object handler =  m_secureAction.getDeclaredField(
-                            framework.getClass(),"m_extensionManager", framework);
 
-                        return (URLConnection) m_secureAction.invoke(
-                            m_secureAction.getMethod(handler.getClass(), 
-                            "openConnection", new Class[]{URL.class}), handler, 
-                            new Object[]{url});
-                    }
-                    catch (Exception ex)
-                    {
-                        throw new IOException(ex.getMessage());
-                    }
-                }
-            });
-        }
+       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, 
-            (m_streamHandlerFactory != this) ? m_streamHandlerFactory : null));
+        return addToStreamCache(protocol, 
+            new URLHandlersStreamHandlerProxy(protocol, m_secureAction, 
+                handler, (URL) m_handlerToURL.get(handler)));
     }
 
     /**
@@ -390,6 +472,23 @@
         return result;
     }
 
+    synchronized void flush()
+    {
+        if (m_streamHandlerCache != null)
+        {
+            for (Iterator iter = m_streamHandlerCache.values().iterator();iter.hasNext();)
+            {
+                ((URLHandlersStreamHandlerProxy) iter.next()).flush();
+            }
+        }
+        if (m_contentHandlerCache != null)
+        {
+            for (Iterator iter = m_contentHandlerCache.values().iterator();iter.hasNext();)
+            {
+                ((URLHandlersContentHandlerProxy) iter.next()).flush();
+            }
+        }
+    }
     /**
      * <p>
      * Static method that adds a framework instance to the centralized
@@ -434,27 +533,38 @@
         synchronized (m_frameworks)
         {
             m_counter--;
-            if (m_frameworks.remove(framework) && m_frameworks.isEmpty())
-            {
-                if (m_handler.m_streamHandlerFactory.getClass().getName().equals(
-                    URLHandlers.class.getName()))
+            if (m_frameworks.remove(framework))
+            {    
+                try
+                {
+                    m_secureAction.invoke(m_secureAction.getDeclaredMethod(
+                        m_rootURLHandlers.getClass(), 
+                       "flush", null), 
+                       m_rootURLHandlers, null);
+                }
+                catch (Exception e)
+                {
+                    // TODO: this should not happen
+                    e.printStackTrace();
+                }
+                if (m_frameworks.isEmpty())
                 {
                     try
                     {
                         m_secureAction.invoke(m_secureAction.getDeclaredMethod(
-                            m_handler.m_streamHandlerFactory.getClass(), 
+                            m_rootURLHandlers.getClass(), 
                             "unregisterFrameworkListsForContextSearch", 
                             new Class[]{ ClassLoader.class}), 
-                            m_handler.m_streamHandlerFactory, 
+                            m_rootURLHandlers,
                             new Object[] {URLHandlers.class.getClassLoader()});
                     }
                     catch (Exception e)
                     {
-                        // TODO this should not happen
+                        // TODO: this should not happen
                         e.printStackTrace();
                     }
+                    m_handler = null;
                 }
-                m_handler = null;
             }
         }
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java b/framework/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java
index 4f956b3..f9c6ecd 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlersContentHandlerProxy.java
@@ -23,6 +23,7 @@
 import java.net.ContentHandlerFactory;
 import java.net.URLConnection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.StringTokenizer;
 
@@ -55,8 +56,18 @@
 {
     private static final String CONTENT_HANDLER_PACKAGE_PROP = "java.content.handler.pkgs";
     private static final String DEFAULT_CONTENT_HANDLER_PACKAGE = "sun.net.www.content|com.ibm.oti.net.www.content|gnu.java.net.content|org.apache.harmony.luni.internal.net.www.content|COM.newmonics.www.content";
-
+    
     private static final Map m_builtIn = new HashMap();
+    private static final String m_pkgs;
+
+    static 
+    {
+        String pkgs = new SecureAction().getSystemProperty(CONTENT_HANDLER_PACKAGE_PROP, "");
+        m_pkgs = (pkgs.equals(""))
+            ? DEFAULT_CONTENT_HANDLER_PACKAGE
+            : pkgs + "|" + DEFAULT_CONTENT_HANDLER_PACKAGE;
+    }
+
     private final ContentHandlerFactory m_factory;
 
     private final Map m_trackerMap = new HashMap();
@@ -153,7 +164,7 @@
             }
             else
             {
-                result = (ContentHandler) m_action.invoke(
+                result = (ContentHandler) m_action.invokeDirect(
                     m_action.getMethod(tracker.getClass(), "getService", null), 
                     tracker, null);
             }
@@ -171,6 +182,38 @@
         }
     }
 
+    void flush()
+    {
+        synchronized (m_trackerMap)
+        {
+            for (Iterator iter = m_trackerMap.values().iterator(); iter.hasNext();)
+            {
+                unregister(iter.next());
+            }
+            m_trackerMap.clear();
+        }
+    }
+
+    private void unregister(Object tracker)
+    {
+        if (tracker instanceof URLHandlersServiceTracker)
+        {
+            ((URLHandlersServiceTracker) tracker).unregister();
+        }
+        else
+        {
+            try
+            {
+                m_action.invokeDirect(m_action.getMethod(tracker.getClass(), 
+                    "unregister", null), tracker, null);
+            }
+            catch (Exception e)
+            {
+                // Not much we can do - log this or something
+            }
+        }
+    }
+
     private ContentHandler getBuiltIn()
     {
         synchronized (m_builtIn)
@@ -189,16 +232,11 @@
             }
         }
         // Check for built-in handlers for the mime type.
-        String pkgs = m_action.getSystemProperty(CONTENT_HANDLER_PACKAGE_PROP, "");
-        pkgs = (pkgs.equals(""))
-            ? DEFAULT_CONTENT_HANDLER_PACKAGE
-            : pkgs + "|" + DEFAULT_CONTENT_HANDLER_PACKAGE;
-
         // Remove periods, slashes, and dashes from mime type.
         String fixedType = m_mimeType.replace('.', '_').replace('/', '.').replace('-', '_');
 
         // Iterate over built-in packages.
-        StringTokenizer pkgTok = new StringTokenizer(pkgs, "| ");
+        StringTokenizer pkgTok = new StringTokenizer(m_pkgs, "| ");
         while (pkgTok.hasMoreTokens())
         {
             String pkg = pkgTok.nextToken().trim();
diff --git a/framework/src/main/java/org/apache/felix/framework/URLHandlersServiceTracker.java b/framework/src/main/java/org/apache/felix/framework/URLHandlersServiceTracker.java
index 0df00fd..65c2e66 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlersServiceTracker.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlersServiceTracker.java
@@ -30,7 +30,7 @@
  * and lowest service identifier.
  *</p>
 **/
-class URLHandlersServiceTracker
+public class URLHandlersServiceTracker implements ServiceListener
 {
     private final BundleContext m_context;
     private final String m_filter;
@@ -57,47 +57,9 @@
         {
             // Add a service listener to track service changes
             // for services matching the specified filter.
-            ServiceListener sl = new ServiceListener() {
-                public void serviceChanged(ServiceEvent event)
-                {
-                    ServiceReference eventRef = event.getServiceReference();
-                    if ((event.getType() == ServiceEvent.REGISTERED) ||
-                        (event.getType() == ServiceEvent.MODIFIED))
-                    {
-                        synchronized (URLHandlersServiceTracker.this)
-                        {
-                            Long idObj = (Long) eventRef.getProperty(FelixConstants.SERVICE_ID);
-                            Integer rankObj = (Integer) eventRef.getProperty(FelixConstants.SERVICE_RANKING);
-                            int rank = (rankObj == null) ? 0 : rankObj.intValue();
-                            if ((rank > m_rank) ||
-                                ((rank == m_rank) && (idObj.longValue() < m_id)))
-                            {
-                                if (m_ref != null)
-                                {
-                                    m_context.ungetService(m_ref);
-                                }
-                                m_ref = eventRef;
-                                m_rank = rank;
-                                m_id = idObj.longValue();
-                                m_svcObj = m_context.getService(m_ref);
-                            }
-                        }
-                    }
-                    else if (event.getType() == ServiceEvent.UNREGISTERING)
-                    {
-                        synchronized (URLHandlersServiceTracker.this)
-                        {
-                            if (eventRef == m_ref)
-                            {
-                                selectBestService();
-                            }
-                        }
-                    }
-                }
-            };
             try
             {
-                m_context.addServiceListener(sl, m_filter);
+                m_context.addServiceListener(this, m_filter);
             }
             catch (InvalidSyntaxException ex)
             {
@@ -110,10 +72,52 @@
         } // End of synchronized block.
     }
 
+    public void unregister()
+    {
+        m_context.removeServiceListener(this);
+    }
+
     public Object getService()
     {
         return m_svcObj;
     }
+    
+    public void serviceChanged(ServiceEvent event)
+    {
+        ServiceReference eventRef = event.getServiceReference();
+        if ((event.getType() == ServiceEvent.REGISTERED) ||
+            (event.getType() == ServiceEvent.MODIFIED))
+        {
+            synchronized (URLHandlersServiceTracker.this)
+            {
+                Long idObj = (Long) eventRef.getProperty(FelixConstants.SERVICE_ID);
+                Integer rankObj = (Integer) eventRef.getProperty(FelixConstants.SERVICE_RANKING);
+                int rank = (rankObj == null) ? 0 : rankObj.intValue();
+                if ((rank > m_rank) ||
+                    ((rank == m_rank) && (idObj.longValue() < m_id)))
+                {
+                    if (m_ref != null)
+                    {
+                        m_context.ungetService(m_ref);
+                    }
+                    m_ref = eventRef;
+                    m_rank = rank;
+                    m_id = idObj.longValue();
+                    m_svcObj = m_context.getService(m_ref);
+                }
+            }
+        }
+        else if (event.getType() == ServiceEvent.UNREGISTERING)
+        {
+            synchronized (URLHandlersServiceTracker.this)
+            {
+                if (eventRef == m_ref)
+                {
+                    selectBestService();
+                }
+            }
+        }
+    }
 
     /**
      * <p>
diff --git a/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java b/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
index f4ff511..e5ee777 100644
--- a/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
+++ b/framework/src/main/java/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
@@ -24,6 +24,7 @@
 import java.lang.reflect.Proxy;
 import java.net.*;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.StringTokenizer;
 
@@ -57,39 +58,43 @@
 public final class URLHandlersStreamHandlerProxy extends URLStreamHandler
     implements URLStreamHandlerSetter, InvocationHandler
 {
-    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 final Method EQUALS;
     private static final Method GET_DEFAULT_PORT;
     private static final Method GET_HOST_ADDRESS;
     private static final Method HASH_CODE;
     private static final Method HOSTS_EQUAL;
     private static final Method OPEN_CONNECTION;
-    private static final Method PARSE_URL;
     private static final Method SAME_FILE;
     private static final Method TO_EXTERNAL_FORM;
     
     static {
         try
         {
+            SecureAction action = new SecureAction();
             EQUALS = URLStreamHandler.class.getDeclaredMethod("equals", 
                 new Class[]{URL.class, URL.class});
-            GET_DEFAULT_PORT = URLStreamHandler.class.getDeclaredMethod("getDefaultPort", null);
+            action.setAccesssible(EQUALS);
+            GET_DEFAULT_PORT = URLStreamHandler.class.getDeclaredMethod("getDefaultPort", 
+                (Class[]) null);
+            action.setAccesssible(GET_DEFAULT_PORT);
             GET_HOST_ADDRESS = URLStreamHandler.class.getDeclaredMethod(
                     "getHostAddress", new Class[]{URL.class});
+            action.setAccesssible(GET_HOST_ADDRESS);
             HASH_CODE = URLStreamHandler.class.getDeclaredMethod( 
                     "hashCode", new Class[]{URL.class});
+            action.setAccesssible(HASH_CODE);
             HOSTS_EQUAL = URLStreamHandler.class.getDeclaredMethod(
                     "hostsEqual", new Class[]{URL.class, URL.class});
+            action.setAccesssible(HOSTS_EQUAL);
             OPEN_CONNECTION = URLStreamHandler.class.getDeclaredMethod(
                     "openConnection", new Class[]{URL.class});
-            PARSE_URL = URLStreamHandler.class.getDeclaredMethod( 
-                    "parseURL", new Class[]{URL.class, String.class, Integer.TYPE, Integer.TYPE});
+            action.setAccesssible(OPEN_CONNECTION);
             SAME_FILE = URLStreamHandler.class.getDeclaredMethod(
                     "sameFile", new Class[]{URL.class, URL.class});
+            action.setAccesssible(SAME_FILE);
             TO_EXTERNAL_FORM = URLStreamHandler.class.getDeclaredMethod( 
                    "toExternalForm", new Class[]{URL.class});
+            action.setAccesssible(TO_EXTERNAL_FORM);
         }
         catch (Exception ex)
         {
@@ -97,22 +102,22 @@
             throw new RuntimeException(ex.getMessage());
         }
     }
-
-    private static final Map m_builtIn = new HashMap();
-    private final URLStreamHandlerFactory m_factory;
     
     private final Map m_trackerMap = new HashMap();
     private final String m_protocol;
     private final Object m_service;
     private final SecureAction m_action;
+    private final URLStreamHandler m_builtIn;
+    private final URL m_builtInURL;
 
-    public URLHandlersStreamHandlerProxy(String protocol, SecureAction action, 
-        URLStreamHandlerFactory factory)
+    public URLHandlersStreamHandlerProxy(String protocol, 
+        SecureAction action, URLStreamHandler builtIn, URL builtInURL)
     {
         m_protocol = protocol;
         m_service = null;
         m_action = action;
-        m_factory = factory;
+        m_builtIn = builtIn;
+        m_builtInURL = builtInURL;
     }
     
     private URLHandlersStreamHandlerProxy(Object service, SecureAction action)
@@ -120,7 +125,8 @@
         m_protocol = null;
         m_service = service;
         m_action = action;
-        m_factory = null;
+        m_builtIn = null;
+        m_builtInURL = null;
     }
 
     //
@@ -140,7 +146,7 @@
         }
         try 
         {
-            return ((Boolean) m_action.invoke(EQUALS, svc, new Object[]{url1, url2})).booleanValue();
+            return ((Boolean) EQUALS.invoke(svc, new Object[]{url1, url2})).booleanValue();
         } 
         catch (Exception ex)  
         {
@@ -162,7 +168,7 @@
         }
         try 
         {
-            return ((Integer) m_action.invoke(GET_DEFAULT_PORT, svc, null)).intValue();
+            return ((Integer) GET_DEFAULT_PORT.invoke(svc, null)).intValue();
         } 
         catch (Exception ex)  
         {
@@ -185,7 +191,7 @@
         }
         try 
         {
-            return (InetAddress) m_action.invoke(GET_HOST_ADDRESS, svc, new Object[]{url});
+            return (InetAddress) GET_HOST_ADDRESS.invoke(svc, new Object[]{url});
         } 
         catch (Exception ex)  
         {
@@ -208,7 +214,7 @@
         }
         try 
         {
-            return ((Integer) m_action.invoke(HASH_CODE, svc, new Object[]{url})).intValue();
+            return ((Integer) HASH_CODE.invoke(svc, new Object[]{url})).intValue();
         } 
         catch (Exception ex)  
         {
@@ -231,7 +237,7 @@
         }
         try 
         {
-            return ((Boolean) m_action.invoke(HOSTS_EQUAL, svc, new Object[]{url1, url2})).booleanValue();
+            return ((Boolean) HOSTS_EQUAL.invoke(svc, new Object[]{url1, url2})).booleanValue();
         } 
         catch (Exception ex)  
         {
@@ -253,7 +259,31 @@
         }
         try 
         {
-            return (URLConnection) m_action.invoke(OPEN_CONNECTION, svc, new Object[]{url});
+            if ("http".equals(url.getProtocol()) &&
+                "felix.extensions".equals(url.getHost()) &&
+                9 == url.getPort())
+            {
+                try
+                {
+                    Object handler =  m_action.getDeclaredField(
+                        ExtensionManager.class, "m_extensionManager", null);
+    
+                    if (handler != null)
+                    {
+                        return (URLConnection) m_action.invoke(
+                            m_action.getMethod(handler.getClass(), 
+                            "openConnection", new Class[]{URL.class}), handler, 
+                            new Object[]{url});
+                    }
+                    
+                    throw new IOException("Extensions not supported or ambiguous context.");
+                }
+                catch (Exception ex)
+                {
+                    throw new IOException(ex.getMessage());
+                }
+            }
+            return (URLConnection) OPEN_CONNECTION.invoke(svc, new Object[]{url});
         } 
         catch (Exception ex)  
         {
@@ -278,10 +308,18 @@
         {
             try 
             {
-                URL test = new URL(url, spec, (URLStreamHandler) svc);
-                
+                URL test = null;
+                if (m_builtInURL != null)
+                {
+                    test = new URL(new URL(m_builtInURL, url.toExternalForm()), spec);
+                }
+                else
+                {
+                    test = m_action.createURL(url, spec, (URLStreamHandler) svc);
+                }
+                    
                 super.setURL(url, test.getProtocol(), test.getHost(), test.getPort(),test.getAuthority(), 
-                        test.getUserInfo(), test.getPath(), test.getQuery(), test.getRef());
+                   test.getUserInfo(), test.getPath(), test.getQuery(), test.getRef());
             } 
             catch (Exception ex)  
             {
@@ -305,7 +343,7 @@
         }
         try 
         {
-            return ((Boolean) m_action.invoke(SAME_FILE, 
+            return ((Boolean) SAME_FILE.invoke( 
                 svc, new Object[]{url1, url2})).booleanValue();
         } 
         catch (Exception ex)  
@@ -346,7 +384,7 @@
         }
         try 
         {
-            return (String) m_action.invoke(TO_EXTERNAL_FORM, 
+            return (String) TO_EXTERNAL_FORM.invoke( 
                 svc, new Object[]{url});
         } 
         catch (Exception ex)  
@@ -355,7 +393,7 @@
             throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
         }
     }
-
+    
     /**
      * <p>
      * Private method to retrieve the stream handler service from the
@@ -369,22 +407,22 @@
     **/
     private Object getStreamHandlerService()
     {
-        // Get the framework instance associated with call stack.
-        Object framework = URLHandlers.getFrameworkFromContext();
-
-        if (framework == null) 
-        {
-            return getBuiltIn();
-        }
-
-        // Get the service tracker for the framework instance or create one.
-        Object tracker;
-        synchronized (m_trackerMap)
-        {
-            tracker = m_trackerMap.get(framework);
-        }
         try
         {
+            // Get the framework instance associated with call stack.
+            Object framework = URLHandlers.getFrameworkFromContext();
+    
+            if (framework == null) 
+            {
+                return m_builtIn;
+            }
+    
+            // Get the service tracker for the framework instance or create one.
+            Object tracker;
+            synchronized (m_trackerMap)
+            {
+                tracker = m_trackerMap.get(framework);
+            }
             if (tracker == null)
             {
                 // Create a filter for the protocol.
@@ -413,6 +451,7 @@
                     }
                     else
                     {
+                        unregister(tracker);
                         tracker = m_trackerMap.get(framework);
                     }
                 }
@@ -424,12 +463,12 @@
             }
             else
             {
-                service = m_action.invoke(m_action.getMethod(
+                service = m_action.invokeDirect(m_action.getMethod(
                     tracker.getClass(), "getService", null), tracker, null);
             }
             if (service == null) 
             {
-                return getBuiltIn();
+                return m_builtIn;
             }
             if (service instanceof URLStreamHandlerService)
             {
@@ -440,71 +479,47 @@
                 new Class[]{URLStreamHandlerService.class}, 
                 new URLHandlersStreamHandlerProxy(service, m_action));
         }
-        catch (Exception ex)
+        catch (ThreadDeath td)
         {
-            // TODO: log this or something
-            ex.printStackTrace();
-            return null;
+            throw td;
+        }
+        catch (Throwable t)
+        {
+         //   t.printStackTrace();
+            return m_builtIn;
         }
     }
-    
-    private URLStreamHandler getBuiltIn()
-    {
-        synchronized (m_builtIn)
-        {
-            if (m_builtIn.containsKey(m_protocol))
-            {
-                return (URLStreamHandler) m_builtIn.get(m_protocol);
-            }
-        }
-        if (m_factory != null)
-        {
-            URLStreamHandler result = m_factory.createURLStreamHandler(m_protocol);
-            if (result != null)
-            {
-                return addToCache(m_protocol, result);
-            }
-        }
-        // Check for built-in handlers for the mime type.
-        String pkgs = m_action.getSystemProperty(STREAM_HANDLER_PACKAGE_PROP, "");
-        pkgs = (pkgs.equals(""))
-            ? DEFAULT_STREAM_HANDLER_PACKAGE
-            : pkgs + "|" + DEFAULT_STREAM_HANDLER_PACKAGE;
 
-        // Iterate over built-in packages.
-        StringTokenizer pkgTok = new StringTokenizer(pkgs, "| ");
-        while (pkgTok.hasMoreTokens())
+    void flush()
+    {
+        synchronized (m_trackerMap)
         {
-            String pkg = pkgTok.nextToken().trim();
-            String className = pkg + "." + m_protocol + ".Handler";
+            for (Iterator iter = m_trackerMap.values().iterator(); iter.hasNext();)
+            {
+                unregister(iter.next());
+            }
+            m_trackerMap.clear();
+        }
+    }
+
+    private void unregister(Object tracker)
+    {
+        if (tracker instanceof URLHandlersServiceTracker)
+        {
+            ((URLHandlersServiceTracker) tracker).unregister();
+        }
+        else
+        {
             try
             {
-                // If a built-in handler is found then cache and return it
-                Class handler = m_action.forName(className); 
-                if (handler != null)
-                {
-                    return addToCache(m_protocol, 
-                        (URLStreamHandler) handler.newInstance());
-                }
+                m_action.invokeDirect(m_action.getMethod(tracker.getClass(), 
+                    "unregister", null), tracker, null);
             }
-            catch (Exception ex)
+            catch (Exception e)
             {
-                // This could be a class not found exception or an
-                // instantiation exception, not much we can do in either
-                // case other than ignore it.
+                // Not much we can do - log this or something
             }
         }
-        return addToCache(m_protocol, 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);
     }
 
     public Object invoke(Object obj, Method method, Object[] params)
@@ -521,7 +536,7 @@
                     m_service.getClass().getClassLoader(), new Class[]{types[0]}, 
                     (URLHandlersStreamHandlerProxy) params[0]);
             }
-            return m_action.invoke(m_action.getMethod(m_service.getClass(), 
+            return m_action.invokeDirect(m_action.getMethod(m_service.getClass(), 
                 method.getName(), types), m_service, params);
         } 
         catch (Exception ex)
diff --git a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
index 75c34ed..ac524fc 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/SecureAction.java
@@ -721,6 +721,27 @@
         }
     }
 
+    public void setAccesssible(Method method)
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.SET_ACCESSIBLE_ACTION, method);
+            try
+            {
+                AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw (RuntimeException) e.getException();
+            }
+        }
+        else
+        {
+            method.setAccessible(true);
+        }
+    }
+
     public Object invoke(Method method, Object target, Object[] params) throws Exception
     {
         if (System.getSecurityManager() != null)
@@ -742,6 +763,27 @@
             return method.invoke(target, params);
         }
     }
+    
+    public Object invokeDirect(Method method, Object target, Object[] params) throws Exception
+    {
+        if (System.getSecurityManager() != null)
+        {
+            Actions actions = (Actions) m_actions.get();
+            actions.set(Actions.INVOKE_DIRECTMETHOD_ACTION, method, target, params);
+            try
+            {
+                return AccessController.doPrivileged(actions, m_acc);
+            }
+            catch (PrivilegedActionException e)
+            {
+                throw e.getException();
+            }
+        }
+        else
+        {
+            return method.invoke(target, params);
+        }
+    }
 
     public Object invoke(Constructor constructor, Object[] params) throws Exception
     {
@@ -760,7 +802,6 @@
         }
         else
         {
-            constructor.setAccessible(true);
             return constructor.newInstance(params);
         }
     }
@@ -919,6 +960,8 @@
         public static final int SWAP_FIELD_ACTION = 30;
         public static final int GET_FIELD_ACTION = 31;
         public static final int GET_DECLAREDMETHOD_ACTION = 32;
+        public static final int SET_ACCESSIBLE_ACTION = 33;
+        public static final int INVOKE_DIRECTMETHOD_ACTION = 34;
 
         private int m_action = -1;
         private Object m_arg1 = null;
@@ -1118,9 +1161,12 @@
                 ((Method) arg1).setAccessible(true);
                 return ((Method) arg1).invoke(arg2, (Object[]) arg3);
             }
+            else if (action == INVOKE_DIRECTMETHOD_ACTION)
+            {
+                return ((Method) arg1).invoke(arg2, (Object[]) arg3);
+            }
             else if (action == INVOKE_CONSTRUCTOR_ACTION)
             {
-                ((Constructor) arg1).setAccessible(true);
                 return ((Constructor) arg1).newInstance((Object[]) arg2);
             }
             else if (action == SWAP_FIELD_ACTION)
@@ -1138,6 +1184,10 @@
             {
                 return ((Class) arg1).getDeclaredMethod((String) arg2, (Class[]) arg3);
             }
+            else if (action == SET_ACCESSIBLE_ACTION)
+            {
+                ((Method) arg1).setAccessible(true);
+            }
 
             return null;
         }