001/* 002 * Copyright (c) 2006-2012 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 * 009 * Contributors: 010 * IBM Corporation - initial API and implementation 011 */ 012package org.nuxeo.common.utils; 013 014import java.lang.reflect.Field; 015import java.lang.reflect.Modifier; 016import java.net.URL; 017import java.net.URLStreamHandler; 018import java.net.URLStreamHandlerFactory; 019import java.util.ArrayList; 020import java.util.Hashtable; 021 022/** 023 * Used to force installation of URLStreamHandlerFactory as the default mechanism in Java is failing to set a new 024 * factory if one was already set. 025 * <p> 026 * This class provides the capability to stack any number of factories - each factory having precedence over the last 027 * one. 028 * <p> 029 * Thus, when querying for a URL protocol handler all factories will be asked in turn (from the newest one to the older 030 * one) until a stream handler is obtained. 031 * <p> 032 * Contains some code from Eclipse Framework class. 033 * 034 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> 035 */ 036public class URLStreamHandlerFactoryInstaller { 037 038 private static final FactoryStackHolder factoryStackHolder = new FactoryStackHolder(); 039 040 private static final FactoryStack stack = new FactoryStack(); 041 042 private static class FactoryStackHolder extends InheritableThreadLocal<FactoryStack> implements 043 URLStreamHandlerFactory { 044 045 @Override 046 public FactoryStack initialValue() { 047 return stack; 048 } 049 050 @Override 051 public void remove() { 052 super.remove(); 053 } 054 055 @Override 056 public URLStreamHandler createURLStreamHandler(String protocol) { 057 return get().createURLStreamHandler(protocol); 058 } 059 } 060 061 private URLStreamHandlerFactoryInstaller() { 062 } 063 064 public static void installURLStreamHandlerFactory(URLStreamHandlerFactory shf) { 065 FactoryStack factoryStack = factoryStackHolder.get(); 066 Field factoryField = getStaticField(URL.class, URLStreamHandlerFactory.class); 067 if (factoryField == null) { 068 throw new IllegalArgumentException("Could not find URLStreamHandlerFactory field"); 069 } 070 // look for a lock to synchronize on 071 Object lock = getURLStreamHandlerFactoryLock(); 072 synchronized (lock) { 073 try { 074 URLStreamHandlerFactory factory = (URLStreamHandlerFactory) factoryField.get(null); 075 if (factory == null) { // not installed - install it 076 factoryStack.push(shf); // push the new factory 077 } else if (factory != factoryStackHolder) { // another factory is 078 // installed 079 factoryStack.push(factory); 080 factoryStack.push(shf); // push the new factory 081 } else { // already installed 082 factoryStack.push(shf); // push the new factory 083 } 084 flush(factoryField); 085 } catch (IllegalAccessException e) { 086 throw new IllegalArgumentException(e); 087 } 088 } 089 } 090 091 public static void uninstallURLStreamHandlerFactory() { 092 factoryStackHolder.remove(); 093 try { 094 Field factoryField = getStaticField(URL.class, URLStreamHandlerFactory.class); 095 if (factoryField == null) { 096 return; // oh well, we tried 097 } 098 Object lock = getURLStreamHandlerFactoryLock(); 099 synchronized (lock) { 100 URLStreamHandlerFactory factory = (URLStreamHandlerFactory) factoryField.get(null); 101 if (factory == null) { 102 return; 103 } 104 if (factory != factoryStackHolder) { 105 return; 106 } 107 factoryField.set(null, null); 108 resetURLStreamHandlers(); 109 } 110 } catch (IllegalArgumentException e) { 111 // ignore and continue closing the framework 112 } catch (IllegalAccessException e) { 113 // ignore and continue closing the framework 114 } 115 } 116 117 public static void uninstallURLStreamHandlerFactory(URLStreamHandlerFactory shf) { 118 try { 119 Field factoryField = getStaticField(URL.class, URLStreamHandlerFactory.class); 120 if (factoryField == null) { 121 return; // oh well, we tried 122 } 123 Object lock = getURLStreamHandlerFactoryLock(); 124 synchronized (lock) { 125 URLStreamHandlerFactory factory = (URLStreamHandlerFactory) factoryField.get(null); 126 if (factory == null) { 127 return; 128 } 129 if (factory != factoryStackHolder) { 130 return; 131 } 132 FactoryStack factoryStack = factoryStackHolder.get(); 133 if (shf == null) { 134 factoryStack.pop(); 135 } else { 136 factoryStack.remove(shf); 137 } 138 // reinstall factory (to flush cache) 139 flush(factoryField); 140 } 141 } catch (IllegalArgumentException e) { 142 // ignore and continue closing the framework 143 } catch (IllegalAccessException e) { 144 // ignore and continue closing the framework 145 } 146 147 } 148 149 protected static void flush(Field factoryField) throws IllegalArgumentException, IllegalAccessException { 150 factoryField.set(null, null); 151 resetURLStreamHandlers(); 152 URL.setURLStreamHandlerFactory(factoryStackHolder); 153 } 154 155 private static Field getStaticField(Class<?> clazz, Class<?> type) { 156 Field[] fields = clazz.getDeclaredFields(); 157 for (Field field : fields) { 158 if (Modifier.isStatic(field.getModifiers()) && field.getType().equals(type)) { 159 field.setAccessible(true); 160 return field; 161 } 162 } 163 return null; 164 } 165 166 public static void resetURLStreamHandlers() { 167 Field handlersField = getStaticField(URL.class, Hashtable.class); 168 if (handlersField != null) { 169 Hashtable<?, ?> handlers; 170 try { 171 handlers = (Hashtable<?, ?>) handlersField.get(null); 172 } catch (IllegalAccessException e) { 173 throw new IllegalArgumentException("Cannot clear URL handlers cache", e); 174 } 175 if (handlers != null) { 176 handlers.clear(); 177 } 178 } 179 } 180 181 private static Object getURLStreamHandlerFactoryLock() { 182 Object lock; 183 try { 184 Field streamHandlerLockField = URL.class.getDeclaredField("streamHandlerLock"); 185 streamHandlerLockField.setAccessible(true); 186 lock = streamHandlerLockField.get(null); 187 } catch (NoSuchFieldException noField) { 188 // could not find the lock, lets sync on the class object 189 lock = URL.class; 190 } catch (IllegalAccessException e) { 191 throw new IllegalArgumentException(e); 192 } 193 return lock; 194 } 195 196 /** 197 * Get the underlying stack. 198 * <p> 199 * This should not be used to register/unregister factories (since it is not synchronized). To install / uninstall 200 * factories use the static method of that class. 201 */ 202 public static FactoryStack getStack() { 203 return factoryStackHolder.get(); 204 } 205 206 public static class FactoryStack implements URLStreamHandlerFactory { 207 208 final ArrayList<URLStreamHandlerFactory> factories = new ArrayList<URLStreamHandlerFactory>(); 209 210 @Override 211 public URLStreamHandler createURLStreamHandler(String protocol) { 212 for (int i = factories.size() - 1; i >= 0; i--) { 213 URLStreamHandler h = factories.get(i).createURLStreamHandler(protocol); 214 if (h != null) { 215 return h; 216 } 217 } 218 return null; 219 } 220 221 public void push(URLStreamHandlerFactory factory) { 222 factories.add(factory); 223 } 224 225 public URLStreamHandlerFactory pop() { 226 if (factories.isEmpty()) { 227 return null; 228 } 229 return factories.remove(factories.size() - 1); 230 } 231 232 URLStreamHandlerFactory remove(URLStreamHandlerFactory shf) { 233 return factories.remove(factories.indexOf(shf)); 234 } 235 236 public URLStreamHandlerFactory peek() { 237 if (factories.isEmpty()) { 238 return null; 239 } 240 return factories.get(factories.size() - 1); 241 } 242 243 public boolean isEmpty() { 244 return factories.isEmpty(); 245 } 246 247 public int size() { 248 return factories.size(); 249 } 250 251 public void clear() { 252 factories.clear(); 253 } 254 255 } 256 257}