1 25 26 27 package com.lutris.classloader; 28 29 import java.io.ByteArrayOutputStream ; 30 import java.io.File ; 31 import java.io.FileNotFoundException ; 32 import java.io.IOException ; 33 import java.io.InputStream ; 34 import java.net.MalformedURLException ; 35 import java.net.URL ; 36 import java.util.Enumeration ; 37 import java.util.Hashtable ; 38 import java.util.StringTokenizer ; 39 import java.util.Vector ; 40 41 import com.lutris.logging.LogChannel; 42 43 194 public class MultiClassLoader extends ClassLoader { 195 196 200 public interface ClassFilter { 201 205 public static final int NORMAL_LOAD = 1; 206 211 public static final int DONT_LOAD = 2; 212 218 public static final int CAN_LOAD = 3; 219 224 public static final int MUST_LOAD = 4; 225 226 231 public int loadCheck(String className); 232 } 233 234 237 public class ClassResource { 238 239 242 private Class classObj; 243 244 247 private Resource resource; 248 249 252 public ClassResource(Class classObj, Resource resource) { 253 this.classObj = classObj; 254 this.resource = resource; 255 } 256 257 260 public Class getClassObj() { 261 return classObj; 262 } 263 264 268 public Resource getResource() { 269 return resource; 270 } 271 } 272 273 277 private Hashtable loadedClasses = new Hashtable (); 278 279 282 private ClassPath classPath; 283 284 287 public static final String LOG_LEVEL = "CLASSLOAD"; 288 289 292 private boolean loggingEnabled = false; 293 294 297 private LogChannel logChannel; 298 299 302 private int logLevel; 303 304 308 private Vector filters = null; 309 310 313 private ClassLoader parentClassLoader; 314 315 318 private ClassLoader secondaryClassLoader; 319 320 325 private boolean forceSecondaryLoader = false; 327 330 private boolean isAutoReload = false; 332 343 public MultiClassLoader(ClassLoader parent, ClassLoader secondary, LogChannel loadLogChannel) { 344 classPath = new ClassPath(loadLogChannel); 345 parentClassLoader = (null != parent) ? parent : getSystemClassLoader(); 348 secondaryClassLoader = secondary; 349 logChannel = loadLogChannel; 350 if (logChannel != null) { 351 logLevel = logChannel.getLevel(LOG_LEVEL); 352 loggingEnabled = logChannel.isEnabled(logLevel); 353 } 354 355 } 356 357 362 public MultiClassLoader(LogChannel loadLogChannel) { 363 this(null, getSystemClassLoader(), loadLogChannel); 364 } 365 366 372 public MultiClassLoader(String path, LogChannel loadLogChannel) { 373 this(new String [] {path}, loadLogChannel); 374 } 375 376 382 public MultiClassLoader(String [] path, LogChannel loadLogChannel) { 383 this(loadLogChannel); 384 setClassPath(path); 385 } 386 387 393 public MultiClassLoader(File path, LogChannel loadLogChannel) { 394 this(new File [] {path} 395 , loadLogChannel); 396 } 397 398 404 public MultiClassLoader(File [] path, LogChannel loadLogChannel) { 405 this(loadLogChannel); 406 setClassPath(path); 407 } 408 409 416 public MultiClassLoader(URL path, LogChannel loadLogChannel) { 417 this(new URL [] {path} 418 , loadLogChannel); 419 } 420 421 428 public MultiClassLoader(URL [] path, LogChannel loadLogChannel) { 429 this(loadLogChannel); 430 setClassPath(path); 431 } 432 433 440 public void forceSecondaryLoader(boolean force) { this.forceSecondaryLoader = force; 442 } 443 444 451 public void enableAutoReloadForSecLoader(boolean enable) { this.isAutoReload = enable; 453 } 454 455 458 public void removeLoadedClass() { this.loadedClasses = new Hashtable (); 460 } 461 462 467 public void setClassPath(String path) { 468 setClassPath(new String [] {path}); 469 } 470 471 476 public synchronized void setClassPath(String [] path) { 477 classPath.set(path); 478 } 479 480 485 public void setClassPath(File path) { 486 setClassPath(new File [] {path}); 487 } 488 489 494 public synchronized void setClassPath(File [] path) { 495 classPath.set(path); 496 } 497 498 504 public void setClassPath(URL path) { 505 setClassPath(new URL [] {path}); 506 } 507 508 514 public synchronized void setClassPath(URL [] path) { 515 classPath.set(path); 516 } 517 518 524 public void addClassPath(String path) { 525 addClassPath(new String [] {path}); 526 } 527 528 534 public synchronized void addClassPath(String [] path) { 535 classPath.add(path); 536 } 537 538 543 public void addClassPath(File path) { 544 addClassPath(new File [] {path}); 545 } 546 547 553 public synchronized void addClassPath(File [] path) { 554 classPath.add(path); 555 } 556 557 564 public void addClassPath(URL path) { 565 addClassPath(new URL [] {path}); 566 } 567 568 575 public synchronized void addClassPath(URL [] path) { 576 classPath.add(path); 577 } 578 579 583 public synchronized void clearClassPath() { 584 classPath.clear(); 585 } 586 587 597 public URL [] getClassPath() { 598 int len = classPath.getLength(); 599 URL [] urlPath = new URL [len]; 600 Enumeration cpeEnum = classPath.getPath(); 601 for (int i = 0; i < len; i++) { 602 ClassPathEntry cpe = (ClassPathEntry) cpeEnum.nextElement(); 603 urlPath[i] = cpe.getURL(); 604 } 605 return urlPath; 606 } 607 608 611 public static String [] parseClassPath(String path) { 612 String systemSeparator = System.getProperty("path.separator"); 613 if (systemSeparator == null) { 614 throw new NullPointerException ("path.separator property not defined"); 615 } 616 StringTokenizer tokenizer = new StringTokenizer (path, systemSeparator); 617 String [] parsed = new String [tokenizer.countTokens()]; 618 for (int i = 0; tokenizer.hasMoreTokens(); i++) { 619 parsed[i] = tokenizer.nextToken(); 620 } 621 return parsed; 622 } 623 624 630 public static URL [] getSystemClassPath() { 631 String systemClassPath = System.getProperty("java.class.path"); 633 if (systemClassPath == null) { 634 systemClassPath = ""; 635 } 636 String [] parsedPath = parseClassPath(systemClassPath); 637 Vector urlVector = new Vector (parsedPath.length); 639 for (int i = 0; i < parsedPath.length; i++) { 640 try { 641 urlVector.addElement(new URL ("file", "", parsedPath[i])); 642 } 643 catch (MalformedURLException mue) { 644 } 646 } 647 URL [] urlArray = new URL [urlVector.size()]; 648 urlVector.copyInto(urlArray); 649 return urlArray; 650 } 651 652 655 public void setParent(ClassLoader parent) { 656 parentClassLoader = parent; 657 } 658 659 662 public ClassLoader getSecondary() { 663 return secondaryClassLoader; 664 } 665 666 669 public void setSecondary(ClassLoader secondary) { 670 secondaryClassLoader = secondary; 671 } 672 673 676 public LogChannel getLogChannel() { 677 return logChannel; 678 } 679 680 684 public synchronized void addClassFilter(ClassFilter filter) { 685 if (filters == null) { 686 filters = new Vector (); 687 } 688 filters.addElement(filter); 689 } 690 691 697 private int checkFilters(String className) { 698 int restrict = ClassFilter.NORMAL_LOAD; 699 if (className.startsWith("java.lang.")) { 700 restrict = ClassFilter.DONT_LOAD; 701 } 702 else if (filters != null) { 703 int numFilters = filters.size(); 704 for (int idx = 0; idx < numFilters; idx++) { 705 restrict = ( (ClassFilter) filters.elementAt(idx)).loadCheck(className); 706 if (restrict != ClassFilter.NORMAL_LOAD) { 707 break; 708 } 709 } 710 } 711 if ( (restrict != ClassFilter.NORMAL_LOAD) && loggingEnabled) { 713 String msg = ""; 714 switch (restrict) { 715 case ClassFilter.DONT_LOAD: 716 msg = "Filter disallows loading by this classloader: "; 717 break; 718 case ClassFilter.CAN_LOAD: 719 msg = "Filter allows loading by this classloader: "; 720 break; 721 case ClassFilter.MUST_LOAD: 722 msg = "Filter requires loading by this classloader: "; 723 break; 724 } 725 if (loggingEnabled) { 726 logChannel.write(logLevel, msg + className); 727 } 728 } 729 return restrict; 730 } 731 732 741 private ClassResource checkForLoadedClass(String className) { 742 ClassResource cr = (ClassResource) loadedClasses.get(className); 743 if ( (cr != null) && loggingEnabled) { 744 logChannel.write(logLevel, "loadClass already loaded: " + className); 745 } 746 return cr; 747 } 748 749 771 public Class loadClass(String className, boolean resolve) throws ClassNotFoundException { 772 return loadClassResource(className, resolve).getClassObj(); 773 } 774 775 public Class loadClass(String className) throws ClassNotFoundException { 776 if (this.forceSecondaryLoader) 777 return loadClassResource(className, false).getClassObj(); 778 else 779 return super.loadClass(className); 780 } 781 782 785 private void logClassLoadFailure(String className, Throwable except) { 786 if (loggingEnabled) { 787 logChannel.write(logLevel, "load of class failed: " + className, except); 788 } 789 } 790 791 794 private void logReadFailure(String name, Throwable except) { 795 if (loggingEnabled) { 796 logChannel.write(logLevel, "read of resource failed: " + name, except); 797 } 798 } 799 800 814 private ClassResource loadClassResource(String className, boolean resolve) throws ClassNotFoundException { 815 if (loggingEnabled) { 816 logChannel.write(logLevel, "loadClass: " + className); 817 } 818 try { 819 ClassResource cr = checkForLoadedClass(className); 820 if (cr == null) { 821 cr = doLoadClass(className); 822 if (resolve) { 823 resolveClass(cr.getClassObj()); 824 } 825 } 826 return cr; 827 } 828 catch (ClassNotFoundException except) { 829 logClassLoadFailure(className, except); 830 throw except; 831 } 832 catch (IOException except) { 833 logClassLoadFailure(className, except); 834 throw new ClassNotFoundException (except.getClass().getName() + ": " + 835 except.getMessage()); 836 } 837 catch (RuntimeException except) { 838 logClassLoadFailure(className, except); 839 throw except; 840 } 841 catch (Error except) { 842 logClassLoadFailure(className, except); 843 throw except; 844 } 845 } 846 847 850 public Resource getClassResource(String className) throws ClassNotFoundException { 851 ClassResource cr = checkForLoadedClass(className); 852 if (cr == null) { 853 throw new ClassNotFoundException ("Class \"" + className + "\" is not loaded by this class loader"); 854 } 855 return cr.getResource(); 856 } 857 858 862 private ClassResource loadClassOther(String className, ClassLoader loader, 863 String loaderName) { 864 ClassResource cr = null; 865 866 if (loggingEnabled) { 867 logChannel.write(logLevel, "checking " + loaderName + " class loader: " + className); 868 } 869 try { 870 if (loader instanceof MultiClassLoader) { 871 cr = ( (MultiClassLoader) loader).loadClassResource(className, false); 872 } 873 else { 874 Class c = loader.loadClass(className); 875 cr = new ClassResource(c, null); 876 } 877 if (loggingEnabled) { 878 logChannel.write(logLevel, "class loaded by: " + loaderName); 879 } 880 } 881 catch (ClassNotFoundException except) { 882 if (loggingEnabled) { 884 logChannel.write(logLevel, "class not loaded by " + loaderName 885 + ": " + except.getClass().getName() + ": " + except.getMessage()); 886 } 887 } 888 return cr; 889 } 890 891 895 private ClassResource loadClassHere(String className) throws IOException { 896 ClassResource cr = null; 897 898 if (loggingEnabled) { 899 logChannel.write(logLevel, "checking MultiClassLoader: " + className); 900 } 901 String fileName = className.replace('.', '/').concat(".class"); 902 Resource resource = getResourceObject(fileName); 903 904 if (resource != null) { 905 906 Class c = null; 907 try { 908 if (isAutoReload && forceSecondaryLoader) { 910 InputStream is = null; 911 try { 912 is = this.secondaryClassLoader.getResource(fileName).openConnection().getInputStream(); 913 c = getClassFromStream(className, is); 914 } 915 catch (Throwable th) { 916 try { 917 is = this.secondaryClassLoader.getResourceAsStream(fileName); 918 c = getClassFromStream(className, is); 919 } 920 catch (Throwable th1) { 921 c = Class.forName(className, true, this.secondaryClassLoader); 922 } 923 } 924 925 } 926 else if (forceSecondaryLoader) { 928 c = Class.forName(className, true, this.secondaryClassLoader); 929 } 930 else 932 c = Class.forName(className); 933 } 934 catch (ClassNotFoundException e) { 935 try { 936 c = Class.forName(className, true, ClassLoader.getSystemClassLoader()); 940 } 941 catch (ClassNotFoundException e1) { 942 byte[] classBytes = resource.getBytes(); 945 c = defineClass(className, classBytes, 0, classBytes.length); 946 } 947 } 948 949 cr = new ClassResource(c, resource); 950 loadedClasses.put(className, cr); 951 952 if (loggingEnabled) { 953 logChannel.write(logLevel, "class loaded by MultiClassLoader: " + className); 954 } 955 } 956 if ( (cr == null) && loggingEnabled) { 957 logChannel.write(logLevel, "class not loaded by MultiClassLoader: " + className); 958 } 959 return cr; 960 } 961 962 972 private Class getClassFromStream(String className, InputStream is) throws IOException { 973 ByteArrayOutputStream baos = new ByteArrayOutputStream (); 974 byte[] buf = new byte[1024]; 975 int i = is.read(buf); 976 while (i == 1024) { 977 baos.write(buf); 978 i = is.read(buf); 979 } 980 if (i != -1) 981 baos.write(buf, 0, i); 982 983 buf = baos.toByteArray(); 984 baos.close(); 985 is.close(); 986 987 return defineClass(className, buf, 0, buf.length); 988 } 989 1004 private synchronized ClassResource doLoadClass(String className) throws ClassNotFoundException , 1005 IOException { 1006 ClassResource cr = checkForLoadedClass(className); 1008 if (cr != null) { 1009 return cr; 1010 } 1011 int filterRestrict = checkFilters(className); 1013 1014 if ( (parentClassLoader != null) && (filterRestrict != ClassFilter.CAN_LOAD) 1016 && (filterRestrict != ClassFilter.MUST_LOAD)) { 1017 cr = loadClassOther(className, parentClassLoader, "parent"); 1018 if (cr != null) { 1019 return cr; 1020 } 1021 } 1022 1023 if ( (filterRestrict == ClassFilter.NORMAL_LOAD) || (filterRestrict == ClassFilter.CAN_LOAD) 1025 || (filterRestrict == ClassFilter.MUST_LOAD)) { 1026 cr = loadClassHere(className); 1027 if (cr != null) { 1028 return cr; 1029 } 1030 } 1031 1032 if ( (secondaryClassLoader != null) && (filterRestrict != ClassFilter.MUST_LOAD)) { 1034 cr = loadClassOther(className, secondaryClassLoader, "secondary"); 1035 if (cr != null) { 1036 return cr; 1037 } 1038 } 1039 throw new ClassNotFoundException (className); 1040 } 1041 1042 1048 public URL getResource(String name) { 1049 Resource resource = getResourceObject(name); 1050 if (resource != null) { 1051 ClassPathEntry location = resource.getLocation(); 1052 try { 1053 return new URL (location.getURL() + resource.getName()); 1054 } 1055 catch (MalformedURLException mue) { 1056 if (loggingEnabled) { 1057 logChannel.write(logLevel, "getResource not returned due to exception: " + name, mue); 1058 } 1059 return null; 1060 } 1061 } 1062 return null; 1063 } 1064 1065 1071 public Resource getResourceObject(String name) { 1072 Resource resource; 1073 if (loggingEnabled) { 1074 logChannel.write(logLevel, "getResource loading: " + name); 1075 } 1076 try { 1077 resource = classPath.getResource(name); 1078 if (loggingEnabled) { 1079 if (resource == null) { 1080 logChannel.write(logLevel, "getResource not found: " + name); 1081 } 1082 else { 1083 logChannel.write(logLevel, "getResource finished: " + name); 1084 } 1085 } 1086 } 1087 catch (RuntimeException except) { 1088 logReadFailure(name, except); 1089 throw except; 1090 } 1091 catch (Error except) { 1092 logReadFailure(name, except); 1093 throw except; 1094 } 1095 return resource; 1096 } 1097 1098 1103 public Resource getResourceAsIs(String name) { 1104 return getResourceObject(name); 1105 } 1106 1107 1113 public InputStream getResourceAsStream(String name) { 1114 Resource resource = getResourceObject(name); 1115 if (resource != null) { 1116 try { 1117 return resource.getInputStream(); 1118 } 1119 catch (IOException except) { 1120 logReadFailure(name, except); 1121 return null; } 1123 } 1124 return null; 1125 } 1126 1127 1133 public byte[] getResourceAsByteArray(String name) { 1134 Resource resource = getResourceObject(name); 1135 if (resource != null) { 1136 try { 1137 return resource.getBytes(); 1138 } 1139 catch (IOException except) { 1140 logReadFailure(name, except); 1141 return null; } 1143 } 1144 return null; 1145 } 1146 1147 1157 public boolean shouldReload() { 1158 Enumeration classes = loadedClasses.elements(); 1159 if (loggingEnabled) { 1160 logChannel.write(logLevel, "Checking for out-of-date classes"); 1161 } 1162 while (classes.hasMoreElements()) { 1163 boolean isModified; 1164 try { 1165 isModified = ( (ClassResource) classes.nextElement()).getResource().hasBeenModified(); 1166 } 1167 catch (FileNotFoundException except) { 1168 if (loggingEnabled) { 1169 logChannel.write(logLevel, "File for loaded class can no longer be accessed", except); 1170 } 1171 isModified = true; 1172 } 1173 if (isModified) { 1174 if (loggingEnabled) { 1175 logChannel.write(logLevel, "Loaded classes have been modified"); 1176 } 1177 return true; 1178 } 1179 } 1180 if (loggingEnabled) { 1181 logChannel.write(logLevel, "Loaded classes have not been modified"); 1182 } 1183 return false; 1184 } 1185} 1186
| Popular Tags
|