blob: 7cc5a83a4684672a67e4287fd8d3a089f8f078b6 [file] [log] [blame]
/*
* 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.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.*;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import org.apache.felix.framework.util.SecureAction;
import org.osgi.service.url.URLConstants;
import org.osgi.service.url.URLStreamHandlerService;
import org.osgi.service.url.URLStreamHandlerSetter;
/**
* <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 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
{
EQUALS = URLStreamHandler.class.getDeclaredMethod("equals",
new Class[]{URL.class, URL.class});
GET_DEFAULT_PORT = URLStreamHandler.class.getDeclaredMethod("getDefaultPort", null);
GET_HOST_ADDRESS = URLStreamHandler.class.getDeclaredMethod(
"getHostAddress", new Class[]{URL.class});
HASH_CODE = URLStreamHandler.class.getDeclaredMethod(
"hashCode", new Class[]{URL.class});
HOSTS_EQUAL = URLStreamHandler.class.getDeclaredMethod(
"hostsEqual", new Class[]{URL.class, URL.class});
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});
SAME_FILE = URLStreamHandler.class.getDeclaredMethod(
"sameFile", new Class[]{URL.class, URL.class});
TO_EXTERNAL_FORM = URLStreamHandler.class.getDeclaredMethod(
"toExternalForm", new Class[]{URL.class});
}
catch (Exception ex)
{
ex.printStackTrace();
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 boolean m_override;
public URLHandlersStreamHandlerProxy(String protocol, SecureAction action,
URLStreamHandlerFactory factory, boolean override)
{
m_protocol = protocol;
m_service = null;
m_action = action;
m_factory = factory;
m_override = override;
}
private URLHandlersStreamHandlerProxy(Object service, SecureAction action)
{
m_protocol = null;
m_service = service;
m_action = action;
m_factory = null;
m_override = false;
}
//
// URLStreamHandler interface methods.
//
protected boolean equals(URL url1, URL url2)
{
Object svc = getStreamHandlerService();
if (svc == null)
{
throw new IllegalStateException(
"Unknown protocol: " + url1.getProtocol());
}
if (svc instanceof URLStreamHandlerService)
{
return ((URLStreamHandlerService) svc).equals(url1, url2);
}
try
{
return ((Boolean) m_action.invoke(EQUALS, svc, new Object[]{url1, url2})).booleanValue();
}
catch (Exception ex)
{
ex.printStackTrace();
throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
}
}
protected int getDefaultPort()
{
Object svc = getStreamHandlerService();
if (svc == null)
{
throw new IllegalStateException("Stream handler unavailable.");
}
if (svc instanceof URLStreamHandlerService)
{
return ((URLStreamHandlerService) svc).getDefaultPort();
}
try
{
return ((Integer) m_action.invoke(GET_DEFAULT_PORT, svc, null)).intValue();
}
catch (Exception ex)
{
ex.printStackTrace();
throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
}
}
protected InetAddress getHostAddress(URL url)
{
Object svc = getStreamHandlerService();
if (svc == null)
{
throw new IllegalStateException(
"Unknown protocol: " + url.getProtocol());
}
if (svc instanceof URLStreamHandlerService)
{
return ((URLStreamHandlerService) svc).getHostAddress(url);
}
try
{
return (InetAddress) m_action.invoke(GET_HOST_ADDRESS, svc, new Object[]{url});
}
catch (Exception ex)
{
ex.printStackTrace();
throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
}
}
protected int hashCode(URL url)
{
Object svc = getStreamHandlerService();
if (svc == null)
{
throw new IllegalStateException(
"Unknown protocol: " + url.getProtocol());
}
if (svc instanceof URLStreamHandlerService)
{
return ((URLStreamHandlerService) svc).hashCode(url);
}
try
{
return ((Integer) m_action.invoke(HASH_CODE, svc, new Object[]{url})).intValue();
}
catch (Exception ex)
{
ex.printStackTrace();
throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
}
}
protected boolean hostsEqual(URL url1, URL url2)
{
Object svc = getStreamHandlerService();
if (svc == null)
{
throw new IllegalStateException(
"Unknown protocol: " + url1.getProtocol());
}
if (svc instanceof URLStreamHandlerService)
{
return ((URLStreamHandlerService) svc).hostsEqual(url1, url2);
}
try
{
return ((Boolean) m_action.invoke(HOSTS_EQUAL, svc, new Object[]{url1, url2})).booleanValue();
}
catch (Exception ex)
{
ex.printStackTrace();
throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
}
}
protected URLConnection openConnection(URL url) throws IOException
{
Object svc = getStreamHandlerService();
if (svc == null)
{
throw new MalformedURLException("Unknown protocol: " + url.toString());
}
if (svc instanceof URLStreamHandlerService)
{
return ((URLStreamHandlerService) svc).openConnection(url);
}
try
{
return (URLConnection) m_action.invoke(OPEN_CONNECTION, svc, new Object[]{url});
}
catch (Exception ex)
{
ex.printStackTrace();
throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
}
}
protected void parseURL(URL url, String spec, int start, int limit)
{
Object svc = getStreamHandlerService();
if (svc == null)
{
throw new IllegalStateException(
"Unknown protocol: " + url.getProtocol());
}
if (svc instanceof URLStreamHandlerService)
{
((URLStreamHandlerService) svc).parseURL(this, url, spec, start, limit);
}
else
{
try
{
URL test = new URL(null, toExternalForm(url, svc), (URLStreamHandler) svc);
m_action.invoke(PARSE_URL,
svc, new Object[]{test, spec, new Integer(start), new Integer(limit)});
super.setURL(url, test.getProtocol(), test.getHost(), test.getPort(),test.getAuthority(),
test.getUserInfo(), test.getPath(), test.getQuery(), test.getRef());
}
catch (Exception ex)
{
ex.printStackTrace();
throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
}
}
}
protected boolean sameFile(URL url1, URL url2)
{
Object svc = getStreamHandlerService();
if (svc == null)
{
throw new IllegalStateException(
"Unknown protocol: " + url1.getProtocol());
}
if (svc instanceof URLStreamHandlerService)
{
return ((URLStreamHandlerService) svc).sameFile(url1, url2);
}
try
{
return ((Boolean) m_action.invoke(SAME_FILE,
svc, new Object[]{url1, url2})).booleanValue();
}
catch (Exception ex)
{
ex.printStackTrace();
throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
}
}
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 String toExternalForm(URL url)
{
return toExternalForm(url, getStreamHandlerService());
}
private String toExternalForm(URL url, Object svc)
{
if (svc == null)
{
throw new IllegalStateException(
"Unknown protocol: " + url.getProtocol());
}
if (svc instanceof URLStreamHandlerService)
{
return ((URLStreamHandlerService) svc).toExternalForm(url);
}
try
{
return (String) m_action.invoke(TO_EXTERNAL_FORM,
svc, new Object[]{url});
}
catch (Exception ex)
{
ex.printStackTrace();
throw new IllegalStateException("Stream handler unavailable due to: " + ex.getMessage());
}
}
/**
* <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 Object getStreamHandlerService()
{
// Get the framework instance associated with call stack.
Object framework = URLHandlers.getFrameworkFromContext();
if (framework == null)
{
return m_override ? getBuiltIn() : null;
}
// Get the service tracker for the framework instance or create one.
Object tracker;
synchronized (m_trackerMap)
{
tracker = m_trackerMap.get(framework);
}
try
{
if (tracker == null)
{
// 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 = m_action.invoke(m_action.getConstructor(
framework.getClass().getClassLoader().loadClass(
URLHandlersServiceTracker.class.getName()),
new Class[]{framework.getClass().getClassLoader().loadClass(
Felix.class.getName()), String.class}),
new Object[]{framework, filter});
// Cache the simple service tracker.
synchronized (m_trackerMap)
{
if (!m_trackerMap.containsKey(framework))
{
m_trackerMap.put(framework, tracker);
}
else
{
tracker = m_trackerMap.get(framework);
}
}
}
Object service;
if (tracker instanceof URLHandlersServiceTracker)
{
service = ((URLHandlersServiceTracker) tracker).getService();
}
else
{
service = m_action.invoke(m_action.getMethod(
tracker.getClass(), "getService", null), tracker, null);
}
if (service == null)
{
return m_override ? getBuiltIn() : null;
}
if (service instanceof URLStreamHandlerService)
{
return (URLStreamHandlerService) service;
}
return (URLStreamHandlerService) Proxy.newProxyInstance(
URLStreamHandlerService.class.getClassLoader(),
new Class[]{URLStreamHandlerService.class},
new URLHandlersStreamHandlerProxy(service, m_action));
}
catch (Exception ex)
{
// TODO: log this or something
ex.printStackTrace();
return null;
}
}
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())
{
String pkg = pkgTok.nextToken().trim();
String className = pkg + "." + m_protocol + ".Handler";
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());
}
}
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(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)
throws Throwable
{
try
{
Class[] types = method.getParameterTypes();
if ("parseURL".equals(method.getName()))
{
types[0] = m_service.getClass().getClassLoader().loadClass(
URLStreamHandlerSetter.class.getName());
params[0] = Proxy.newProxyInstance(
m_service.getClass().getClassLoader(), new Class[]{types[0]},
(URLHandlersStreamHandlerProxy) params[0]);
}
return m_action.invoke(m_action.getMethod(m_service.getClass(),
method.getName(), types), m_service, params);
}
catch (Exception ex)
{
throw ex;
}
}
}