/*
* (C) Copyright 2006-2008 Nuxeo SAS (http://nuxeo.com/) and contributors.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Lesser General Public License
* (LGPL) version 2.1 which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/lgpl.html
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* Contributors:
* bstefanescu
*/
package org.nuxeo.common.utils;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.util.ArrayList;
import java.util.Hashtable;
/**
* Used to force installation of URLStreamHandlerFactory as the default
* mechanism in Java is failing to set a new factory if one was already set.
* <p>
* This class provides the capability to stack any number of factories - each
* factory having precedence over the last one.
* <p>
* Thus, when querying for a URL protocol handler all factories will be asked in
* turn (from the newest one to the older one) until a stream handler is
* obtained.
* <p>
* Contains some code from Eclipse Framework class.
*
* @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
*/
public class URLStreamHandlerFactoryInstaller {
private static final FactoryStack factoryStack = new FactoryStack();
private URLStreamHandlerFactoryInstaller() {
}
public static void installURLStreamHandlerFactory(URLStreamHandlerFactory shf) throws Exception {
Field factoryField = getStaticField(URL.class, URLStreamHandlerFactory.class);
if (factoryField == null) {
throw new Exception("Could not find URLStreamHandlerFactory field");
}
// look for a lock to synchronize on
Object lock = getURLStreamHandlerFactoryLock();
synchronized (lock) {
URLStreamHandlerFactory factory = (URLStreamHandlerFactory) factoryField.get(null);
if (factory == null) { // not installed - install it
factoryStack.push(shf); // push the new factory
} else if (factory != factoryStack) { // another factory is
// installed
factoryStack.push(factory);
factoryStack.push(shf); // push the new factory
} else { // already installed
factoryStack.push(shf); // push the new factory
}
// install it
factoryField.set(null, null);
resetURLStreamHandlers();
URL.setURLStreamHandlerFactory(factoryStack);
}
}
public static void uninstallURLStreamHandlerFactory() {
try {
Field factoryField = getStaticField(URL.class, URLStreamHandlerFactory.class);
if (factoryField == null) {
return; // oh well, we tried
}
factoryField.set(null, null);
resetURLStreamHandlers();
} catch (Exception e) {
// ignore and continue closing the framework
}
}
public static void uninstallURLStreamHandlerFactory(URLStreamHandlerFactory shf) {
try {
Field factoryField = getStaticField(URL.class, URLStreamHandlerFactory.class);
if (factoryField == null) {
return; // oh well, we tried
}
Object lock = getURLStreamHandlerFactoryLock();
synchronized (lock) {
URLStreamHandlerFactory factory = (URLStreamHandlerFactory) factoryField.get(null);
if (factory == null) {
return;
}
if (factory != factoryStack) {
return;
}
if (shf == null) {
factoryStack.pop();
} else {
factoryStack.remove(shf);
}
// reinstall factory (to flush cache)
factoryField.set(null, null);
resetURLStreamHandlers();
URL.setURLStreamHandlerFactory(factoryStack);
}
} catch (Exception e) {
// ignore and continue closing the framework
}
}
private static Field getStaticField(Class<?> clazz, Class<?> type) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(type)) {
field.setAccessible(true);
return field;
}
}
return null;
}
public static void resetURLStreamHandlers() {
Field handlersField = getStaticField(URL.class, Hashtable.class);
if (handlersField != null) {
Hashtable<?, ?> handlers;
try {
handlers = (Hashtable<?, ?>) handlersField.get(null);
} catch (Exception e) {
throw new Error("Cannot clear URL handlers cache");
}
if (handlers != null)
handlers.clear();
}
}
private static Object getURLStreamHandlerFactoryLock() throws IllegalAccessException {
Object lock;
try {
Field streamHandlerLockField = URL.class.getDeclaredField("streamHandlerLock");
streamHandlerLockField.setAccessible(true);
lock = streamHandlerLockField.get(null);
} catch (NoSuchFieldException noField) {
// could not find the lock, lets sync on the class object
lock = URL.class;
}
return lock;
}
/**
* Get the underlying stack.
* <p>
* This should not be used to register/unregister factories (since it is not
* synchronized). To install / uninstall factories use the static method of
* that class.
*/
public static FactoryStack getStack() {
return factoryStack;
}
public static class FactoryStack implements URLStreamHandlerFactory {
final ArrayList<URLStreamHandlerFactory> factories = new ArrayList<URLStreamHandlerFactory>();
public URLStreamHandler createURLStreamHandler(String protocol) {
for (int i = factories.size() - 1; i >= 0; i--) {
URLStreamHandler h = factories.get(i).createURLStreamHandler(protocol);
if (h != null) {
return h;
}
}
return null;
}
public void push(URLStreamHandlerFactory factory) {
factories.add(factory);
}
public URLStreamHandlerFactory pop() {
if (factories.isEmpty()) {
return null;
}
return factories.remove(factories.size() - 1);
}
URLStreamHandlerFactory remove(URLStreamHandlerFactory shf) {
return factories.remove(factories.indexOf(shf));
}
public URLStreamHandlerFactory peek() {
if (factories.isEmpty()) {
return null;
}
return factories.get(factories.size() - 1);
}
public boolean isEmpty() {
return factories.isEmpty();
}
public int size() {
return factories.size();
}
public void clear() {
factories.clear();
}
}
}