1 /* 2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.bind.v2.runtime.reflect.opt; 27 28 import java.lang.reflect.InvocationTargetException; 29 import java.lang.reflect.Method; 30 import java.lang.ref.WeakReference; 31 import java.security.AccessController; 32 import java.security.PrivilegedAction; 33 import java.util.concurrent.locks.Lock; 34 import java.util.concurrent.locks.ReentrantReadWriteLock; 35 import java.util.HashMap; 36 import java.util.Map; 37 import java.util.WeakHashMap; 38 import java.util.logging.Level; 39 import java.util.logging.Logger; 40 41 import com.sun.xml.internal.bind.Util; 42 import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor; 43 44 /** 45 * A {@link ClassLoader} used to "inject" optimized accessor classes 46 * into the VM. 47 * 48 * <p> 49 * Its parent class loader needs to be set to the one that can see the user 50 * class. 51 * 52 * @author Kohsuke Kawaguchi 53 */ 54 final class Injector { 55 56 /** 57 * {@link Injector}s keyed by their parent {@link ClassLoader}. 58 * 59 * We only need one injector per one user class loader. 60 */ 61 private static final ReentrantReadWriteLock irwl = new ReentrantReadWriteLock(); 62 private static final Lock ir = irwl.readLock(); 63 private static final Lock iw = irwl.writeLock(); 64 private static final Map<ClassLoader, WeakReference<Injector>> injectors = 65 new WeakHashMap<ClassLoader, WeakReference<Injector>>(); 66 private static final Logger logger = Util.getClassLogger(); 67 68 /** 69 * Injects a new class into the given class loader. 70 * 71 * @return null 72 * if it fails to inject. 73 */ 74 static Class inject(ClassLoader cl, String className, byte[] image) { 75 Injector injector = get(cl); 76 if (injector != null) { 77 return injector.inject(className, image); 78 } else { 79 return null; 80 } 81 } 82 83 /** 84 * Returns the already injected class, or null. 85 */ 86 static Class find(ClassLoader cl, String className) { 87 Injector injector = get(cl); 88 if (injector != null) { 89 return injector.find(className); 90 } else { 91 return null; 92 } 93 } 94 95 /** 96 * Gets or creates an {@link Injector} for the given class loader. 97 * 98 * @return null 99 * if it fails. 100 */ 101 private static Injector get(ClassLoader cl) { 102 Injector injector = null; 103 WeakReference<Injector> wr; 104 ir.lock(); 105 try { 106 wr = injectors.get(cl); 107 } finally { 108 ir.unlock(); 109 } 110 if (wr != null) { 111 injector = wr.get(); 112 } 113 if (injector == null) { 114 try { 115 wr = new WeakReference<Injector>(injector = new Injector(cl)); 116 iw.lock(); 117 try { 118 if (!injectors.containsKey(cl)) { 119 injectors.put(cl, wr); 120 } 121 } finally { 122 iw.unlock(); 123 } 124 } catch (SecurityException e) { 125 logger.log(Level.FINE, "Unable to set up a back-door for the injector", e); 126 return null; 127 } 128 } 129 return injector; 130 } 131 /** 132 * Injected classes keyed by their names. 133 */ 134 private final Map<String, Class> classes = new HashMap<String, Class>(); 135 private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); 136 private final Lock r = rwl.readLock(); 137 private final Lock w = rwl.writeLock(); 138 private final ClassLoader parent; 139 /** 140 * True if this injector is capable of injecting accessors. 141 * False otherwise, which happens if this classloader can't see {@link Accessor}. 142 */ 143 private final boolean loadable; 144 private static final Method defineClass; 145 private static final Method resolveClass; 146 private static final Method findLoadedClass; 147 148 static { 149 Method[] m = AccessController.doPrivileged( 150 new PrivilegedAction<Method[]>() { 151 @Override 152 public Method[] run() { 153 return new Method[]{ 154 getMethod(ClassLoader.class, "defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE), 155 getMethod(ClassLoader.class, "resolveClass", Class.class), 156 getMethod(ClassLoader.class, "findLoadedClass", String.class) 157 }; 158 } 159 } 160 ); 161 defineClass = m[0]; 162 resolveClass = m[1]; 163 findLoadedClass = m[2]; 164 } 165 166 private static Method getMethod(final Class<?> c, final String methodname, final Class<?>... params) { 167 try { 168 Method m = c.getDeclaredMethod(methodname, params); 169 m.setAccessible(true); 170 return m; 171 } catch (NoSuchMethodException e) { 172 throw new NoSuchMethodError(e.getMessage()); 173 } 174 } 175 176 private Injector(ClassLoader parent) { 177 this.parent = parent; 178 assert parent != null; 179 180 boolean loadableCheck = false; 181 182 try { 183 loadableCheck = parent.loadClass(Accessor.class.getName()) == Accessor.class; 184 } catch (ClassNotFoundException e) { 185 // not loadable 186 } 187 188 this.loadable = loadableCheck; 189 } 190 191 @SuppressWarnings("LockAcquiredButNotSafelyReleased") 192 private Class inject(String className, byte[] image) { 193 if (!loadable) // this injector cannot inject anything 194 { 195 return null; 196 } 197 198 boolean wlocked = false; 199 boolean rlocked = false; 200 try { 201 202 r.lock(); 203 rlocked = true; 204 205 Class c = classes.get(className); 206 207 // Unlock now during the findLoadedClass process to avoid 208 // deadlocks 209 r.unlock(); 210 rlocked = false; 211 212 //find loaded class from classloader 213 if (c == null) { 214 215 try { 216 c = (Class) findLoadedClass.invoke(parent, className.replace('/', '.')); 217 } catch (IllegalArgumentException e) { 218 logger.log(Level.FINE, "Unable to find " + className, e); 219 } catch (IllegalAccessException e) { 220 logger.log(Level.FINE, "Unable to find " + className, e); 221 } catch (InvocationTargetException e) { 222 Throwable t = e.getTargetException(); 223 logger.log(Level.FINE, "Unable to find " + className, t); 224 } 225 226 if (c != null) { 227 228 w.lock(); 229 wlocked = true; 230 231 classes.put(className, c); 232 233 w.unlock(); 234 wlocked = false; 235 236 return c; 237 } 238 } 239 240 if (c == null) { 241 242 r.lock(); 243 rlocked = true; 244 245 c = classes.get(className); 246 247 // Unlock now during the define/resolve process to avoid 248 // deadlocks 249 r.unlock(); 250 rlocked = false; 251 252 if (c == null) { 253 254 // we need to inject a class into the 255 try { 256 c = (Class) defineClass.invoke(parent, className.replace('/', '.'), image, 0, image.length); 257 resolveClass.invoke(parent, c); 258 } catch (IllegalAccessException e) { 259 logger.log(Level.FINE, "Unable to inject " + className, e); 260 return null; 261 } catch (InvocationTargetException e) { 262 Throwable t = e.getTargetException(); 263 if (t instanceof LinkageError) { 264 logger.log(Level.FINE, "duplicate class definition bug occured? Please report this : " + className, t); 265 } else { 266 logger.log(Level.FINE, "Unable to inject " + className, t); 267 } 268 return null; 269 } catch (SecurityException e) { 270 logger.log(Level.FINE, "Unable to inject " + className, e); 271 return null; 272 } catch (LinkageError e) { 273 logger.log(Level.FINE, "Unable to inject " + className, e); 274 return null; 275 } 276 277 w.lock(); 278 wlocked = true; 279 280 // During the time we were unlocked, we could have tried to 281 // load the class from more than one thread. Check now to see 282 // if someone else beat us to registering this class 283 if (!classes.containsKey(className)) { 284 classes.put(className, c); 285 } 286 287 w.unlock(); 288 wlocked = false; 289 } 290 } 291 return c; 292 } finally { 293 if (rlocked) { 294 r.unlock(); 295 } 296 if (wlocked) { 297 w.unlock(); 298 } 299 } 300 } 301 302 private Class find(String className) { 303 r.lock(); 304 try { 305 return classes.get(className); 306 } finally { 307 r.unlock(); 308 } 309 } 310 }