These classes make up the core of the new URL Handlers service implementation.
git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@331741 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/org/apache/felix/framework/URLHandlers.java b/framework/src/org/apache/felix/framework/URLHandlers.java
new file mode 100644
index 0000000..8a2865d
--- /dev/null
+++ b/framework/src/org/apache/felix/framework/URLHandlers.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.*;
+import java.util.*;
+
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.felix.framework.util.SecurityManagerEx;
+import org.apache.felix.moduleloader.ModuleClassLoader;
+import org.osgi.framework.BundleContext;
+
+/**
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * 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:
+ * </p>
+ * <ol>
+ * <li>Caching behavior by the JVM of stream handlers does not give you
+ * a second chance to provide a handler.
+ * </li>
+ * <li>Due to the dynamic nature of OSGi services, handlers may appear at
+ * any time, so always creating a proxy makes sense.
+ * </li>
+ * <li>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.
+ * </li>
+ * </ol>
+ * <p>
+ * It is possible to disable the URL Handlers service by setting the
+ * <tt>framework.service.urlhandlers</tt> configuration property to <tt>false</tt>.
+ * 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., <tt>URL.setURLStreamHandlerFactory()</tt> and
+ * <tt>URLConnection.setContentHandlerFactory()</tt>). 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.
+ * </p>
+**/
+class URLHandlers implements URLStreamHandlerFactory, ContentHandlerFactory
+{
+ private static String m_lock = new String("string-lock");
+ private static SecurityManagerEx m_sm = null;
+ private static URLHandlers m_handler = null;
+ private static int m_frameworkCount = 0;
+ private static List m_frameworkList = null;
+ private static Map m_contentHandlerCache = null;
+ private static URLHandlersBundleStreamHandler m_bundleHandler = null;
+
+ /**
+ * <p>
+ * Only one instance of this class is created in a static initializer
+ * and that one instance is registered as the stream and content handler
+ * factories for the JVM.
+ * </p>
+ **/
+ private URLHandlers()
+ {
+System.out.println("SETTING HANDLERS");
+ // No one can create an instance, but we need an instance
+ // so we can set this as the stream and content handler factory.
+ URL.setURLStreamHandlerFactory(this);
+ URLConnection.setContentHandlerFactory(this);
+ }
+
+ /**
+ * <p>
+ * This is a method implementation for the <tt>URLStreamHandlerFactory</tt>
+ * interface. It simply creates a stream handler proxy object for the
+ * specified protocol. It does not perform caching of the return proxies,
+ * since this is done by the Java runtime.
+ * </p>
+ * @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)
+ {
+ synchronized (this)
+ {
+ // TODO: Determine the best way to handle internal handlers.
+ if (protocol.equals("file"))
+ {
+ return null;
+ }
+ else if (protocol.equals(FelixConstants.BUNDLE_URL_PROTOCOL))
+ {
+ if (m_bundleHandler == null)
+ {
+ m_bundleHandler = new URLHandlersBundleStreamHandler(null);
+ }
+ return m_bundleHandler;
+ }
+ return new URLHandlersStreamHandlerProxy(protocol);
+ }
+ }
+
+ /**
+ * <p>
+ * This is a method implementation for the <tt>ContentHandlerFactory</tt>
+ * interface. It simply creates a content handler proxy object for the
+ * specified mime type. It also performs caching of the return proxies,
+ * since this is not done by the Java runtime.
+ * </p>
+ * @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)
+ {
+ synchronized (m_lock)
+ {
+ // See if we have a cached content handler.
+ ContentHandler hdlr = (m_contentHandlerCache == null)
+ ? null
+ : (ContentHandler) m_contentHandlerCache.get(mimeType);
+ // If no cache content handler, then create one.
+ if (hdlr == null)
+ {
+ hdlr = new URLHandlersContentHandlerProxy(mimeType);
+ if (m_contentHandlerCache == null)
+ {
+ m_contentHandlerCache = new HashMap();
+ }
+ m_contentHandlerCache.put(mimeType, hdlr);
+ }
+ return hdlr;
+ }
+ }
+
+ /**
+ * <p>
+ * Static method that adds a framework instance to the centralized
+ * instance registry.
+ * </p>
+ * @param framework the framework instance to be added to the instance
+ * registry.
+ * @param context the system bundle context associated with the framework
+ * instance.
+ * @param enable a flag indicating whether or not the framework wants to
+ * enable the URL Handlers service.
+ **/
+ public static void registerInstance(
+ Felix framework, BundleContext context, boolean enable)
+ {
+ synchronized (m_lock)
+ {
+ // Increment framework instance count.
+ m_frameworkCount++;
+
+ // 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)
+ {
+ m_sm = new SecurityManagerEx();
+ m_handler = new URLHandlers();
+ }
+
+ // Create the framework list, if necessary, and add the
+ // new framework instance to it.
+ if (m_frameworkList == null)
+ {
+ m_frameworkList = new ArrayList();
+ }
+ m_frameworkList.add(framework);
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * Static method that removes a framework instance from the centralized
+ * instance registry.
+ * </p>
+ * @param framework the framework instance to be removed from the instance
+ * registry.
+ **/
+ public static void unregisterInstance(Felix framework)
+ {
+ synchronized (m_lock)
+ {
+ m_frameworkCount--;
+ if (m_frameworkList != null)
+ {
+ m_frameworkList.remove(framework);
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * 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.
+ * </p>
+ * @return the system bundle context associated with the caller or
+ * <tt>null</tt> if no associated framework was found.
+ **/
+ public static Felix getFrameworkFromContext()
+ {
+ synchronized (m_lock)
+ {
+ if (m_frameworkList != null)
+ {
+ // First, perform a simple short cut, if there is only
+ // one framework instance registered, assume that this
+ // is the bundle context to be returned and just return
+ // it immediately.
+ if ((m_frameworkList.size() == 1) && (m_frameworkCount == 1))
+ {
+ return (Felix) m_frameworkList.get(0);
+ }
+
+ // If there is more than one registered framework instance,
+ // then 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() instanceof ModuleClassLoader)
+ {
+ 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)
+ {
+ // Check the registry of framework instances
+ for (int i = 0; i < m_frameworkList.size(); i++)
+ {
+ if (((Felix) m_frameworkList.get(i)).isBundleClass(targetClass))
+ {
+ return (Felix) m_frameworkList.get(i);
+ }
+ }
+ }
+ }
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/org/apache/felix/framework/URLHandlersActivator.java b/framework/src/org/apache/felix/framework/URLHandlersActivator.java
new file mode 100644
index 0000000..8851a6a
--- /dev/null
+++ b/framework/src/org/apache/felix/framework/URLHandlersActivator.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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 org.apache.felix.framework.util.FelixConstants;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+
+/**
+ * <p>
+ * Simple activator class used by the system bundle to enable the
+ * URL Handlers service. The only purpose of this class is to call
+ * <tt>URLHandlers.registerInstance()</tt> when the framework is
+ * started and <tt>URLHandlers.unregisterInstance()</tt> when the
+ * framework is stopped.
+ *</p>
+**/
+class URLHandlersActivator implements BundleActivator
+{
+ private Felix m_framework = null;
+ private BundleContext m_context = null;
+
+ public URLHandlersActivator(Felix framework)
+ {
+ m_framework = framework;
+ }
+
+ //
+ // Bundle activator methods.
+ //
+
+ public void start(BundleContext context)
+ {
+ m_context = context;
+ // Only register the framework with the URL Handlers service
+ // if the service is enabled.
+ boolean enable = (m_framework.getConfig().get(
+ FelixConstants.SERVICE_URLHANDLERS_PROP) == null)
+ ? true
+ : !m_framework.getConfig().get(FelixConstants.SERVICE_URLHANDLERS_PROP).equals("false");
+ URLHandlers.registerInstance(m_framework, m_context, enable);
+ }
+
+ public void stop(BundleContext context)
+ {
+ URLHandlers.unregisterInstance(m_framework);
+ }
+}
\ No newline at end of file
diff --git a/framework/src/org/apache/felix/framework/URLHandlersBundleStreamHandler.java b/framework/src/org/apache/felix/framework/URLHandlersBundleStreamHandler.java
new file mode 100644
index 0000000..19f017a
--- /dev/null
+++ b/framework/src/org/apache/felix/framework/URLHandlersBundleStreamHandler.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.io.IOException;
+import java.net.*;
+
+class URLHandlersBundleStreamHandler extends URLStreamHandler
+{
+ private Felix m_framework = null;
+
+ public URLHandlersBundleStreamHandler(Felix framework)
+ {
+ m_framework = framework;
+ }
+
+ protected synchronized URLConnection openConnection(URL url) throws IOException
+ {
+ return new URLHandlersBundleURLConnection(url, m_framework);
+ }
+}
\ No newline at end of file
diff --git a/framework/src/org/apache/felix/framework/URLHandlersBundleURLConnection.java b/framework/src/org/apache/felix/framework/URLHandlersBundleURLConnection.java
new file mode 100644
index 0000000..551373e
--- /dev/null
+++ b/framework/src/org/apache/felix/framework/URLHandlersBundleURLConnection.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.Permission;
+
+class URLHandlersBundleURLConnection extends URLConnection
+{
+ private Felix m_framework;
+ private int m_contentLength;
+ private long m_contentTime;
+ private String m_contentType;
+ private InputStream m_is;
+
+ public URLHandlersBundleURLConnection(URL url, Felix framework)
+ {
+ super(url);
+ m_framework = framework;
+ }
+
+ public void connect() throws IOException
+ {
+ if (!connected)
+ {
+ // If we don't have a framework instance, try to find
+ // one from the call context.
+ if (m_framework == null)
+ {
+ m_framework = URLHandlers.getFrameworkFromContext();
+ }
+
+ // If the framework has disabled the URL Handlers service,
+ // then it will not be found so just return null.
+ if (m_framework == null)
+ {
+ throw new IOException("Unable to find framework instance from context.");
+ }
+
+ m_is = m_framework.getBundleResourceInputStream(url);
+ m_contentLength = m_is.available();
+ m_contentTime = 0L;
+ m_contentType = URLConnection.guessContentTypeFromName(url.getFile());
+ connected = true;
+ }
+ }
+
+ public InputStream getInputStream()
+ throws IOException
+ {
+ if (!connected)
+ {
+ connect();
+ }
+ return m_is;
+ }
+
+ public int getContentLength()
+ {
+ if (!connected)
+ {
+ try
+ {
+ connect();
+ }
+ catch(IOException ex)
+ {
+ return -1;
+ }
+ }
+ return m_contentLength;
+ }
+
+ public long getLastModified()
+ {
+ if (!connected)
+ {
+ try
+ {
+ connect();
+ }
+ catch(IOException ex)
+ {
+ return 0;
+ }
+ }
+ if (m_contentTime != -1L)
+ {
+ return m_contentTime;
+ }
+ else
+ {
+ return 0L;
+ }
+ }
+
+ public String getContentType()
+ {
+ if (!connected)
+ {
+ try
+ {
+ connect();
+ }
+ catch (IOException ex)
+ {
+ return null;
+ }
+ }
+ return m_contentType;
+ }
+
+ public Permission getPermission()
+ {
+ // TODO: This should probably return a FilePermission
+ // to access the bundle JAR file, but we don't have the
+ // necessary information here to construct the absolute
+ // path of the JAR file...so it would take some
+ // re-arranging to get this to work.
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/framework/src/org/apache/felix/framework/URLHandlersContentHandlerProxy.java b/framework/src/org/apache/felix/framework/URLHandlersContentHandlerProxy.java
new file mode 100644
index 0000000..42cfefe
--- /dev/null
+++ b/framework/src/org/apache/felix/framework/URLHandlersContentHandlerProxy.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.io.IOException;
+import java.net.ContentHandler;
+import java.net.URLConnection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.url.URLConstants;
+
+/**
+ * <p>
+ * This class implements a content handler proxy. When the content handler
+ * proxy instance is created, it is associated with a particular mime type
+ * and will answer all future requests for content of that type. It does
+ * not directly handle the content requests, but delegates the requests to
+ * an underlying content handler service.
+ * </p>
+ * <p>
+ * The proxy for a particular mime type is used for all framework instances
+ * that may contain their own content handler services. When performing a
+ * content handler operation, the proxy retrieves the handler service from
+ * the framework instance associated with the current call stack and delegates
+ * the call to the handler service.
+ * </p>
+ * <p>
+ * The proxy will create simple content handler service trackers for each
+ * framework instance. The trackers will listen to service events in its
+ * respective framework instance to maintain a reference to the "best"
+ * content handler service at any given time.
+ * </p>
+**/
+class URLHandlersContentHandlerProxy extends ContentHandler
+{
+ private Map m_trackerMap = new HashMap();
+ private String m_mimeType = null;
+
+ public URLHandlersContentHandlerProxy(String mimeType)
+ {
+ m_mimeType = mimeType;
+ }
+
+ //
+ // ContentHandler interface method.
+ //
+
+ public synchronized Object getContent(URLConnection urlc) throws IOException
+ {
+ ContentHandler svc = getContentHandlerService();
+ if (svc == null)
+ {
+ throw new IOException("Content handler unavailable: "
+ + urlc.getContentType());
+ }
+ return svc.getContent(urlc);
+ }
+
+ /**
+ * <p>
+ * Private method to retrieve the content handler service from the
+ * framework instance associated with the current call stack. A
+ * simple service tracker is created and cached for the associated
+ * framework instance when this method is called.
+ * </p>
+ * @return the content handler service from the framework instance
+ * associated with the current call stack or <tt>null</tt>
+ * is no service is available.
+ **/
+ private ContentHandler getContentHandlerService()
+ {
+ // Get the framework instance associated with call stack.
+ Felix framework = URLHandlers.getFrameworkFromContext();
+
+ // If the framework has disabled the URL Handlers service,
+ // then it will not be found so just return null.
+ if (framework == null)
+ {
+ return null;
+ }
+
+ // Get the service tracker for the framework instance or create one.
+ URLHandlersServiceTracker tracker =
+ (URLHandlersServiceTracker) m_trackerMap.get(framework);
+ if (tracker == null)
+ {
+ // Get the framework's system bundle context.
+ BundleContext context =
+ ((SystemBundleActivator)
+ ((SystemBundle) framework.getBundle(0)).getActivator())
+ .getBundleContext();
+ // Create a filter for the mime type.
+ String filter =
+ "(&(objectClass="
+ + ContentHandler.class.getName()
+ + ")("
+ + URLConstants.URL_CONTENT_MIMETYPE
+ + "="
+ + m_mimeType
+ + "))";
+ // Create a simple service tracker for the framework.
+ tracker = new URLHandlersServiceTracker(context, filter);
+ // Cache the simple service tracker.
+ m_trackerMap.put(framework, tracker);
+ }
+ return (ContentHandler) tracker.getService();
+ }
+}
\ No newline at end of file
diff --git a/framework/src/org/apache/felix/framework/URLHandlersServiceTracker.java b/framework/src/org/apache/felix/framework/URLHandlersServiceTracker.java
new file mode 100644
index 0000000..13e7861
--- /dev/null
+++ b/framework/src/org/apache/felix/framework/URLHandlersServiceTracker.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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 org.apache.felix.framework.util.FelixConstants;
+import org.osgi.framework.*;
+
+/**
+ * <p>
+ * This class implements a simple service tracker that maintains a
+ * service object reference to the "best" service available at any
+ * given time that matches the filter associated with the tracker.
+ * The best service is the one with the one with the highest ranking
+ * and lowest service identifier.
+ *</p>
+**/
+class URLHandlersServiceTracker
+{
+ private BundleContext m_context = null;
+ private String m_filter = null;
+ private ServiceReference m_ref = null;
+ private Object m_svcObj = null;
+ private long m_id = -1;
+ private int m_rank = -1;
+
+ /**
+ * <p>
+ * Creates a simple service tracker associated with the specified bundle
+ * context for services matching the specified filter.
+ * </p>
+ * @param context the bundle context used for tracking services.
+ * @param filter the filter used for matching services.
+ **/
+ public URLHandlersServiceTracker(BundleContext context, String filter)
+ {
+ m_context = context;
+ m_filter = filter;
+
+ synchronized (this)
+ {
+ // 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)
+ {
+ 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);
+ }
+ catch (InvalidSyntaxException ex)
+ {
+ System.out.println("Cannot add service listener." + ex);
+ }
+
+ // Select the best service object.
+ selectBestService();
+
+ } // End of synchronized block.
+ }
+
+ public Object getService()
+ {
+ return m_svcObj;
+ }
+
+ /**
+ * <p>
+ * This method selects the highest ranking service object with the
+ * lowest service identifier out of the services selected by the
+ * service filter associated with this proxy. This method is called
+ * to initialize the proxy and any time when the service object
+ * being used is unregistered. If there is an existing service
+ * selected when this method is called, it will unget the existing
+ * service before selecting the best available service.
+ * </p>
+ **/
+ private void selectBestService()
+ {
+ // If there is an existing service, then unget it.
+ if (m_ref != null)
+ {
+ m_context.ungetService(m_ref);
+ m_ref = null;
+ m_svcObj = null;
+ m_id = -1;
+ m_rank = -1;
+ }
+
+ try
+ {
+ // Get all service references matching the service filter
+ // associated with this proxy.
+ ServiceReference[] refs = m_context.getServiceReferences(null, m_filter);
+ // Loop through all service references and select the reference
+ // with the highest ranking and lower service identifier.
+ for (int i = 0; (refs != null) && (i < refs.length); i++)
+ {
+ Long idObj = (Long) refs[i].getProperty(FelixConstants.SERVICE_ID);
+ Integer rankObj = (Integer) refs[i].getProperty(FelixConstants.SERVICE_RANKING);
+ // Ranking value defaults to zero.
+ int rank = (rankObj == null) ? 0 : rankObj.intValue();
+ if ((rank > m_rank) ||
+ ((rank == m_rank) && (idObj.longValue() < m_id)))
+ {
+ m_ref = refs[i];
+ m_rank = rank;
+ m_id = idObj.longValue();
+ }
+ }
+
+ // If a service reference was selected, then
+ // get its service object.
+ if (m_ref != null)
+ {
+ m_svcObj = m_context.getService(m_ref);
+ }
+ }
+ catch (InvalidSyntaxException ex)
+ {
+//TODO: LOGGER.
+ System.err.println("URLHandlersServiceTracker: " + ex);
+ }
+ }
+}
\ No newline at end of file
diff --git a/framework/src/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java b/framework/src/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
new file mode 100644
index 0000000..ed2e8bb
--- /dev/null
+++ b/framework/src/org/apache/felix/framework/URLHandlersStreamHandlerProxy.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ * Licensed 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.io.IOException;
+import java.net.*;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.service.url.*;
+
+/**
+ * <p>
+ * This class implements a stream handler proxy. When the stream handler
+ * proxy instance is created, it is associated with a particular protocol
+ * and will answer all future requests for handling of that stream type. It
+ * does not directly handle the stream handler requests, but delegates the
+ * requests to an underlying stream handler service.
+ * </p>
+ * <p>
+ * The proxy instance for a particular protocol is used for all framework
+ * instances that may contain their own stream handler services. When
+ * performing a stream handler operation, the proxy retrieves the handler
+ * service from the framework instance associated with the current call
+ * stack and delegates the call to the handler service.
+ * </p>
+ * <p>
+ * The proxy will create simple stream handler service trackers for each
+ * framework instance. The trackers will listen to service events in its
+ * respective framework instance to maintain a reference to the "best"
+ * stream handler service at any given time.
+ * </p>
+**/
+public class URLHandlersStreamHandlerProxy extends URLStreamHandler
+ implements URLStreamHandlerSetter
+{
+ private Map m_trackerMap = new HashMap();
+ private String m_protocol = null;
+
+ public URLHandlersStreamHandlerProxy(String protocol)
+ {
+ m_protocol = protocol;
+ }
+
+ //
+ // URLStreamHandler interface methods.
+ //
+
+ protected synchronized boolean equals(URL url1, URL url2)
+ {
+ URLStreamHandlerService svc = getStreamHandlerService();
+ if (svc == null)
+ {
+ throw new IllegalStateException(
+ "Unknown protocol: " + url1.getProtocol());
+ }
+ return svc.equals(url1, url2);
+ }
+
+ protected synchronized int getDefaultPort()
+ {
+ URLStreamHandlerService svc = getStreamHandlerService();
+ if (svc == null)
+ {
+ throw new IllegalStateException("Stream handler unavailable.");
+ }
+ return svc.getDefaultPort();
+ }
+
+ protected synchronized InetAddress getHostAddress(URL url)
+ {
+ URLStreamHandlerService svc = getStreamHandlerService();
+ if (svc == null)
+ {
+ throw new IllegalStateException(
+ "Unknown protocol: " + url.getProtocol());
+ }
+ return svc.getHostAddress(url);
+ }
+
+ protected synchronized int hashCode(URL url)
+ {
+ URLStreamHandlerService svc = getStreamHandlerService();
+ if (svc == null)
+ {
+ throw new IllegalStateException(
+ "Unknown protocol: " + url.getProtocol());
+ }
+ return svc.hashCode(url);
+ }
+
+ protected synchronized boolean hostsEqual(URL url1, URL url2)
+ {
+ URLStreamHandlerService svc = getStreamHandlerService();
+ if (svc == null)
+ {
+ throw new IllegalStateException(
+ "Unknown protocol: " + url1.getProtocol());
+ }
+ return svc.hostsEqual(url1, url2);
+ }
+
+ protected synchronized URLConnection openConnection(URL url) throws IOException
+ {
+ URLStreamHandlerService svc = getStreamHandlerService();
+ if (svc == null)
+ {
+ throw new MalformedURLException("Unknown protocol: " + url.toString());
+ }
+ return svc.openConnection(url);
+ }
+
+ protected synchronized void parseURL(URL url, String spec, int start, int limit)
+ {
+ URLStreamHandlerService svc = getStreamHandlerService();
+ if (svc == null)
+ {
+ throw new IllegalStateException(
+ "Unknown protocol: " + url.getProtocol());
+ }
+ svc.parseURL(this, url, spec, start, limit);
+ }
+
+ protected synchronized boolean sameFile(URL url1, URL url2)
+ {
+ URLStreamHandlerService svc = getStreamHandlerService();
+ if (svc == null)
+ {
+ throw new IllegalStateException(
+ "Unknown protocol: " + url1.getProtocol());
+ }
+ return svc.sameFile(url1, url2);
+ }
+
+ public void setURL(
+ URL url, String protocol, String host, int port, String authority,
+ String userInfo, String path, String query, String ref)
+ {
+ super.setURL(url, protocol, host, port, authority, userInfo, path, query, ref);
+ }
+
+ public void setURL(
+ URL url, String protocol, String host, int port, String file, String ref)
+ {
+ super.setURL(url, protocol, host, port, null, null, file, null, ref);
+ }
+
+ protected synchronized String toExternalForm(URL url)
+ {
+ URLStreamHandlerService svc = getStreamHandlerService();
+ if (svc == null)
+ {
+ throw new IllegalStateException(
+ "Unknown protocol: " + url.getProtocol());
+ }
+ return svc.toExternalForm(url);
+ }
+
+ /**
+ * <p>
+ * Private method to retrieve the stream handler service from the
+ * framework instance associated with the current call stack. A
+ * simple service tracker is created and cached for the associated
+ * framework instance when this method is called.
+ * </p>
+ * @return the stream handler service from the framework instance
+ * associated with the current call stack or <tt>null</tt>
+ * is no service is available.
+ **/
+ private URLStreamHandlerService getStreamHandlerService()
+ {
+ // Get the framework instance associated with call stack.
+ Felix framework = URLHandlers.getFrameworkFromContext();
+
+ // If the framework has disabled the URL Handlers service,
+ // then it will not be found so just return null.
+ if (framework == null)
+ {
+ return null;
+ }
+
+ // Get the service tracker for the framework instance or create one.
+ URLHandlersServiceTracker tracker =
+ (URLHandlersServiceTracker) m_trackerMap.get(framework);
+ if (tracker == null)
+ {
+ // Get the framework's system bundle context.
+ BundleContext context =
+ ((SystemBundleActivator)
+ ((SystemBundle) framework.getBundle(0)).getActivator())
+ .getBundleContext();
+ // Create a filter for the protocol.
+ String filter =
+ "(&(objectClass="
+ + URLStreamHandlerService.class.getName()
+ + ")("
+ + URLConstants.URL_HANDLER_PROTOCOL
+ + "="
+ + m_protocol
+ + "))";
+ // Create a simple service tracker for the framework.
+ tracker = new URLHandlersServiceTracker(context, filter);
+ // Cache the simple service tracker.
+ m_trackerMap.put(framework, tracker);
+ }
+ return (URLStreamHandlerService) tracker.getService();
+ }
+}
\ No newline at end of file