1 35 package groovy.lang; 36 37 import org.codehaus.groovy.ast.ClassNode; 38 import org.codehaus.groovy.classgen.Verifier; 39 import org.codehaus.groovy.control.CompilationFailedException; 40 import org.codehaus.groovy.control.CompilationUnit; 41 import org.codehaus.groovy.control.CompilerConfiguration; 42 import org.codehaus.groovy.control.Phases; 43 import org.objectweb.asm.ClassVisitor; 44 import org.objectweb.asm.ClassWriter; 45 46 import java.io.*; 47 import java.lang.reflect.Field ; 48 import java.net.MalformedURLException ; 49 import java.net.URL ; 50 import java.security.*; 51 import java.security.cert.Certificate ; 52 import java.util.*; 53 import java.util.jar.Attributes ; 54 import java.util.jar.JarEntry ; 55 import java.util.jar.JarFile ; 56 import java.util.jar.Manifest ; 57 58 68 public class GroovyClassLoader extends SecureClassLoader { 69 70 private Map cache = new HashMap(); 71 72 76 private String [] _searchPaths; 77 78 public void removeFromCache(Class aClass) { 79 cache.remove(aClass); 80 } 81 82 private class PARSING { 83 } 84 85 private class NOT_RESOLVED { 86 } 87 88 private CompilerConfiguration config; 89 90 private String [] searchPaths; 91 92 private List additionalPaths = new ArrayList(); 93 94 public GroovyClassLoader() { 95 this(Thread.currentThread().getContextClassLoader()); 96 } 97 98 public GroovyClassLoader(ClassLoader loader) { 99 this(loader, new CompilerConfiguration()); 100 } 101 102 public GroovyClassLoader(GroovyClassLoader parent) { 103 this(parent, parent.config); 104 } 105 106 public GroovyClassLoader(ClassLoader loader, CompilerConfiguration config) { 107 super(loader); 108 this.config = config; 109 } 110 111 117 public Class defineClass(ClassNode classNode, String file) { 118 return defineClass(classNode, file, "/groovy/defineClass"); 119 } 120 121 127 public Class defineClass(ClassNode classNode, String file, String newCodeBase) { 128 CodeSource codeSource = null; 129 try { 130 codeSource = new CodeSource(new URL ("file", "", newCodeBase), (java.security.cert.Certificate []) null); 131 } catch (MalformedURLException e) { 132 } 134 135 138 CompilationUnit unit = new CompilationUnit(config, codeSource, getParent()); 139 try { 140 ClassCollector collector = createCollector(unit); 141 142 unit.addClassNode(classNode); 143 unit.setClassgenCallback(collector); 144 unit.compile(Phases.CLASS_GENERATION); 145 146 return collector.generatedClass; 147 } catch (CompilationFailedException e) { 148 throw new RuntimeException (e); 149 } 150 } 151 152 158 public Class parseClass(File file) throws CompilationFailedException, IOException { 159 return parseClass(new GroovyCodeSource(file)); 160 } 161 162 169 public Class parseClass(String text, String fileName) throws CompilationFailedException { 170 return parseClass(new ByteArrayInputStream(text.getBytes()), fileName); 171 } 172 173 179 public Class parseClass(String text) throws CompilationFailedException { 180 return parseClass(new ByteArrayInputStream(text.getBytes()), "script" + System.currentTimeMillis() + ".groovy"); 181 } 182 183 189 public Class parseClass(InputStream in) throws CompilationFailedException { 190 return parseClass(in, "script" + System.currentTimeMillis() + ".groovy"); 191 } 192 193 public Class parseClass(final InputStream in, final String fileName) throws CompilationFailedException { 194 GroovyCodeSource gcs = (GroovyCodeSource) AccessController.doPrivileged(new PrivilegedAction() { 200 public Object run() { 201 return new GroovyCodeSource(in, fileName, "/groovy/script"); 202 } 203 }); 204 return parseClass(gcs); 205 } 206 207 208 public Class parseClass(GroovyCodeSource codeSource) throws CompilationFailedException { 209 return parseClass(codeSource, true); 210 } 211 212 217 public Class parseClass(GroovyCodeSource codeSource, boolean shouldCache) throws CompilationFailedException { 218 String name = codeSource.getName(); 219 Class answer = null; 220 synchronized (cache) { 224 answer = (Class ) cache.get(name); 225 if (answer != null) { 226 return (answer == PARSING.class ? null : answer); 227 } else { 228 cache.put(name, PARSING.class); 229 } 230 } 231 try { 234 CompilationUnit unit = new CompilationUnit(config, codeSource.getCodeSource(), this); 235 ClassCollector collector = createCollector(unit); 237 238 if (codeSource.getFile()==null) { 239 unit.addSource(name, codeSource.getInputStream()); 240 } else { 241 unit.addSource(codeSource.getFile()); 242 } 243 unit.setClassgenCallback(collector); 244 int goalPhase = Phases.CLASS_GENERATION; 245 if (config != null && config.getTargetDirectory()!=null) goalPhase = Phases.OUTPUT; 246 unit.compile(goalPhase); 247 248 answer = collector.generatedClass; 249 } finally { 254 synchronized (cache) { 255 if (answer == null || !shouldCache) { 256 cache.remove(name); 257 } else { 258 cache.put(name, answer); 259 } 260 } 261 try { 262 codeSource.getInputStream().close(); 263 } catch (IOException e) { 264 throw new GroovyRuntimeException("unable to close stream",e); 265 } 266 } 267 return answer; 268 } 269 270 278 297 298 protected Class findGroovyClass(String name) throws ClassNotFoundException { 299 String filename = name.replace('.', '/') + ".groovy"; 304 String [] paths = getClassPath(); 305 File classnameAsFile = new File (filename); 308 String classname = classnameAsFile.getName(); 310 String pkg = classnameAsFile.getParent(); 311 String pkgdir; 312 for (int i = 0; i < paths.length; i++) { 313 String pathName = paths[i]; 314 File path = new File (pathName); 315 if (path.exists()) { 316 if (path.isDirectory()) { 317 File nocasefile = new File (path, filename); 324 if (!nocasefile.exists()) 325 continue; 326 327 if (pkg == null) 332 pkgdir = pathName; 333 else 334 pkgdir = pathName + "/" + pkg; 335 File pkgdirF = new File (pkgdir); 336 if (pkgdirF.exists() && pkgdirF.isDirectory()) { 338 File files[] = pkgdirF.listFiles(); 339 for (int j = 0; j < files.length; j++) { 340 if (files[j].getName().equals(classname)) { 342 try { 343 return parseClass(files[j]); 344 } catch (CompilationFailedException e) { 345 throw new ClassNotFoundException ("Syntax error in groovy file: " + files[j].getAbsolutePath(), e); 346 } catch (IOException e) { 347 throw new ClassNotFoundException ("Error reading groovy file: " + files[j].getAbsolutePath(), e); 348 } 349 } 350 } 351 } 352 } else { 353 try { 354 JarFile jarFile = new JarFile (path); 355 JarEntry entry = jarFile.getJarEntry(filename); 356 if (entry != null) { 357 byte[] bytes = extractBytes(jarFile, entry); 358 Certificate [] certs = entry.getCertificates(); 359 try { 360 return parseClass(new GroovyCodeSource(new ByteArrayInputStream(bytes), filename, path, certs)); 361 } catch (CompilationFailedException e1) { 362 throw new ClassNotFoundException ("Syntax error in groovy file: " + filename, e1); 363 } 364 } 365 366 } catch (IOException e) { 367 } 369 } 370 } 371 } 372 throw new ClassNotFoundException (name); 373 } 374 375 private byte[] extractBytes(JarFile jarFile, JarEntry entry) { 380 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 381 int b; 382 try { 383 BufferedInputStream bis = new BufferedInputStream(jarFile.getInputStream(entry)); 384 while ((b = bis.read()) != -1) { 385 baos.write(b); 386 } 387 } catch (IOException ioe) { 388 throw new GroovyRuntimeException("Could not read the jar bytes for " + entry.getName()); 389 } 390 return baos.toByteArray(); 391 } 392 393 396 protected String [] getClassPath() { 397 if (null == _searchPaths) { 398 final String classpath; 399 if(null != config && null != config.getClasspath()) { 400 final List paths = config.getClasspath(); 403 final StringBuffer sb = new StringBuffer (); 404 for(Iterator iter = paths.iterator(); iter.hasNext(); ) { 405 sb.append(iter.next().toString()); 406 sb.append(File.pathSeparatorChar); 407 } 408 sb.deleteCharAt(sb.length()-1); 410 classpath = sb.toString(); 411 } else { 412 classpath = System.getProperty("java.class.path", "."); 413 } 414 final List pathList = new ArrayList(); 415 expandClassPath(pathList, null, classpath); 416 _searchPaths = new String [pathList.size()]; 417 _searchPaths = (String []) pathList.toArray(_searchPaths); 418 } 419 return _searchPaths; 420 } 421 422 426 protected void expandClassPath(List pathList, String base, String classpath) { 427 428 if (classpath != null) { 432 433 String [] paths = classpath.split("[\\ ,:;]"); 438 439 for (int i = 0; i < paths.length; i++) { 440 if (paths.length > 0) { 441 File path = null; 442 443 if ("".equals(base)) { 444 path = new File (paths[i]); 445 } else { 446 path = new File (base, paths[i]); 447 } 448 449 if (path.exists()) { 450 if (!path.isDirectory()) { 451 try { 452 JarFile jar = new JarFile (path); 453 pathList.add(paths[i]); 454 455 Manifest manifest = jar.getManifest(); 456 if (manifest != null) { 457 Attributes classPathAttributes = manifest.getMainAttributes(); 458 String manifestClassPath = classPathAttributes.getValue("Class-Path"); 459 460 if (manifestClassPath != null) 461 expandClassPath(pathList, paths[i], manifestClassPath); 462 } 463 } catch (IOException e) { 464 continue; 466 } 467 } else { 468 pathList.add(paths[i]); 469 } 470 } 471 } 472 } 473 } 474 } 475 476 480 protected Class defineClass(String name, byte[] bytecode, ProtectionDomain domain) { 481 return defineClass(name, bytecode, 0, bytecode.length, domain); 482 } 483 484 protected ClassCollector createCollector(CompilationUnit unit) { 485 return new ClassCollector(this, unit); 486 } 487 488 public static class ClassCollector extends CompilationUnit.ClassgenCallback { 489 private Class generatedClass; 490 491 private GroovyClassLoader cl; 492 493 private CompilationUnit unit; 494 495 protected ClassCollector(GroovyClassLoader cl, CompilationUnit unit) { 496 this.cl = cl; 497 this.unit = unit; 498 } 499 500 protected Class onClassNode(ClassWriter classWriter, ClassNode classNode) { 501 byte[] code = classWriter.toByteArray(); 502 503 Class theClass = cl.defineClass(classNode.getName(), code, 0, code.length, unit.getAST().getCodeSource()); 504 505 if (generatedClass == null) { 506 generatedClass = theClass; 507 } 508 509 return theClass; 510 } 511 512 public void call(ClassVisitor classWriter, ClassNode classNode) { 513 onClassNode((ClassWriter) classWriter, classNode); 514 } 515 } 516 517 521 public Class defineClass(String name, byte[] b) { 522 return super.defineClass(name, b, 0, b.length); 523 } 524 525 533 protected synchronized Class loadClass(final String name, boolean resolve) throws ClassNotFoundException { 534 synchronized (cache) { 535 Class cls = (Class ) cache.get(name); 536 if (cls == NOT_RESOLVED.class) throw new ClassNotFoundException (name); 537 if (cls!=null) return cls; 538 } 539 540 SecurityManager sm = System.getSecurityManager(); 541 if (sm != null) { 542 String className = name.replace('/', '.'); 543 int i = className.lastIndexOf('.'); 544 if (i != -1) { 545 sm.checkPackageAccess(className.substring(0, i)); 546 } 547 } 548 549 Class cls = null; 550 ClassNotFoundException last = null; 551 try { 552 cls = super.loadClass(name, resolve); 553 554 boolean recompile = false; 555 if (getTimeStamp(cls) < Long.MAX_VALUE) { 556 Class [] inters = cls.getInterfaces(); 557 for (int i = 0; i < inters.length; i++) { 558 if (inters[i].getName().equals(GroovyObject.class.getName())) { 559 recompile=true; 560 break; 561 } 562 } 563 } 564 if (!recompile) return cls; 565 } catch (ClassNotFoundException cnfe) { 566 last = cnfe; 567 } 568 569 try { 571 File source = (File ) AccessController.doPrivileged(new PrivilegedAction() { 572 public Object run() { 573 return getSourceFile(name); 574 } 575 }); 576 if (source != null) { 577 if ((cls!=null && isSourceNewer(source, cls)) || (cls==null)) { 578 cls = parseClass(source); 579 } 580 } 581 } catch (Exception e) { 582 cls = null; 583 last = new ClassNotFoundException ("Failed to parse groovy file: " + name, e); 584 } 585 if (cls==null) { 586 if (last==null) throw new AssertionError (true); 587 synchronized (cache) { 588 cache.put(name, NOT_RESOLVED.class); 589 } 590 throw last; 591 } 592 synchronized (cache) { 593 cache.put(name, cls); 594 } 595 return cls; 596 } 597 598 private long getTimeStamp(Class cls) { 599 Field field; 600 Long o; 601 try { 602 field = cls.getField(Verifier.__TIMESTAMP); 603 o = (Long ) field.get(null); 604 } catch (Exception e) { 605 return Long.MAX_VALUE; 607 } 608 return o.longValue(); 609 } 610 611 623 private File getSourceFile(String name) { 624 File source = null; 625 String filename = name.replace('.', '/') + ".groovy"; 626 String [] paths = getClassPath(); 627 for (int i = 0; i < paths.length; i++) { 628 String pathName = paths[i]; 629 File path = new File (pathName); 630 if (path.exists()) { if (path.isDirectory()) { 632 File file = new File (path, filename); 633 if (file.exists()) { 634 boolean fileExists = false; 637 int sepp = filename.lastIndexOf('/'); 638 String fn = filename; 639 if (sepp >= 0) { 640 fn = filename.substring(++sepp); 641 } 642 File parent = file.getParentFile(); 643 String [] files = parent.list(); 644 for (int j = 0; j < files.length; j++) { 645 if (files[j].equals(fn)) { 646 fileExists = true; 647 break; 648 } 649 } 650 651 if (fileExists) { 652 source = file; 653 break; 654 } 655 } 656 } 657 } 658 } 659 return source; 660 } 661 662 private boolean isSourceNewer(File source, Class cls) { 663 return source.lastModified() > getTimeStamp(cls); 664 } 665 666 public void addClasspath(String path) { 667 additionalPaths.add(path); 668 searchPaths = null; 669 } 670 } 671 | Popular Tags |