1 9 package com.vladium.emma.rt; 10 11 import java.io.File ; 12 import java.io.IOException ; 13 import java.io.InputStream ; 14 import java.io.PrintWriter ; 15 import java.net.MalformedURLException ; 16 import java.net.URL ; 17 import java.net.URLClassLoader ; 18 import java.security.CodeSource ; 19 import java.util.Map ; 20 21 import com.vladium.logging.Logger; 22 import com.vladium.util.ByteArrayOStream; 23 import com.vladium.util.asserts.$assert; 24 import com.vladium.emma.IAppConstants; 25 import com.vladium.emma.filter.IInclExclFilter; 26 27 31 public 32 final class InstrClassLoader extends URLClassLoader 33 { 34 36 40 41 public static final String PROPERTY_FORCED_DELEGATION_FILTER = "clsload.forced_delegation_filter"; 42 public static final String PROPERTY_THROUGH_DELEGATION_FILTER = "clsload.through_delegation_filter"; 43 44 45 public InstrClassLoader (final ClassLoader parent, final File [] classpath, 46 final IInclExclFilter forcedDelegationFilter, 47 final IInclExclFilter throughDelegationFilter, 48 final IClassLoadHook hook, final Map cache) 49 throws MalformedURLException 50 { 51 54 super (filesToURLs (classpath), null); 55 56 58 m_hook = hook; 59 m_cache = cache; 61 m_forcedDelegationFilter = forcedDelegationFilter; 62 m_throughDelegationFilter = throughDelegationFilter; 63 64 m_parent = parent; 65 m_bufPool = new PoolEntry [BAOS_POOL_SIZE]; 66 67 m_log = Logger.getLogger (); 68 } 69 70 76 public synchronized final Class loadClass (final String name, final boolean resolve) 77 throws ClassNotFoundException 78 { 79 final boolean trace1 = m_log.atTRACE1 (); 80 81 if (trace1) m_log.trace1 ("loadClass", "(" + name + ", " + resolve + "): nest level " + m_nestLevel); 82 83 Class c = null; 84 85 c = findLoadedClass (name); 88 89 if (c == null) 90 { 91 Class parentsVersion = null; 92 if (m_parent != null) 93 { 94 try 95 { 96 parentsVersion = m_parent.loadClass (name); 98 if ((parentsVersion.getClassLoader () != m_parent) || 99 ((m_forcedDelegationFilter == null) || m_forcedDelegationFilter.included (name))) 100 { 101 c = parentsVersion; 104 if (trace1) m_log.trace1 ("loadClass", "using parent's version for [" + name + "]"); 105 } 106 } 107 catch (ClassNotFoundException cnfe) 108 { 109 if ((m_forcedDelegationFilter == null) || m_forcedDelegationFilter.included (name)) 111 throw cnfe; 112 } 113 } 114 115 if (c == null) 116 { 117 try 118 { 119 c = findClass (name); 123 } 124 catch (ClassNotFoundException cnfe) 125 { 126 129 132 if (parentsVersion != null) 133 { 134 final boolean delegate = (m_throughDelegationFilter == null) || m_throughDelegationFilter.included (name); 135 136 if (delegate) 137 { 138 c = parentsVersion; 139 if (trace1) m_log.trace1 ("loadClass", "[delegation filter] using parent's version for [" + name + "]"); 140 } 141 else 142 throw cnfe; 143 } 144 else 145 throw cnfe; 146 } 147 } 148 } 149 150 if (c == null) throw new ClassNotFoundException (name); 151 152 if (resolve) resolveClass (c); return c; 154 } 155 156 158 public final URL getResource (final String name) 159 { 160 final boolean trace1 = m_log.atTRACE1 (); 161 162 if (trace1) m_log.trace1 ("getResource", "(" + name + "): nest level " + m_nestLevel); 163 164 final URL result = super.getResource (name); 165 if (trace1 && (result != null)) m_log.trace1 ("loadClass", "[" + name + "] found in " + result); 166 167 return result; 168 } 169 170 172 173 protected final Class findClass (final String name) 174 throws ClassNotFoundException 175 { 176 final boolean trace1 = m_log.atTRACE1 (); 177 178 if (trace1) m_log.trace1 ("findClass", "(" + name + "): nest level " + m_nestLevel); 179 180 final boolean useClassCache = (m_cache != null); 181 final ClassPathCacheEntry entry = useClassCache ? (ClassPathCacheEntry) m_cache.remove (name) : null; 182 183 byte [] bytes; 184 int length; 185 URL classURL = null; 186 187 if (entry != null) { 189 ++ m_cacheHits; 190 191 193 try 194 { 195 classURL = new URL (entry.m_srcURL); 196 } 197 catch (MalformedURLException murle) { 199 if ($assert.ENABLED) 200 { 201 murle.printStackTrace (System.out); 202 } 203 } 204 205 PoolEntry buf = null; 206 try 207 { 208 buf = acquirePoolEntry (); 209 final ByteArrayOStream baos = buf.m_baos; 211 bytes = entry.m_bytes; 213 length = bytes.length; 214 215 if ((m_hook != null) && m_hook.processClassDef (name, bytes, length, baos)) { 217 bytes = baos.getByteArray (); 219 length = baos.size (); 220 221 if (trace1) m_log.trace1 ("findClass", "defining [cached] instrumented [" + name + "] {" + length + " bytes }"); 222 } 223 else 224 { 225 if (trace1) m_log.trace1 ("findClass", "defining [cached] [" + name + "] {" + length + " bytes }"); 226 } 227 228 return defineClass (name, bytes, length, classURL); 229 } 230 catch (IOException ioe) 231 { 232 throw new ClassNotFoundException (name); 233 } 234 finally 235 { 236 if (buf != null) releasePoolEntry (buf); 237 } 238 } 239 else { 241 if (useClassCache) ++ m_cacheMisses; 242 243 final String classResource = name.replace ('.', '/') + ".class"; 246 247 classURL = getResource (classResource); 250 if (trace1 && (classURL != null)) m_log.trace1 ("findClass", "[" + name + "] found in " + classURL); 251 252 if (classURL == null) 253 throw new ClassNotFoundException (name); 254 else 255 { 256 InputStream in = null; 257 PoolEntry buf = null; 258 try 259 { 260 in = classURL.openStream (); 261 262 buf = acquirePoolEntry (); 263 final ByteArrayOStream baos = buf.m_baos; 265 readFully (in, baos, buf.m_buf); 266 in.close (); in = null; 268 269 bytes = baos.getByteArray (); 271 length = baos.size (); 272 273 baos.reset (); 275 if ((m_hook != null) && m_hook.processClassDef (name, bytes, length, baos)) { 277 bytes = baos.getByteArray (); 279 length = baos.size (); 280 281 if (trace1) m_log.trace1 ("findClass", "defining instrumented [" + name + "] {" + length + " bytes }"); 282 } 283 else 284 { 285 if (trace1) m_log.trace1 ("findClass", "defining [" + name + "] {" + length + " bytes }"); 286 } 287 288 return defineClass (name, bytes, length, classURL); 289 } 290 catch (IOException ioe) 291 { 292 throw new ClassNotFoundException (name); 293 } 294 finally 295 { 296 if (buf != null) releasePoolEntry (buf); 297 if (in != null) try { in.close (); } catch (Exception ignore) {} 298 } 299 } 300 } 301 } 302 303 public void debugDump (final PrintWriter out) 304 { 305 if (out != null) 306 { 307 out.println (this + ": " + m_cacheHits + " class cache hits, " + m_cacheMisses + " misses"); 308 } 309 } 310 311 313 315 316 private static final class PoolEntry 317 { 318 PoolEntry (final int baosCapacity, final int bufSize) 319 { 320 m_baos = new ByteArrayOStream (baosCapacity); 321 m_buf = new byte [bufSize]; 322 } 323 324 void trim (final int baosCapacity, final int baosMaxCapacity) 325 { 326 if (m_baos.capacity () > baosMaxCapacity) 327 { 328 m_baos = new ByteArrayOStream (baosCapacity); 329 } 330 } 331 332 ByteArrayOStream m_baos; 333 final byte [] m_buf; 334 335 } 337 338 341 private Class defineClass (final String className, final byte [] bytes, final int length, final URL srcURL) 342 { 343 346 final CodeSource csrc = new CodeSource (srcURL, null); 347 348 352 final int lastDot = className.lastIndexOf ('.'); 353 if (lastDot >= 0) 354 { 355 final String packageName = className.substring (0, lastDot); 356 357 final Package pkg = getPackage (packageName); 358 if (pkg == null) 359 { 360 definePackage (packageName, 361 IAppConstants.APP_NAME, IAppConstants.APP_VERSION, IAppConstants.APP_COPYRIGHT, 362 IAppConstants.APP_NAME, IAppConstants.APP_VERSION, IAppConstants.APP_COPYRIGHT, 363 srcURL); 364 } 365 } 366 367 return defineClass (className, bytes, 0, length, csrc); 368 } 369 370 371 private static URL [] filesToURLs (final File [] classpath) 372 throws MalformedURLException 373 { 374 if ((classpath == null) || (classpath.length == 0)) 375 return EMPTY_URL_ARRAY; 376 377 final URL [] result = new URL [classpath.length]; 378 379 for (int f = 0; f < result.length ; ++ f) 380 { 381 result [f] = classpath [f].toURL (); } 383 384 return result; 385 } 386 387 390 private static void readFully (final InputStream in, final ByteArrayOStream out, final byte [] buf) 391 throws IOException 392 { 393 for (int read; (read = in.read (buf)) >= 0; ) 394 { 395 out.write (buf, 0, read); 396 } 397 } 398 399 402 private PoolEntry acquirePoolEntry () 403 { 404 PoolEntry result; 405 406 if (m_nestLevel >= BAOS_POOL_SIZE) 407 { 408 result = new PoolEntry (BAOS_INIT_SIZE, BAOS_INIT_SIZE); 409 } 410 else 411 { 412 result = m_bufPool [m_nestLevel]; 413 if (result == null) 414 { 415 result = new PoolEntry (BAOS_INIT_SIZE, BAOS_INIT_SIZE); 416 m_bufPool [m_nestLevel] = result; 417 } 418 else 419 { 420 result.m_baos.reset (); 421 } 422 } 423 424 ++ m_nestLevel; 425 426 return result; 427 } 428 429 432 private void releasePoolEntry (final PoolEntry buf) 433 { 434 if (-- m_nestLevel < BAOS_POOL_SIZE) 435 { 436 buf.trim (BAOS_INIT_SIZE, BAOS_MAX_SIZE); 437 } 438 } 439 440 441 private final ClassLoader m_parent; 442 443 private final IInclExclFilter m_forcedDelegationFilter; 444 private final IInclExclFilter m_throughDelegationFilter; 445 446 private final Map m_cache; private final IClassLoadHook m_hook; 448 private final PoolEntry [] m_bufPool; 449 450 private final Logger m_log; 452 private int m_nestLevel; 453 454 private int m_cacheHits, m_cacheMisses; 455 456 private static final int BAOS_INIT_SIZE = 32 * 1024; 457 private static final int BAOS_MAX_SIZE = 1024 * 1024; 458 private static final int BAOS_POOL_SIZE = 8; 459 private static final URL [] EMPTY_URL_ARRAY = new URL [0]; 460 461 } | Popular Tags |