1 28 29 package com.caucho.loader; 30 31 import com.caucho.config.ConfigException; 32 import com.caucho.java.CompileClassNotFound; 33 import com.caucho.java.JavaCompiler; 34 import com.caucho.log.Log; 35 import com.caucho.make.AlwaysModified; 36 import com.caucho.make.Make; 37 import com.caucho.server.util.CauchoSystem; 38 import com.caucho.util.CharBuffer; 39 import com.caucho.util.L10N; 40 import com.caucho.vfs.Depend; 41 import com.caucho.vfs.Path; 42 43 import javax.annotation.PostConstruct; 44 import java.io.IOException ; 45 import java.net.URL ; 46 import java.security.CodeSource ; 47 import java.security.cert.Certificate ; 48 import java.util.ArrayList ; 49 import java.util.HashSet ; 50 import java.util.logging.Level ; 51 import java.util.logging.Logger ; 52 53 56 public class CompilingLoader extends Loader implements Make { 57 private static final Logger log = Log.open(CompilingLoader.class); 58 private static final L10N L = new L10N(CompilingLoader.class); 59 60 private static final char []INNER_CLASS_SEPARATORS = 61 new char[] {'$', '+', '-'}; 62 63 private String _classPath; 65 66 private String _compiler; 67 private String _sourceExt = ".java"; 68 69 private Path _sourceDir; 71 private Path _classDir; 73 74 private CodeSource _codeSource; 75 76 private ArrayList <String > _args; 77 private String _encoding; 78 private boolean _requireSource; 79 80 private HashSet <String > _excludedDirectories = new HashSet <String >(); 81 82 private boolean _isBatch = true; 83 84 public CompilingLoader() 85 { 86 _excludedDirectories.add("CVS"); 87 } 88 89 94 public CompilingLoader(Path classDir) 95 { 96 this(classDir, classDir, null, null); 97 } 98 99 107 public CompilingLoader(Path classDir, Path sourceDir, 108 String args, String encoding) 109 { 110 if (classDir.getScheme().equals("http") || 111 classDir.getScheme().equals("https")) 112 throw new RuntimeException ("compiling class loader can't be http. Use compile=false."); 113 114 _sourceDir = sourceDir; 115 _classDir = classDir; 116 117 _encoding = encoding; 118 119 121 129 } 130 131 138 public static DynamicClassLoader create(Path path) 139 { 140 DynamicClassLoader loader = new DynamicClassLoader(null); 141 142 CompilingLoader compilingLoader = new CompilingLoader(path); 143 144 loader.addLoader(compilingLoader); 145 146 loader.init(); 147 148 return loader; 149 } 150 151 154 public void setPath(Path path) 155 { 156 _classDir = path; 157 158 if (_sourceDir == null) 159 _sourceDir = path; 160 } 161 162 165 public Path getPath() 166 { 167 return _classDir; 168 } 169 170 173 public void setSource(Path path) 174 { 175 _sourceDir = path; 176 } 177 178 181 public void setSourceExtension(String ext) 182 throws ConfigException 183 { 184 if (! ext.startsWith(".")) 185 throw new ConfigException(L.l("source-extension '{0}' must begin with '.'", 186 ext)); 187 188 _sourceExt = ext; 189 } 190 191 194 public void setCompiler(String compiler) 195 throws ConfigException 196 { 197 _compiler = compiler; 198 } 199 200 203 public Path getSource() 204 { 205 return _sourceDir; 206 } 207 208 211 public void setArgs(String arg) 212 { 213 int i = 0; 214 int len = arg.length(); 215 216 CharBuffer cb = new CharBuffer(); 217 218 while (i < len) { 219 char ch; 220 for (; i < len && Character.isWhitespace(ch = arg.charAt(i)); i++) { 221 } 222 223 if (len <= i) 224 return; 225 226 cb.clear(); 227 228 for (; i < len && ! Character.isWhitespace(ch = arg.charAt(i)); i++) 229 cb.append(ch); 230 231 addArg(cb.toString()); 232 } 233 } 234 235 238 public void addArg(String arg) 239 { 240 if (_args == null) 241 _args = new ArrayList <String >(); 242 243 _args.add(arg); 244 } 245 246 249 public void setEncoding(String encoding) 250 { 251 _encoding = encoding; 252 } 253 254 257 public void setRequireSource(boolean requireSource) 258 { 259 _requireSource = requireSource; 260 } 261 262 265 public void setBatch(boolean isBatch) 266 { 267 _isBatch = isBatch; 268 } 269 270 273 @PostConstruct 274 public void init() 275 throws ConfigException 276 { 277 if (_classDir == null) 278 throw new ConfigException(L.l("`path' is a required attribute <compiling-loader>.")); 279 280 try { 281 _classDir.mkdirs(); 282 } catch (IOException e) { 283 log.log(Level.FINE, e.toString(), e); 284 } 285 286 try { 287 _codeSource = new CodeSource (new URL (_classDir.getURL()), (Certificate []) null); 288 } catch (Exception e) { 289 String scheme = _classDir.getScheme(); 290 291 if (scheme != null && ! scheme.equals("memory")) 292 log.log(Level.FINE, e.toString(), e); 293 } 294 } 295 296 304 public static DynamicClassLoader create(ClassLoader parent, 305 Path classDir, Path sourceDir, 306 String args, String encoding) 307 { 308 DynamicClassLoader loader = new DynamicClassLoader(parent); 309 loader.addLoader(new CompilingLoader(classDir, sourceDir, args, encoding)); 310 311 loader.init(); 312 313 return loader; 314 } 315 316 319 public void setLoader(DynamicClassLoader loader) 320 { 321 super.setLoader(loader); 322 323 loader.addURL(_classDir); 324 } 325 326 public String getClassPath() 327 { 328 if (_classPath == null) 329 _classPath = getLoader().getClassPath(); 330 331 return _classPath; 332 } 333 334 337 public void make() 338 throws IOException , ClassNotFoundException 339 { 340 if (_sourceDir.isDirectory() && ! _classDir.isDirectory()) 341 _classDir.mkdirs(); 342 343 String sourcePath = prefixClassPath(getClassPath()); 344 345 if (_isBatch) { 346 ArrayList <String > files = new ArrayList <String >(); 347 findAllModifiedClasses("", _sourceDir, _classDir, sourcePath, files); 348 349 if (files.size() > 0) { 350 String []paths = files.toArray(new String [files.size()]); 351 352 compileBatch(paths, true); 353 } 354 } 355 else 356 makeAllSequential("", _sourceDir, _classDir, sourcePath); 357 } 358 359 private void makeAllSequential(String name, Path sourceDir, Path classDir, 360 String sourcePath) 361 throws IOException , ClassNotFoundException 362 { 363 String []list; 364 365 try { 366 list = sourceDir.list(); 367 } catch (IOException e) { 368 return; 369 } 370 371 for (int i = 0; list != null && i < list.length; i++) { 372 if (list[i].startsWith(".")) 373 continue; 374 375 if (_excludedDirectories.contains(list[i])) 376 continue; 377 378 Path subSource = sourceDir.lookup(list[i]); 379 380 if (subSource.isDirectory()) { 381 makeAllSequential(name + list[i] + "/", subSource, 382 classDir.lookup(list[i]), sourcePath); 383 } 384 else if (list[i].endsWith(_sourceExt)) { 385 int tail = list[i].length() - _sourceExt.length(); 386 String prefix = list[i].substring(0, tail); 387 Path subClass = classDir.lookup(prefix + ".class"); 388 389 if (subSource.getLastModified() <= subClass.getLastModified()) 390 continue; 391 392 compileClass(subSource, subClass, sourcePath, true); 393 } 394 } 395 396 if (! _requireSource) 397 return; 398 399 try { 400 list = classDir.list(); 401 } catch (IOException e) { 402 return; 403 } 404 405 for (int i = 0; list != null && i < list.length; i++) { 406 if (list[i].startsWith(".")) 407 continue; 408 409 if (_excludedDirectories.contains(list[i])) 410 continue; 411 412 Path subClass = classDir.lookup(list[i]); 413 414 if (list[i].endsWith(".class")) { 415 String prefix = list[i].substring(0, list[i].length() - 6); 416 Path subSource = sourceDir.lookup(prefix + _sourceExt); 417 418 if (! subSource.exists()) { 419 String tail = subSource.getTail(); 420 boolean doRemove = true; 421 422 if (tail.indexOf('$') > 0) { 423 String subTail = tail.substring(0, tail.indexOf('$')) + _sourceExt; 424 Path subJava = subSource.getParent().lookup(subTail); 425 426 if (subJava.exists()) 427 doRemove = false; 428 } 429 430 if (doRemove) { 431 log.finer(L.l("removing obsolete class `{0}'.", subClass.getPath())); 432 433 subClass.remove(); 434 } 435 } 436 } 437 } 438 } 439 440 443 private void findAllModifiedClasses(String name, 444 Path sourceDir, 445 Path classDir, 446 String sourcePath, 447 ArrayList <String > sources) 448 throws IOException , ClassNotFoundException 449 { 450 String []list; 451 452 try { 453 list = sourceDir.list(); 454 } catch (IOException e) { 455 return; 456 } 457 458 for (int i = 0; list != null && i < list.length; i++) { 459 if (list[i].startsWith(".")) 460 continue; 461 462 if (_excludedDirectories.contains(list[i])) 463 continue; 464 465 Path subSource = sourceDir.lookup(list[i]); 466 467 if (subSource.isDirectory()) { 468 findAllModifiedClasses(name + list[i] + "/", subSource, 469 classDir.lookup(list[i]), sourcePath, sources); 470 } 471 else if (list[i].endsWith(_sourceExt)) { 472 int tail = list[i].length() - _sourceExt.length(); 473 String prefix = list[i].substring(0, tail); 474 Path subClass = classDir.lookup(prefix + ".class"); 475 476 if (subClass.getLastModified() < subSource.getLastModified()) { 477 sources.add(name + list[i]); 478 } 479 } 480 } 481 482 if (! _requireSource) 483 return; 484 485 try { 486 list = classDir.list(); 487 } catch (IOException e) { 488 return; 489 } 490 491 for (int i = 0; list != null && i < list.length; i++) { 492 if (list[i].startsWith(".")) 493 continue; 494 495 if (_excludedDirectories.contains(list[i])) 496 continue; 497 498 Path subClass = classDir.lookup(list[i]); 499 500 if (list[i].endsWith(".class")) { 501 String prefix = list[i].substring(0, list[i].length() - 6); 502 Path subSource = sourceDir.lookup(prefix + _sourceExt); 503 504 if (! subSource.exists()) { 505 String tail = subSource.getTail(); 506 boolean doRemove = true; 507 508 if (tail.indexOf('$') > 0) { 509 String subTail = tail.substring(0, tail.indexOf('$')) + _sourceExt; 510 Path subJava = subSource.getParent().lookup(subTail); 511 512 if (subJava.exists()) 513 doRemove = false; 514 } 515 516 if (doRemove) { 517 log.finer(L.l("removing obsolete class `{0}'.", subClass.getPath())); 518 519 subClass.remove(); 520 } 521 } 522 } 523 } 524 } 525 526 529 protected ClassEntry getClassEntry(String name) 530 throws ClassNotFoundException 531 { 532 Path classFile = _classDir.lookup(name.replace('.', '/') + ".class"); 533 String javaName = name.replace('.', '/') + _sourceExt; 534 Path javaFile = _sourceDir.lookup(javaName); 535 536 String tail = javaFile.getTail(); 537 538 for (int i = 0; i < INNER_CLASS_SEPARATORS.length; i++) { 539 char sep = INNER_CLASS_SEPARATORS[i]; 540 if (name.indexOf(sep) > 0) { 541 String subName = name.substring(0, name.indexOf(sep)); 542 String subJavaName = subName.replace('.', '/') + _sourceExt; 543 Path subJava = _sourceDir.lookup(subJavaName); 544 545 if (subJava.exists()) { 546 javaFile = subJava; 547 } 548 } 549 } 550 551 if (_requireSource && ! javaFile.exists()) { 552 boolean doRemove = true; 553 554 if (doRemove) { 555 log.finer(L.l("removing obsolete class `{0}'.", classFile.getPath())); 556 557 try { 558 classFile.remove(); 559 } catch (IOException e) { 560 log.log(Level.WARNING, e.toString(), e); 561 } 562 563 return null; 564 } 565 } 566 567 if (! classFile.canRead() && ! javaFile.canRead()) 568 return null; 569 570 return new CompilingClassEntry(this, getLoader(), 571 name, javaFile, 572 classFile, 573 getCodeSource(classFile)); 574 } 575 576 579 protected CodeSource getCodeSource(Path path) 580 { 581 return _codeSource; 582 } 583 584 587 boolean checkSource(Path sourceDir, String javaName) 588 { 589 try { 590 while (javaName != null && ! javaName.equals("")) { 591 int p = javaName.indexOf('/'); 592 String head; 593 594 if (p >= 0) { 595 head = javaName.substring(0, p); 596 javaName = javaName.substring(p + 1); 597 } 598 else { 599 head = javaName; 600 javaName = null; 601 } 602 603 String []names = sourceDir.list(); 604 int i; 605 for (i = 0; i < names.length; i++) { 606 if (names[i].equals(head)) 607 break; 608 } 609 610 if (i == names.length) 611 return false; 612 613 sourceDir = sourceDir.lookup(head); 614 } 615 } catch (IOException e) { 616 log.log(Level.FINE, e.toString(), e); 617 618 return false; 619 } 620 621 return true; 622 } 623 624 633 private ClassEntry loadClass(String className, Path javaFile, Path classFile) 634 { 635 long length = classFile.getLength(); 636 637 ClassEntry entry = new CompilingClassEntry(this, getLoader(), 638 className, javaFile, 639 classFile, 640 getCodeSource(classFile)); 641 642 Class cl = null; 643 644 try { 645 cl = getLoader().loadClass(entry); 646 } catch (Exception e) { 647 try { 648 if (javaFile.canRead()) 649 classFile.remove(); 650 } catch (IOException e1) { 651 } 652 } 653 654 if (cl != null) 655 return entry; 656 else 657 return null; 658 } 659 660 666 void compileClass(Path javaSource, Path javaClass, 667 String sourcePath, boolean isMake) 668 throws ClassNotFoundException 669 { 670 try { 671 JavaCompiler compiler = JavaCompiler.create(getLoader()); 672 compiler.setClassDir(_classDir); 673 compiler.setSourceDir(_sourceDir); 674 if (_encoding != null) 675 compiler.setEncoding(_encoding); 676 compiler.setArgs(_args); 677 compiler.setCompileParent(! isMake); 678 compiler.setSourceExtension(_sourceExt); 679 if (_compiler != null) 680 compiler.setCompiler(_compiler); 681 682 686 String prefix = _sourceDir.getPath(); 688 String full = javaSource.getPath(); 689 690 String source; 691 if (full.startsWith(prefix)) { 692 source = full.substring(prefix.length()); 693 if (source.startsWith("/")) 694 source = source.substring(1); 695 } 696 else 697 source = javaSource.getPath(); 698 699 703 704 compiler.compileIfModified(source, null); 705 } catch (Exception e) { 706 getLoader().addDependency(new Depend(javaSource)); 707 708 throw new CompileClassNotFound(e); 711 } 712 } 713 714 720 void compileBatch(String []files, boolean isMake) 721 throws ClassNotFoundException 722 { 723 try { 724 JavaCompiler compiler = JavaCompiler.create(getLoader()); 725 compiler.setClassDir(_classDir); 726 compiler.setSourceDir(_sourceDir); 727 if (_encoding != null) 728 compiler.setEncoding(_encoding); 729 compiler.setArgs(_args); 730 compiler.setCompileParent(! isMake); 731 compiler.setSourceExtension(_sourceExt); 732 if (_compiler != null) 733 compiler.setCompiler(_compiler); 734 735 739 compiler.compileBatch(files); 740 } catch (Exception e) { 741 getLoader().addDependency(AlwaysModified.create()); 742 743 throw new CompileClassNotFound(e); 746 } 747 } 748 749 754 public Path getPath(String name) 755 { 756 Path path = _classDir.lookup(name); 757 758 if (path != null && path.exists()) 759 return path; 760 761 path = _sourceDir.lookup(name); 762 763 if (path != null && path.exists()) 764 return path; 765 766 return null; 767 } 768 769 775 protected String getClassPath(String head) 776 { 777 CharBuffer cb = new CharBuffer(); 778 779 if (! _classDir.getScheme().equals("file")) 780 return head; 781 782 try { 783 if (! _classDir.isDirectory() && _sourceDir.isDirectory()) { 784 try { 785 _classDir.mkdirs(); 786 } catch (IOException e) { 787 } 788 } 789 790 cb.append(head); 791 792 if (_classDir.isDirectory()) { 793 if (cb.length() > 0) 794 cb.append(CauchoSystem.getPathSeparatorChar()); 795 cb.append(_classDir.getNativePath()); 796 } 797 798 if (! _classDir.equals(_sourceDir)) { 799 if (cb.length() > 0) 800 cb.append(CauchoSystem.getPathSeparatorChar()); 801 cb.append(_sourceDir.getNativePath()); 802 } 803 804 return cb.close(); 805 } catch (java.security.AccessControlException e) { 806 log.log(Level.WARNING, e.toString(), e); 807 808 return head; 809 } 810 } 811 812 protected String prefixClassPath(String tail) 813 { 814 CharBuffer cb = new CharBuffer(); 815 816 if (! _classDir.isDirectory() && _sourceDir.isDirectory()) { 817 try { 818 _classDir.mkdirs(); 819 } catch (IOException e) { 820 } 821 } 822 823 if (_classDir.isDirectory()) { 824 if (cb.length() > 0) 825 cb.append(CauchoSystem.getPathSeparatorChar()); 826 cb.append(_classDir.getNativePath()); 827 } 828 829 if (! _classDir.equals(_sourceDir)) { 830 if (cb.length() > 0) 831 cb.append(CauchoSystem.getPathSeparatorChar()); 832 cb.append(_sourceDir.getNativePath()); 833 } 834 835 if (cb.length() > 0) 836 cb.append(CauchoSystem.getPathSeparatorChar()); 837 cb.append(tail); 838 839 return cb.close(); 840 } 841 842 protected String getSourcePath(String head) 843 { 844 return getClassPath(head); 845 846 864 } 865 866 public String toString() 867 { 868 if (_classDir == null) 869 return "CompilingLoader[]"; 870 else if (_classDir.equals(_sourceDir)) 871 return "CompilingLoader[src:" + _sourceDir + "]"; 872 else 873 return ("CompilingLoader[src:" + _sourceDir + 874 ",class:" + _classDir + "]"); 875 } 876 } 877 | Popular Tags |