| /******************************************************************************* |
| * Copyright (c) 2012 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osgi.internal.url; |
| |
| import java.lang.reflect.*; |
| import java.net.*; |
| import java.util.Hashtable; |
| import org.eclipse.osgi.framework.log.FrameworkLogEntry; |
| import org.eclipse.osgi.internal.framework.EquinoxContainer; |
| import org.osgi.framework.BundleContext; |
| |
| public class EquinoxFactoryManager { |
| private final EquinoxContainer container; |
| // we need to hold these so that we can unregister them at shutdown |
| private volatile URLStreamHandlerFactoryImpl urlStreamHandlerFactory; |
| private volatile ContentHandlerFactoryImpl contentHandlerFactory; |
| |
| public EquinoxFactoryManager(EquinoxContainer container) { |
| this.container = container; |
| } |
| |
| public void installHandlerFactories(BundleContext context) { |
| installURLStreamHandlerFactory(context); |
| installContentHandlerFactory(context); |
| } |
| |
| private void installURLStreamHandlerFactory(BundleContext context) { |
| URLStreamHandlerFactoryImpl shf = new URLStreamHandlerFactoryImpl(context, container); |
| try { |
| // first try the standard way |
| URL.setURLStreamHandlerFactory(shf); |
| } catch (Error err) { |
| try { |
| // ok we failed now use more drastic means to set the factory |
| forceURLStreamHandlerFactory(shf); |
| } catch (Exception ex) { |
| container.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, ex.getMessage(), ex); |
| throw err; |
| } |
| } |
| urlStreamHandlerFactory = shf; |
| } |
| |
| private static void forceURLStreamHandlerFactory(URLStreamHandlerFactoryImpl shf) throws Exception { |
| Field factoryField = getField(URL.class, URLStreamHandlerFactory.class, false); |
| if (factoryField == null) |
| throw new Exception("Could not find URLStreamHandlerFactory field"); //$NON-NLS-1$ |
| // look for a lock to synchronize on |
| Object lock = getURLStreamHandlerFactoryLock(); |
| synchronized (lock) { |
| URLStreamHandlerFactory factory = (URLStreamHandlerFactory) factoryField.get(null); |
| // doing a null check here just in case, but it would be really strange if it was null, |
| // because we failed to set the factory normally!! |
| if (factory != null) { |
| try { |
| factory.getClass().getMethod("isMultiplexing", (Class[]) null); //$NON-NLS-1$ |
| Method register = factory.getClass().getMethod("register", new Class[] {Object.class}); //$NON-NLS-1$ |
| register.invoke(factory, new Object[] {shf}); |
| } catch (NoSuchMethodException e) { |
| // current factory does not support multiplexing, ok we'll wrap it |
| shf.setParentFactory(factory); |
| factory = shf; |
| } |
| } |
| factoryField.set(null, null); |
| // always attempt to clear the handlers cache |
| // This allows an optomization for the single framework use-case |
| resetURLStreamHandlers(); |
| URL.setURLStreamHandlerFactory(factory); |
| } |
| } |
| |
| private static void resetURLStreamHandlers() throws IllegalAccessException { |
| Field handlersField = getField(URL.class, Hashtable.class, false); |
| if (handlersField != null) { |
| @SuppressWarnings("rawtypes") |
| Hashtable<?, ?> handlers = (Hashtable) handlersField.get(null); |
| if (handlers != null) |
| handlers.clear(); |
| } |
| } |
| |
| private static Object getURLStreamHandlerFactoryLock() throws IllegalAccessException { |
| Object lock; |
| try { |
| Field streamHandlerLockField = URL.class.getDeclaredField("streamHandlerLock"); //$NON-NLS-1$ |
| MultiplexingFactory.setAccessible(streamHandlerLockField); |
| lock = streamHandlerLockField.get(null); |
| } catch (NoSuchFieldException noField) { |
| // could not find the lock, lets sync on the class object |
| lock = URL.class; |
| } |
| return lock; |
| } |
| |
| private void installContentHandlerFactory(BundleContext context) { |
| ContentHandlerFactoryImpl chf = new ContentHandlerFactoryImpl(context, container); |
| try { |
| // first try the standard way |
| URLConnection.setContentHandlerFactory(chf); |
| } catch (Error err) { |
| // ok we failed now use more drastic means to set the factory |
| try { |
| forceContentHandlerFactory(chf); |
| } catch (Exception ex) { |
| // this is unexpected, log the exception and throw the original error |
| container.getLogServices().log(EquinoxContainer.NAME, FrameworkLogEntry.ERROR, ex.getMessage(), ex); |
| throw err; |
| } |
| } |
| contentHandlerFactory = chf; |
| } |
| |
| private static void forceContentHandlerFactory(ContentHandlerFactoryImpl chf) throws Exception { |
| Field factoryField = getField(URLConnection.class, java.net.ContentHandlerFactory.class, false); |
| if (factoryField == null) |
| throw new Exception("Could not find ContentHandlerFactory field"); //$NON-NLS-1$ |
| synchronized (URLConnection.class) { |
| java.net.ContentHandlerFactory factory = (java.net.ContentHandlerFactory) factoryField.get(null); |
| // doing a null check here just in case, but it would be really strange if it was null, |
| // because we failed to set the factory normally!! |
| |
| if (factory != null) { |
| try { |
| factory.getClass().getMethod("isMultiplexing", (Class[]) null); //$NON-NLS-1$ |
| Method register = factory.getClass().getMethod("register", new Class[] {Object.class}); //$NON-NLS-1$ |
| register.invoke(factory, new Object[] {chf}); |
| } catch (NoSuchMethodException e) { |
| // current factory does not support multiplexing, ok we'll wrap it |
| chf.setParentFactory(factory); |
| factory = chf; |
| } |
| } |
| // null out the field so that we can successfully call setContentHandlerFactory |
| factoryField.set(null, null); |
| // always attempt to clear the handlers cache |
| // This allows an optomization for the single framework use-case |
| resetContentHandlers(); |
| URLConnection.setContentHandlerFactory(factory); |
| } |
| } |
| |
| private static void resetContentHandlers() throws IllegalAccessException { |
| Field handlersField = getField(URLConnection.class, Hashtable.class, false); |
| if (handlersField != null) { |
| @SuppressWarnings("rawtypes") |
| Hashtable<?, ?> handlers = (Hashtable) handlersField.get(null); |
| if (handlers != null) |
| handlers.clear(); |
| } |
| } |
| |
| public void uninstallHandlerFactories() { |
| uninstallURLStreamHandlerFactory(); |
| uninstallContentHandlerFactory(); |
| } |
| |
| private void uninstallURLStreamHandlerFactory() { |
| try { |
| Field factoryField = getField(URL.class, URLStreamHandlerFactory.class, false); |
| if (factoryField == null) |
| return; // oh well, we tried |
| Object lock = getURLStreamHandlerFactoryLock(); |
| synchronized (lock) { |
| URLStreamHandlerFactory factory = (URLStreamHandlerFactory) factoryField.get(null); |
| if (factory == urlStreamHandlerFactory) { |
| factory = (URLStreamHandlerFactory) urlStreamHandlerFactory.designateSuccessor(); |
| } else { |
| Method unregister = factory.getClass().getMethod("unregister", new Class[] {Object.class}); //$NON-NLS-1$ |
| unregister.invoke(factory, new Object[] {urlStreamHandlerFactory}); |
| } |
| factoryField.set(null, null); |
| // always attempt to clear the handlers cache |
| // This allows an optimization for the single framework use-case |
| // Note that the call to setURLStreamHandlerFactory below may clear this cache |
| // but we want to be sure to clear it here just in case the parent is null. |
| // In this case the call below would not occur. |
| resetURLStreamHandlers(); |
| if (factory != null) |
| URL.setURLStreamHandlerFactory(factory); |
| } |
| } catch (Exception e) { |
| // ignore and continue closing the framework |
| } |
| } |
| |
| private void uninstallContentHandlerFactory() { |
| try { |
| Field factoryField = getField(URLConnection.class, java.net.ContentHandlerFactory.class, false); |
| if (factoryField == null) |
| return; // oh well, we tried. |
| synchronized (URLConnection.class) { |
| java.net.ContentHandlerFactory factory = (java.net.ContentHandlerFactory) factoryField.get(null); |
| |
| if (factory == contentHandlerFactory) { |
| factory = (java.net.ContentHandlerFactory) contentHandlerFactory.designateSuccessor(); |
| } else { |
| Method unregister = factory.getClass().getMethod("unregister", new Class[] {Object.class}); //$NON-NLS-1$ |
| unregister.invoke(factory, new Object[] {contentHandlerFactory}); |
| } |
| // null out the field so that we can successfully call setContentHandlerFactory |
| factoryField.set(null, null); |
| // always attempt to clear the handlers cache |
| // This allows an optomization for the single framework use-case |
| // Note that the call to setContentHandlerFactory below may clear this cache |
| // but we want to be sure to clear it here just incase the parent is null. |
| // In this case the call below would not occur. |
| // Also it appears most java libraries actually do not clear the cache |
| // when setContentHandlerFactory is called, go figure!! |
| resetContentHandlers(); |
| if (factory != null) |
| URLConnection.setContentHandlerFactory(factory); |
| } |
| } catch (Exception e) { |
| // ignore and continue closing the framework |
| } |
| } |
| |
| public static Field getField(Class<?> clazz, Class<?> type, boolean instance) { |
| Field[] fields = clazz.getDeclaredFields(); |
| for (int i = 0; i < fields.length; i++) { |
| boolean isStatic = Modifier.isStatic(fields[i].getModifiers()); |
| if (instance != isStatic && fields[i].getType().equals(type)) { |
| MultiplexingFactory.setAccessible(fields[i]); |
| return fields[i]; |
| } |
| } |
| return null; |
| } |
| } |