1 package de.uni_hamburg.eggink.autojar; 2 3 19 20 import java.io.*; 21 import java.util.*; 22 import java.util.jar.*; 23 import java.util.zip.*; 24 25 import org.apache.bcel.*; 26 import org.apache.bcel.classfile.*; 27 import org.apache.bcel.util.*; 28 import org.apache.bcel.generic.*; 29 30 33 34 public class Autojar 35 { 36 static final private String PATH_SEPARATOR = System.getProperty("path.separator"), 37 JAVA_HOME = System.getProperty("java.home"); 38 39 static private boolean autodynamic, 40 bothpaths, 41 debug, 42 dynamic, 43 quiet, 44 searchExtensions, 45 verbose; 46 static private File basedir; 47 static private String outfile, 48 manifest, 49 pwd, 50 classpathStr, 51 userClasspath; 52 static private JarOutputStream jarout; 53 static private ClassPath classpath; 54 static private FilePath filepath; 55 static HashSet doneFiles = new HashSet(), 56 zipEntries = new HashSet(), 57 usedJars = new HashSet(); 58 static ArrayList excludes = new ArrayList(), 59 fileExcludes = new ArrayList(); 60 static private byte[] buffer = new byte[8192]; 61 static TreeSet forname = new TreeSet(), 62 missing = new TreeSet(); 63 64 66 private Autojar() 67 { } 68 69 71 72 73 static private String expandClasspath(String path) 74 { 75 String clspth = "", sep = ""; 76 StringTokenizer tok = new StringTokenizer(path, PATH_SEPARATOR); 77 78 while (tok.hasMoreTokens()) 79 { 80 String el = tok.nextToken(); 81 82 if (el.indexOf('*') >= 0 || el.indexOf('?') >= 0) 83 { 84 86 String [] list = FileExpand.getList(null, el); 87 88 for (int i = 0; i < list.length; ++i) 89 { 90 clspth += sep + list[i]; 91 sep = PATH_SEPARATOR; 92 } 93 } 94 else 95 { 96 clspth += sep + el; sep = PATH_SEPARATOR; 98 } 99 } 100 101 return clspth; 102 } 103 104 106 110 111 static void handleClassFile(String path) 112 throws IOException 113 { 114 File file = new File(basedir, path); 115 FileInputStream in = new FileInputStream(file); 116 String zipPath = path.replace(File.separatorChar, '/'); 117 118 120 if (putEntry(zipPath)) 121 { 122 124 int n; 125 126 while ((n = in.read(buffer)) >= 0) 127 jarout.write(buffer, 0, n); 128 129 in.close(); 130 131 133 handleReferences(new FileInputStream(file), path.substring(path.length() - 6).replace(File.separatorChar, '.')); 134 missing.remove(zipPath); 135 136 if (verbose) 137 System.out.println("added: " + zipPath); 138 139 doneFiles.add(zipPath); 140 } 141 } 142 143 145 149 150 static private void handleDirectory(String path) 151 throws IOException 152 { 153 File file = new File(basedir, path); 154 155 String zipPath = path.replace(File.separatorChar, '/'); 156 157 if (verbose) 158 System.out.println("added: " + zipPath); 159 160 162 putEntry(zipPath + '/'); 163 164 166 String [] files = file.list(); 167 168 for (int i = 0; i < files.length; ++i) 169 handleNormalFile(new File(path, files[i]).getPath()); 170 } 171 172 174 178 179 static private void handleJarFile(String path) 180 throws IOException 181 { 182 File file = new File(basedir, path); 183 JarFile jarfile = new JarFile(file); 184 185 for (Enumeration entries = jarfile.entries(); entries.hasMoreElements(); ) 186 { 187 JarEntry entry = (JarEntry)entries.nextElement(); 188 String entryName = entry.getName(); 189 190 if (entryName.startsWith("META-INF")) 191 continue; 192 193 InputStream in = jarfile.getInputStream(entry); 194 int n; 195 196 if (putEntry(entry)) 197 { 198 while ((n = in.read(buffer)) >= 0) 199 jarout.write(buffer, 0, n); 200 201 in.close(); 202 doneFiles.add(entryName); 203 204 if (entryName.endsWith(".class")) 205 handleReferences(jarfile.getInputStream(entry), 206 entryName.substring(entryName.length() - 6).replace(File.separatorChar, '.')); 207 208 missing.remove(entryName); 209 210 if (verbose) 211 System.out.println("added: " + path + " => " + entry.getName()); 212 } 213 } 214 215 classpathStr += PATH_SEPARATOR + path; 216 setClassPathFromString(); 217 } 218 219 221 225 226 static private void handleList(String [] list) 227 throws IOException 228 { 229 231 for (int iex = 0; iex < list.length; ++iex) 232 { 233 String name = list[iex]; 234 235 if (Utils.patternMatches(fileExcludes, name)) 236 continue; 237 238 File file = new File(basedir, name); 239 240 String path = file.getPath(); 241 242 244 if (file.isDirectory()) 245 { 246 248 handleDirectory(name); 249 } 250 else if (path.endsWith(".class")) 251 { 252 254 handleClassFile(name); 255 } 256 else if (path.endsWith(".jar")) 257 { 258 260 handleJarFile(name); 261 } 262 else 263 { 264 266 handleNormalFile(name); 267 } 268 } 269 } 270 271 273 277 278 static private void handleNormalFile(String path) 279 throws IOException 280 { 281 String zipPath = path.replace(File.separatorChar, '/'); 282 File file = new File(basedir, path); 283 284 if (Utils.patternMatches(fileExcludes, file.getName())) 285 return; 286 287 if (file.isDirectory()) 288 { 289 291 handleDirectory(path); 292 return; 293 } 294 295 BufferedInputStream in = new BufferedInputStream(new FileInputStream(file)); 296 int n; 297 298 300 if (putEntry(zipPath)) 301 { 302 while((n = in.read(buffer)) >= 0) 303 jarout.write(buffer, 0, n); 304 305 doneFiles.add(path); 306 missing.remove(path); 307 308 if (verbose) 309 System.out.println("added: " + zipPath); 310 } 311 } 312 313 315 320 321 static private void handleReferences(InputStream in, String packpath) 322 throws IOException 323 { 324 JavaClass klass = new ClassParser(in, packpath).parse(); 325 Avisitor visitor = new Avisitor(klass); 326 ConstantPoolGen pool = new ConstantPoolGen(klass.getConstantPool()); 327 328 new DescendingVisitor(klass, visitor).visit(); 329 in.close(); 330 331 if (! dynamic) return; 333 334 336 int indexForName = visitor.getIndexForName(); 337 338 if (indexForName < 0) return; 340 341 343 Method[] methods = klass.getMethods(); 344 345 for (int i = 0; i < methods.length; ++i) 346 { 347 Method method = methods[i]; 348 Code code = method.getCode(); 349 350 if (code == null 351 || method.getName().endsWith("$") || method.getName().equals("class") ) 354 continue; 355 356 358 InstructionList instructions = null; 359 360 try 361 { 362 instructions = new InstructionList(code.getCode()); 363 } 364 catch (ClassGenException ex) 365 { 366 System.out.println(code); 367 ex.printStackTrace(); 368 System.exit(1); 369 } 370 371 373 for (Iterator it = instructions.iterator(); it.hasNext(); ) 374 { 375 InstructionHandle handle = (InstructionHandle)it.next(); 376 Instruction instruction = handle.getInstruction(); 377 378 if (instruction instanceof InvokeInstruction) 379 { 380 382 ConstantCP constant = (ConstantCP)pool.getConstant(((InvokeInstruction)instruction).getIndex()); 383 384 if (constant.getNameAndTypeIndex() == indexForName) 385 { 386 388 Instruction pre = handle.getPrev().getInstruction(); 389 390 if (pre.getOpcode() == Constants.LDC) { 392 394 LDC ldc = (LDC)pre; 395 396 String operand = (String )ldc.getValue(pool); boolean found = false; 398 399 found = autodynamic && lookupClass(operand); 400 401 System.out.print("* Dynamic loading: class " + klass.getClassName() 402 + ", method " + method.getName() 403 + ", name=\"" + operand + "\""); 404 405 if (found) 406 System.out.println(" (RESOLVED)"); 407 else 408 System.out.println(); 409 } 410 else 411 { 412 414 forname.add("class " + klass.getClassName() + ", method " + method.getName()); 415 } 416 } 417 } 418 } 419 } 420 } 421 422 424 425 426 static private void help(boolean full) 427 { 428 if (full) 429 printVersion(); 430 431 System.out.println("Usage: java -jar autojar.jar [option...] file..."); 432 System.out.println("Options: -c classpath where to look for classes"); 433 System.out.println(" -b use classpath for normal files"); 434 System.out.println(" -C dir change to dir temporarily"); 435 System.out.println(" -d look for dynamic loading"); 436 System.out.println(" -a add dynamically loaded classes if possible (implies d)"); 437 System.out.println(" -e search jar files in extension dirs"); 438 System.out.println(" -h print this help and exit"); 439 System.out.println(" -m manifest manifest file"); 440 System.out.println(" -o outfile output file (mandatory)"); 441 System.out.println(" -p filepath where to look for non-class files"); 442 System.out.println(" -q be quiet"); 443 System.out.println(" -v be verbose"); 444 System.out.println(" -x prefix skip bases starting with prefix"); 445 System.out.println("Files: name class name"); 446 System.out.println(" path file or directory path"); 447 System.out.println(" -C dir change to dir temporarily"); 448 System.out.println(" -X pattern skip files matching pattern"); 449 System.out.println(" -X - clear pattern list"); 450 } 451 452 454 460 461 static boolean lookupClass(String packpath) 462 throws IOException 463 { 464 String path = packpath + ".class"; 465 466 if (doneFiles.contains(path)) return true; 468 469 ClassPath.ClassFile classFile = null; 470 471 473 try 474 { 475 classFile = classpath.getClassFile(packpath); 476 } 477 catch (IOException ex) 478 { 479 missing.add(path); 480 return false; 481 } 482 483 485 String base = classFile.getBase(); 486 487 if (base.startsWith(JAVA_HOME)) 488 return true; 490 492 for (Iterator it = Autojar.excludes.iterator(); it.hasNext(); ) 493 if (base.startsWith((String )it.next())) 494 return false; 495 496 498 String filename = packpath + ".class"; 499 byte[] bytes = classpath.getBytes(packpath); 500 501 if (putEntry(filename)) 502 { 503 jarout.write(bytes); 504 doneFiles.add(path); 505 missing.remove(path); 506 507 boolean isJar = base.endsWith(".jar"); 508 509 usedJars.add(base); 510 511 if (verbose) 512 { 513 System.out.print("added: " + base + " => "); 514 515 if (isJar) 516 System.out.println(classFile.getPath()); 517 else 518 System.out.println(packpath); 519 } 520 521 523 handleReferences(classFile.getInputStream(), packpath); 524 } 525 526 return true; 527 } 528 529 531 535 536 static private void lookupNormalFile(String name) 537 throws IOException 538 { 539 if (filepath == null) { 541 missing.add(name); 542 return; 543 } 544 545 NormalFile theFile; 546 547 try 548 { 549 theFile = filepath.getFile(name); 550 } 551 catch (IOException ex) 552 { 553 missing.add(name); 554 return; 555 } 556 557 String fpath = theFile.getPath(); 558 559 if (doneFiles.contains(fpath)) 560 return; 562 missing.remove(fpath); 563 564 566 BufferedInputStream in = new BufferedInputStream(theFile.getInputStream()); 567 int n; 568 ByteArrayOutputStream out = new ByteArrayOutputStream(2048); 569 570 if (putEntry(name)) 571 { 572 while((n = in.read(buffer)) >= 0) 573 jarout.write(buffer, 0, n); 574 575 in.close(); 576 doneFiles.add(fpath); 577 578 if (verbose) 579 { 580 String base = theFile.getBase(); 581 582 System.out.print("added: " + base + " => "); 583 584 if (base.endsWith(".jar")) 585 System.out.println(theFile.getPath()); 586 else 587 System.out.println(name); 588 } 589 } 590 } 591 592 594 static public void main(String [] args) 595 { 596 String basedirName = "", 597 filepathStr = ""; 598 599 601 try 602 { 603 Getopt opts = new Getopt(args, "abc:dDehm:o:p:qvx:"); 604 int opt; 605 606 while ((opt = opts.getOption()) >= 0) 607 { 608 switch (opt) 609 { 610 case 'a': 612 dynamic = autodynamic = true; 613 break; 614 615 case 'b': 617 bothpaths = true; 618 break; 619 620 case 'c': 622 if (classpathStr == null) 623 classpathStr = opts.getOptarg(); 624 else 625 classpathStr += PATH_SEPARATOR + opts.getOptarg(); 626 627 break; 628 629 case 'd': 631 dynamic = true; 632 break; 633 634 case 'D': 636 debug = true; 637 break; 638 639 case 'e': 641 searchExtensions = true; 642 break; 643 644 case 'h': 646 help(true); 647 System.exit(0); 648 break; 649 650 case 'm': 652 manifest = opts.getOptarg(); 653 break; 654 655 case 'o': 657 outfile = opts.getOptarg(); 658 break; 659 660 case 'p': 662 if (filepathStr.length() == 0) 663 filepathStr = new String (opts.getOptarg()); 664 else 665 filepathStr += PATH_SEPARATOR + new String (opts.getOptarg()); 666 667 break; 668 669 case 'q': 671 quiet = true; 672 verbose =false; 673 break; 674 675 case 'v': 677 verbose = true; 678 quiet = false; 679 break; 680 681 case 'x': 683 excludes.add(opts.getOptarg()); 684 break; 685 686 default: 688 help(false); 689 System.exit(1); 690 } 691 } 692 693 if (verbose) 694 printVersion(); 695 696 698 700 String extdirs = System.getProperty("java.ext.dirs"), 701 extwild = ""; 702 703 if (searchExtensions && extdirs != null) 704 { 705 StringTokenizer tok = new StringTokenizer(extdirs, PATH_SEPARATOR); 706 707 while (tok.hasMoreTokens()) 708 extwild += PATH_SEPARATOR + tok.nextToken() + File.separator + "*.jar"; 709 } 710 711 713 userClasspath = classpathStr; 714 715 classpathStr = expandClasspath(System.getProperty("sun.boot.class.path") 716 + PATH_SEPARATOR 717 + (classpathStr != null ? classpathStr : ".") 718 + extwild); 719 720 722 classpath = new ClassPath(classpathStr); 723 724 if (filepathStr.length() == 0) 725 { 726 if (bothpaths) 727 { 728 filepathStr = classpathStr; 729 filepath = new FilePath(expandClasspath(filepathStr)); 730 } 731 else 732 filepath = new FilePath("."); 733 } 734 else 735 filepath = new FilePath(expandClasspath(filepathStr)); 736 737 739 if (outfile == null) 740 { 741 System.out.println("ERROR: output file missing"); 742 help(false); 743 System.exit(1); 744 } 745 746 if (verbose) 747 { 748 System.out.println("Class path: " + classpathStr); 749 System.out.println("File path: " + (filepathStr != null ? filepathStr : "")); 750 System.out.println("Output file: " + outfile); 751 } 752 753 755 FileOutputStream out = new FileOutputStream(outfile); 756 String mainclass = null; 757 758 if (manifest == null) jarout = new JarOutputStream(out); 760 else 761 { if (verbose) 763 System.out.println("Manifest: " + manifest); 764 765 Manifest mani = new Manifest(); 766 FileInputStream maniIn = new FileInputStream(manifest); 767 768 mani.getMainAttributes().putValue("Manifest-Version", "1.0"); 769 mani.read(maniIn); 770 maniIn.close(); 771 jarout = new JarOutputStream(out, mani); 772 773 775 mainclass = mani.getMainAttributes().getValue("Main-Class"); 776 777 if (mainclass != null) 778 lookupClass(mainclass.replace('.', '/')); 779 } 780 781 783 String [] files = opts.getParms(); 784 785 if (files.length == 0 && mainclass == null) 786 { 787 System.out.println("ERROR: No input files"); 788 System.exit(1); 789 } 790 791 793 boolean haveBasedir = false, 794 haveExclude = false; 795 796 pwd = new File(".").getCanonicalPath(); 797 798 for (int i = 0; i < files.length; ++i) 799 { 800 String name = files[i]; 801 802 if (name.equals("-C")) 803 { 804 806 haveBasedir = true; 807 haveExclude = false; 808 continue; 809 } 810 811 if (name.equals("-X")) 812 { 813 815 haveExclude = true; 816 haveBasedir = false; 817 continue; 818 } 819 820 if (name.equals("-X-")) 821 { 822 824 fileExcludes.clear(); 825 haveExclude = false; 826 haveBasedir = false; 827 continue; 828 } 829 830 if (haveBasedir) 831 { 832 834 basedirName = files[i]; 835 basedir = new File(basedirName).getCanonicalFile(); 836 837 if (verbose) 838 System.out.println("Entering directory '" + basedirName + "'"); 839 840 haveBasedir = false; 841 continue; 842 } 843 844 if (haveExclude) 845 { 846 if (files[i].equals("-")) 847 fileExcludes.clear(); 848 else 849 fileExcludes.add(files[i]); 850 851 haveExclude = false; 852 continue; 853 } 854 855 if (verbose) 856 System.out.println("Argument: " + name); 857 858 if (name.indexOf('*') >= 0 || name.indexOf('?') >= 0) 859 { 860 862 String [] list = FileExpand.getList(basedir, name); 863 864 if (list == null || list.length == 0) 865 { 866 868 System.err.print("Warning: "); 869 870 if (basedir != null) 871 System.err.print(new File(basedir, name)); 872 else 873 System.err.print(name); 874 875 System.err.println(" not found"); 876 } 877 else 878 handleList(list); 879 } 880 else if (name.equals(".")) 881 { 882 884 handleList(basedir.list()); 885 } 886 else if (name.endsWith(".jar")) 887 { 888 890 handleJarFile(name); 891 } 892 else if (name.endsWith(".class")) 893 { 894 896 if (basedir != null || new File(name).exists()) 897 handleClassFile(name); 898 else 899 lookupClass(name.substring(0, name.length() - 6).replace('.', '/')); } 901 else 902 { 903 905 File file = new File(name); 906 907 if (basedir != null || file.exists()) 908 handleNormalFile(name); 909 else if (file.isAbsolute()) 910 missing.add(name); 911 else 912 lookupNormalFile(name); 913 } 914 915 if (basedir != null && verbose) 916 System.out.println("Leaving directory '" + basedir + "'"); 917 918 basedir = null; 919 } 920 } 921 catch (Exception ex) 922 { 923 if (debug) 924 ex.printStackTrace(); 925 else 926 System.err.println(ex); 927 } 928 finally 929 { 930 try 931 { 932 if (zipEntries.size() > 0) 933 jarout.close(); 934 } 935 catch (IOException e) 936 { 937 if (debug) 938 e.printStackTrace(); 939 } 940 } 941 942 if (verbose) 943 reportUnusedJars(); 944 945 if (forname.size() > 0) 946 reportDynamicLoading(); 947 948 if (missing.size() > 0 && ! quiet) 949 reportMissingFiles(); 950 951 System.out.println(); 952 } 953 954 956 957 958 static private void printVersion() 959 { 960 System.out.println("Autojar " + Version.VERSION + ", Copyright (C) 2004 Bernd Eggink"); 961 System.out.println("http://autojar.sourceforge.net/"); 962 System.out.println("using BCEL 5.1 (http://jakarta.apache.org/bcel/)\n"); 963 System.out.println("Autojar comes with ABSOLUTELY NO WARRANTY."); 964 System.out.println("This is free software, and you may redistribute it"); 965 System.out.println("under the conditions of the GNU General Public License.\n"); 966 } 967 968 970 974 975 static private boolean putEntry(JarEntry entry) 976 throws IOException 977 { 978 if (zipEntries.contains(entry.getName())) 979 return false; 980 981 983 ZipEntry tmpEntry = new ZipEntry(entry); 984 985 tmpEntry.setCompressedSize(-1); 986 jarout.putNextEntry(tmpEntry); 987 988 991 992 zipEntries.add(entry.getName()); 993 return true; 994 } 995 996 998 1001 1002 static private boolean putEntry(String name) 1003 throws IOException 1004 { 1005 return putEntry(new JarEntry(name)); 1006 } 1007 1008 1010 1011 1012 static private void reportDynamicLoading() 1013 { 1014 System.out.println("\n* Dynamic loading, unknown classname:"); 1015 1016 for (Iterator it = forname.iterator(); it.hasNext(); ) 1017 System.out.println(" " + (String )it.next()); 1018 } 1019 1020 1022 1023 1024 static private void reportMissingFiles() 1025 { 1026 System.out.println("\nMissing files:"); 1027 1028 for (Iterator it = missing.iterator(); it.hasNext(); ) 1029 System.out.println(" " + (String )it.next()); 1030 } 1031 1032 1034 1035 1036 static private void reportUnusedJars() 1037 { 1038 if (userClasspath == null) 1039 return; 1040 1041 StringTokenizer tok = new StringTokenizer(userClasspath, PATH_SEPARATOR); 1042 String [] unused = new String [tok.countTokens()]; 1043 int n = 0; 1044 1045 while (tok.hasMoreTokens()) 1046 { 1047 String part = tok.nextToken(); 1048 1049 if (! part.endsWith(".jar")) 1050 continue; 1051 1052 if (! usedJars.contains(part)) 1053 unused[n++] = part; 1054 } 1055 1056 if (n > 0) 1057 { 1058 Arrays.sort(unused, 0, n); 1059 1060 System.out.println("\nUnused jar files in classpath:"); 1061 1062 for (int i = 0; i < n; ++i) 1063 System.out.println(" " + unused[i]); 1064 } 1065 1066 } 1067 1068 1070 1071 1072 static private void setClassPathFromString() 1073 { 1074 classpath = new ClassPath(classpathStr); 1075 1076 if (bothpaths) 1077 filepath = new FilePath(classpathStr); 1078 } 1079 1080} 1081 1082 1083 1084 | Popular Tags |