0001: /*
0002: * Enhydra Java Application Server Project
0003: *
0004: * The contents of this file are subject to the Enhydra Public License
0005: * Version 1.1 (the "License"); you may not use this file except in
0006: * compliance with the License. You may obtain a copy of the License on
0007: * the Enhydra web site ( http://www.enhydra.org/ ).
0008: *
0009: * Software distributed under the License is distributed on an "AS IS"
0010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
0011: * the License for the specific terms governing rights and limitations
0012: * under the License.
0013: *
0014: * The Initial Developer of the Enhydra Application Server is Lutris
0015: * Technologies, Inc. The Enhydra Application Server and portions created
0016: * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
0017: * All Rights Reserved.
0018: *
0019: * Contributor(s):
0020: *
0021: * $Id: MultiClassLoader.java,v 1.3 2007-10-19 10:35:46 sinisa Exp $
0022: *
0023: * formatted with JxBeauty (c) johann.langhofer@nextra.at
0024: */
0025:
0026: package com.lutris.classloader;
0027:
0028: import java.io.ByteArrayOutputStream;
0029: import java.io.File;
0030: import java.io.FileNotFoundException;
0031: import java.io.IOException;
0032: import java.io.InputStream;
0033: import java.net.MalformedURLException;
0034: import java.net.URL;
0035: import java.util.Enumeration;
0036: import java.util.Hashtable;
0037: import java.util.StringTokenizer;
0038: import java.util.Vector;
0039:
0040: import com.lutris.logging.LogChannel;
0041:
0042: /**
0043: * <P><B>Summary:</B>
0044: * <P>A class loader that can load classes from
0045: * class files and zip files residing in a specified class path.
0046: *
0047: * <P>This class loader can also load resources that reside on the
0048: * class path and return them as is, as input streams, or as byte arrays.
0049: *
0050: * <P><B>Features:</B>
0051: * <UL>
0052: * <LI>Loads classes from class files and zip files.
0053: * <LI>Finds resources and can return them as is, as input streams and as byte arrays.
0054: * <LI>Loads classes and resources using the specified class path. Class
0055: * path entries can be URLs, directories, zip files or jar files.
0056: * <P> A secondary classloader can be specified which is used to locate
0057: * the class if its not found by this classloader.
0058: * <LI> A filter object can be supplied to determine if the current class
0059: * loader should load the class or it should be passed to the parent or
0060: * secondary class loader.
0061: * </UL>
0062: *
0063: * <H3>Class Path</H3>
0064: *
0065: * <P>If classes are to be loaded from a class path, it must be set by the
0066: * <CODE>setClassPath</CODE> or <CODE>addClassPath</CODE> methods or by
0067: * the constructor prior to calling <CODE>loadClass</CODE>. If the class
0068: * path is not set, the class will be loaded with the system class loader.
0069: *
0070: * <P>The class path can consist of directories, files, and/or URLs.
0071: * <P>Example valid class path entries are:
0072: * <PRE>
0073: * <EM>Files and directories on the local file system</EM>
0074: * ../../java/classes
0075: * /users/kristen/java/classes
0076: * /users/kristen/java/classes/
0077: * /users/kristen/java/zipfiles/MyClasses.zip
0078: * /users/kristen/java/jarfiles/MyClasses.jar
0079: * file:///users/kristen/java/classes
0080: * file://localhost/users/kristen/java/classes
0081: * <BR>
0082: * <EM>Files and directories on a remote file system
0083: * (must be in URL format)</EM>
0084: * ftp://www.foo.com/pub/java/classes
0085: * file://www.foo.com/pub/java/classes/
0086: * http://www.foo.com/web/java/classes/
0087: * file://www.foo.com:8080/pub/java/zipfiles/MyClasses.zip
0088: * http://www.foo.com:8080/web/java/jarfiles/MyClasses.jar
0089: * </PRE>
0090: *
0091: * <P>Note that the location of the entry includes the protocol, the host name,
0092: * and the port while the file name is everything else. For example,
0093: *
0094: * <PRE>
0095: * http://www.foo.com:8080/web/java/jarfiles/MyClasses.jar
0096: * </PRE>
0097: * has the form [location][name] or
0098: * <PRE>
0099: * [http://www.foo.com:8080/][/web/java/jarfiles/MyClasses.jar]
0100: * </PRE>
0101: * so the location is "http://www.foo.com:8080/" and the name is
0102: * "/web/java/jarfiles/MyClasses.jar".
0103: *
0104: * <P>Note that the two references
0105: * <PRE>
0106: * /users/kristen/java/classes/
0107: * file:///users/kristen/java/classes/
0108: * </PRE>
0109: * represent the same directory on a Unix machine, and
0110: * <PRE>
0111: * C|/windows/java/classes/
0112: * file:///C|/windows/java/classes/
0113: * </PRE>
0114: * are equivalent directories on a Windows box.
0115: *
0116: * <P>But the two references
0117: * <PRE>
0118: * /users/kristen/java/classes/
0119: * file://monet.lutris.com/users/kristen/java/classes/
0120: * </PRE>
0121: * are not equivalent even if the directory
0122: * <EM>/users/kristen/java/classes/</EM> lives
0123: * on the machine named <EM>monet.lutris.com</EM> and all development
0124: * is on this machine. Why? Because the web (browser?) protocol is different
0125: * for URLs with host information and those without. If no host is
0126: * specified, the file is assumed to be on the local machine and the
0127: * path is determined from the <EM>ROOT</EM> of the machine. If the
0128: * host is specified, then the <EM>ftp protocol</EM> is used and the path
0129: * is determined from the <EM>ftp ROOT</EM> (e.g. /users/ftp/) rather
0130: * than the machine's ROOT. Thus, on a machine that support's anonymous
0131: * ftp, the following two directories are the same:
0132: * <PRE>
0133: * /users/ftp/pub/classes/
0134: * file://picasso.lutris.com/pub/classes/
0135: * </PRE>
0136: * assuming the development is being done on <EM>picasso.lutris.com</EM>.
0137: *
0138: * <H3>System Class Path</H3>
0139: *
0140: * <P>The system class path is the system-dependent path of directories
0141: * and files (e.g. CLASSPATH on Unix and Windows) used by the system
0142: * class loader to load classes. This class path is usually configured
0143: * prior to executing a Java program but can be dynamically configured
0144: * during runtime if desired. If you want to use the system class path
0145: * for this class loader, the convenience method <CODE>getSystemClassPath</CODE>
0146: * has been provided.
0147: *
0148: * <P>Valid system class path entries are
0149: * directories and zip files, specified by absolute path or relative path
0150: * on the system. Any valid system class path entry is also valid
0151: * for this class loader.
0152: *
0153: * <H3>Example</H3>
0154: *
0155: * <P>Here is an example of how to use this class loader:
0156: * <PRE>
0157: * MultiClassLoader loader = new MultiClassLoader();
0158: * loader.setClassPath("/web/java/lutris.jar");
0159: * loader.addClassPath("/users/kristen/java/");
0160: * loader.addClassPath("/usr/local/lib/graphics.zip");
0161: * try {
0162: * Class c = loader.loadClass("com.lutris.util.MyClass");
0163: * System.out.println("My loader is " + c.getClassLoader());
0164: * Object o = (Object) c.newInstance();
0165: * System.out.println("My class is " + o.getClass());
0166: * } catch (ClassNotFoundException e) {
0167: * Throwable t = new Throwable();
0168: * t.printStackTrace();
0169: * }
0170: * </PRE>
0171: *
0172: * <P>
0173: * <B>Warning:</B> This class loader is not yet fully compliant with Java 1.2. It
0174: * maybe used on 1.2, but not all features are available. The <em>parent</em> loader,
0175: * <em>secondary</em> loader, and <em>filter</em> may change in a future release without
0176: * maintaining compatibility.
0177: *
0178: * <!--
0179: * FIXME:
0180: * This class needs revisited for JDK 1.2, including adding security
0181: * managers. Should implement the find* methods, but probably
0182: * must have its own loadClass. Need to have resource loading
0183: * follow same paradigm as class loading.
0184: *
0185: * -->
0186: *
0187: * @author Kristen Pol, Lutris Technologies
0188: * @version $Revision : 1.0 $
0189: * @see java.lang.ClassLoader
0190: * @see com.lutris.classloader.Resource
0191: * @see java.net.URL
0192: */
0193: public class MultiClassLoader extends ClassLoader {
0194:
0195: /**
0196: * A filter interface, used in deciding if a class should be loaded
0197: * by this class loader.
0198: */
0199: public interface ClassFilter {
0200: /**
0201: * Value returned to indicate that we don't care, other filters
0202: * maybe checked.
0203: */
0204: public static final int NORMAL_LOAD = 1;
0205: /**
0206: * Value returned to indicate that the class should not be loaded
0207: * by this class loader. Normal delation/secondary class loader
0208: * checks will be done.
0209: */
0210: public static final int DONT_LOAD = 2;
0211: /**
0212: * Value returned to indicate that the class can be loaded
0213: * by this class loader. If it is not loaded, it will be
0214: * passed to the secondary class loader. The delegate
0215: * is not check, since it is check before this class loader.
0216: */
0217: public static final int CAN_LOAD = 3;
0218: /**
0219: * Value returned to indicate that the class must loaded
0220: * by this class loader. If it is not loaded, not other
0221: * class loader will be checked.
0222: */
0223: public static final int MUST_LOAD = 4;
0224:
0225: /**
0226: * Check if a class should be loaded by this class loader.
0227: * @param className the class being loaded.
0228: * @return One of NORMAL_LOAD, DONT_LOAD, CAN_LOAD, or MUST_LOAD.
0229: */
0230: public int loadCheck(String className);
0231: }
0232:
0233: /**
0234: * Information kept about a loaded class.
0235: */
0236: public static class ClassResource {
0237:
0238: /**
0239: * Class object
0240: */
0241: private Class classObj;
0242:
0243: /**
0244: * Resource were the class was obtained.
0245: */
0246: private Resource resource;
0247:
0248: /**
0249: * Constructor.
0250: */
0251: public ClassResource(Class classObj, Resource resource) {
0252: this .classObj = classObj;
0253: this .resource = resource;
0254: }
0255:
0256: /**
0257: * Get the class object.
0258: */
0259: public Class getClassObj() {
0260: return classObj;
0261: }
0262:
0263: /**
0264: * Get the resource. Maybe null if the class was not loaded by
0265: * a MultiClassLoader.
0266: */
0267: public Resource getResource() {
0268: return resource;
0269: }
0270: }
0271:
0272: /**
0273: * The ClassResource objects for classes loaded by this classloader
0274: * (but not those handled by the parent).
0275: */
0276: private Hashtable loadedClasses = new Hashtable();
0277:
0278: /**
0279: * The class path to check when loading classes.
0280: */
0281: private ClassPath classPath;
0282:
0283: /**
0284: * Log level symbolic name
0285: */
0286: public static final String LOG_LEVEL = "CLASSLOAD";
0287:
0288: /**
0289: * Is logging enabled?
0290: */
0291: private boolean loggingEnabled = false;
0292:
0293: /**
0294: * Log channel to write messages to; maybe null.
0295: */
0296: private LogChannel logChannel;
0297:
0298: /**
0299: * Numeric log level number for LOG_LEVEL string
0300: */
0301: private int logLevel;
0302:
0303: /**
0304: * If any class filters are supplied, this is a non-null list of
0305: * filters to check.
0306: */
0307: private Vector filters = null;
0308:
0309: /**
0310: * Parent class loader, or null if there isn't one.
0311: */
0312: private ClassLoader parentClassLoader;
0313:
0314: /**
0315: * Secondary class loader, or null if there isn't one.
0316: */
0317: private ClassLoader secondaryClassLoader;
0318:
0319: /**
0320: * Flag which indicates that using of secondary ClassLoader will be forced.
0321: * If this flag is true the MultiClassLoader is used as utility wrapper class
0322: * for passed secondary ClassLoader.
0323: */
0324: private boolean forceSecondaryLoader = false; //vr 28.06.2004
0325:
0326: /**
0327: * Flag which indicates using of MultiClassLoader with AutoReload option.
0328: */
0329: private boolean isAutoReload = false; //vr 30.06.2004
0330:
0331: /**
0332: * Constructs class loader with no initial class path and a
0333: * specified parent class loader.
0334: * @param parent The parent class loader for delegation,
0335: * or null if no parent is defined.
0336: * @param secondary The secondary class loader.
0337: * Use <code>getSysClassLoader</code> to get the system class loader
0338: * to specify as the secondary.
0339: * @param loadLogChannel The log channel, maybe null.
0340: * @see getSysClassLoader
0341: */
0342: public MultiClassLoader(ClassLoader parent, ClassLoader secondary,
0343: LogChannel loadLogChannel) {
0344: classPath = new ClassPath(loadLogChannel);
0345: // v. puskas, 10.03.2003 native compile changes;
0346: // parentClassLoader = parent;
0347: parentClassLoader = (null != parent) ? parent
0348: : getSystemClassLoader();
0349: secondaryClassLoader = secondary;
0350: logChannel = loadLogChannel;
0351: if (logChannel != null) {
0352: logLevel = logChannel.getLevel(LOG_LEVEL);
0353: loggingEnabled = logChannel.isEnabled(logLevel);
0354: }
0355:
0356: }
0357:
0358: /**
0359: * Constructs class loader with no initial class path and the
0360: * system class loader as the secondary.
0361: * @param loadLogChannel The log channel, maybe null.
0362: */
0363: public MultiClassLoader(LogChannel loadLogChannel) {
0364: this (null, getSystemClassLoader(), loadLogChannel);
0365: }
0366:
0367: /**
0368: * Constructs class loader with specified class path. The parameter is
0369: * assumed to be either a directory, URL, or zip file.
0370: * @param path The class path represented by a String.
0371: * @param loadLogChannel The log channel, maybe null.
0372: */
0373: public MultiClassLoader(String path, LogChannel loadLogChannel) {
0374: this (new String[] { path }, loadLogChannel);
0375: }
0376:
0377: /**
0378: * Constructs class loader with specified class path. The parameter is
0379: * assumed to be an array of directories, URLs, and/or zip files.
0380: * @param path The class path represented by a String array.
0381: * @param loadLogChannel The log channel, maybe null.
0382: */
0383: public MultiClassLoader(String[] path, LogChannel loadLogChannel) {
0384: this (loadLogChannel);
0385: setClassPath(path);
0386: }
0387:
0388: /**
0389: * Constructs class loader with specified class path. The parameter is
0390: * assumed to be either a zip file or directory.
0391: * @param path The class path represented by a File.
0392: * @param loadLogChannel The log channel, maybe null.
0393: */
0394: public MultiClassLoader(File path, LogChannel loadLogChannel) {
0395: this (new File[] { path }, loadLogChannel);
0396: }
0397:
0398: /**
0399: * Constructs class loader with specified class path. The parameter is
0400: * assumed to be an array of zip files and/or directories.
0401: * @param path The class path represented by a File array.
0402: * @param loadLogChannel The log channel, maybe null.
0403: */
0404: public MultiClassLoader(File[] path, LogChannel loadLogChannel) {
0405: this (loadLogChannel);
0406: setClassPath(path);
0407: }
0408:
0409: /**
0410: * Constructs class loader with specified class path. The parameter is
0411: * represent a directory or zip file on the local machine or a
0412: * remote machine.
0413: * @param path The class path represented by a URL.
0414: * @param loadLogChannel The log channel, maybe null.
0415: */
0416: public MultiClassLoader(URL path, LogChannel loadLogChannel) {
0417: this (new URL[] { path }, loadLogChannel);
0418: }
0419:
0420: /**
0421: * Constructs class loader with specified class path. The parameter is
0422: * represent directories and/or zip files on the local machine and/or
0423: * on remote machines.
0424: * @param path The class path represented by a URL array.
0425: * @param loadLogChannel The log channel, maybe null.
0426: */
0427: public MultiClassLoader(URL[] path, LogChannel loadLogChannel) {
0428: this (loadLogChannel);
0429: setClassPath(path);
0430: }
0431:
0432: /**
0433: * Sets flag to indicate forsing of Secondary ClassLoader usage or not.
0434: * @param force true when the MultiClassLoader is used as utility wrapper
0435: * class for passed secondary ClassLoader. All resource loads will be forced
0436: * via passed Secondary ClassLoader. If parameter is set to false, then the
0437: * MultyClassLoader will work in standard maner.
0438: */
0439: public void forceSecondaryLoader(boolean force) { //vr 28.06.2004
0440: this .forceSecondaryLoader = force;
0441: }
0442:
0443: /**
0444: * Sets flag to enable Auto Reloading possibility when Secondary ClassLoader
0445: * usage is forced (refer to forceSecondaryLoader() method for more help).
0446: * This flag has no efect when MultiClassLoader is used in standard maner (as
0447: * realy ClassLoader, not only as wrapper class for external ClassLoader)
0448: * @param enable true when the AutoReloading should be used.
0449: */
0450: public void enableAutoReloadForSecLoader(boolean enable) { //vr 30.06.2004
0451: this .isAutoReload = enable;
0452: }
0453:
0454: /**
0455: * Removes stored data about already loaded classes
0456: */
0457: public void removeLoadedClass() { //vr 28.06.2004
0458: this .loadedClasses = new Hashtable();
0459: }
0460:
0461: /**
0462: * Sets class loader with specified class path. The parameter is
0463: * assumed to be either a directory, URL, or zip file.
0464: * @param path The class path to be used when loading classes.
0465: */
0466: public void setClassPath(String path) {
0467: setClassPath(new String[] { path });
0468: }
0469:
0470: /**
0471: * Sets class loader with specified class path. The parameter is
0472: * assumed to be an array of directories, URLs, and/or zip files.
0473: * @param path The class path to be used when loading classes.
0474: */
0475: public synchronized void setClassPath(String[] path) {
0476: classPath.set(path);
0477: }
0478:
0479: /**
0480: * Sets class loader with specified class path. The parameter is
0481: * assumed to be either a zip file or directory.
0482: * @param path The class path to be used when loading classes.
0483: */
0484: public void setClassPath(File path) {
0485: setClassPath(new File[] { path });
0486: }
0487:
0488: /**
0489: * Sets class loader with specified class path. The parameter is
0490: * assumed to be an array of zip files and/or directories.
0491: * @param path The class path to be used when loading classes.
0492: */
0493: public synchronized void setClassPath(File[] path) {
0494: classPath.set(path);
0495: }
0496:
0497: /**
0498: * Sets class loader with specified class path. The parameter is
0499: * represent a directory or zip file on the local machine or a
0500: * remote machine.
0501: * @param path The class path to be used when loading classes.
0502: */
0503: public void setClassPath(URL path) {
0504: setClassPath(new URL[] { path });
0505: }
0506:
0507: /**
0508: * Sets class loader with specified class path. The parameter is
0509: * represent directories and/or zip files on the local machine and/or
0510: * on remote machines.
0511: * @param path The class path to be used when loading classes.
0512: */
0513: public synchronized void setClassPath(URL[] path) {
0514: classPath.set(path);
0515: }
0516:
0517: /**
0518: * Adds specified class path to beginning of existing path.
0519: * The parameter is
0520: * assumed to be either a directory, URL, or zip file.
0521: * @param path The class path to be added to current class path.
0522: */
0523: public void addClassPath(String path) {
0524: addClassPath(new String[] { path });
0525: }
0526:
0527: /**
0528: * Adds specified class path to beginning of existing path.
0529: * The parameter is
0530: * assumed to be an array of directories, URLs, and/or zip files.
0531: * @param path The class path to be added to current class path.
0532: */
0533: public synchronized void addClassPath(String[] path) {
0534: classPath.add(path);
0535: }
0536:
0537: /**
0538: * Adds specified class path to beginning of existing path.
0539: * The parameter is assumed to be either a zip file or directory.
0540: * @param path The class path to be added to current class path.
0541: */
0542: public void addClassPath(File path) {
0543: addClassPath(new File[] { path });
0544: }
0545:
0546: /**
0547: * Adds specified class path to beginning of existing path.
0548: * The parameter is
0549: * assumed to be an array of zip files and/or directories.
0550: * @param path The class path to be added to current class path.
0551: */
0552: public synchronized void addClassPath(File[] path) {
0553: classPath.add(path);
0554: }
0555:
0556: /**
0557: * Adds specified class path to beginning of existing path.
0558: * The parameter is
0559: * represent a directory or zip file on the local machine or a
0560: * remote machine.
0561: * @param path The class path to be added to current class path.
0562: */
0563: public void addClassPath(URL path) {
0564: addClassPath(new URL[] { path });
0565: }
0566:
0567: /**
0568: * Adds specified class path to beginning of existing path.
0569: * The parameter is
0570: * represent directories and/or zip files on the local machine and/or
0571: * on remote machines.
0572: * @param path The class path to be added to current class path.
0573: */
0574: public synchronized void addClassPath(URL[] path) {
0575: classPath.add(path);
0576: }
0577:
0578: /**
0579: * Clears class path entries.
0580: * @see #setClassPath
0581: */
0582: public synchronized void clearClassPath() {
0583: classPath.clear();
0584: }
0585:
0586: /**
0587: * Gets class path for class loader defined previously by constructor and
0588: * <CODE>setClassPath</CODE>/<CODE>addClassPath</CODE> methods.
0589: * @return the class path represented by an <CODE>Enumeration</CODE>
0590: * of URL objects.
0591: * @see #setClassPath
0592: * @see #addClassPath
0593: * <!-- FIXME: should really return a format that can be passed
0594: * to another class loader, like an array -->
0595: */
0596: public URL[] getClassPath() {
0597: int len = classPath.getLength();
0598: URL[] urlPath = new URL[len];
0599: Enumeration cpeEnum = classPath.getPath();
0600: for (int i = 0; i < len; i++) {
0601: ClassPathEntry cpe = (ClassPathEntry) cpeEnum.nextElement();
0602: urlPath[i] = cpe.getURL();
0603: }
0604: return urlPath;
0605: }
0606:
0607: /**
0608: * Parse a class-path string using the system path separator.
0609: */
0610: public static String[] parseClassPath(String path) {
0611: String systemSeparator = System.getProperty("path.separator");
0612: if (systemSeparator == null) {
0613: throw new NullPointerException(
0614: "path.separator property not defined");
0615: }
0616: StringTokenizer tokenizer = new StringTokenizer(path,
0617: systemSeparator);
0618: String[] parsed = new String[tokenizer.countTokens()];
0619: for (int i = 0; tokenizer.hasMoreTokens(); i++) {
0620: parsed[i] = tokenizer.nextToken();
0621: }
0622: return parsed;
0623: }
0624:
0625: /**
0626: * Gets class path from system.
0627: *
0628: * @return the system class path represented by an
0629: * array of URL objects.
0630: */
0631: public static URL[] getSystemClassPath() {
0632: // Parse system class path into its components
0633: String systemClassPath = System.getProperty("java.class.path");
0634: if (systemClassPath == null) {
0635: systemClassPath = "";
0636: }
0637: String[] parsedPath = parseClassPath(systemClassPath);
0638: // Convert to URLs, dropping invalid entries
0639: Vector urlVector = new Vector(parsedPath.length);
0640: for (int i = 0; i < parsedPath.length; i++) {
0641: try {
0642: urlVector
0643: .addElement(new URL("file", "", parsedPath[i]));
0644: } catch (MalformedURLException mue) {
0645: // Do not add this entry
0646: }
0647: }
0648: URL[] urlArray = new URL[urlVector.size()];
0649: urlVector.copyInto(urlArray);
0650: return urlArray;
0651: }
0652:
0653: /**
0654: * Set the parent class loader for delegation.
0655: */
0656: public void setParent(ClassLoader parent) {
0657: parentClassLoader = parent;
0658: }
0659:
0660: /**
0661: * Get the secondary class loader.
0662: */
0663: public ClassLoader getSecondary() {
0664: return secondaryClassLoader;
0665: }
0666:
0667: /**
0668: * Set the secondary class loader.
0669: */
0670: public void setSecondary(ClassLoader secondary) {
0671: secondaryClassLoader = secondary;
0672: }
0673:
0674: /**
0675: * Get the log channel associated this class loader.
0676: */
0677: public LogChannel getLogChannel() {
0678: return logChannel;
0679: }
0680:
0681: /**
0682: * Add a filter to the list of filters that check if a class maybe
0683: * loaded by this class loader.
0684: */
0685: public synchronized void addClassFilter(ClassFilter filter) {
0686: if (filters == null) {
0687: filters = new Vector();
0688: }
0689: filters.addElement(filter);
0690: }
0691:
0692: /**
0693: * Check class filters to determine if a class should be loaded by
0694: * this class loader. This also handles check an implicit filter
0695: * for java.lang.* classes, which must go to the main class loader.
0696: * @return One of NORMAL_LOAD, DONT_LOAD, CAN_LOAD, or MUST_LOAD.
0697: */
0698: private int checkFilters(String className) {
0699: int restrict = ClassFilter.NORMAL_LOAD;
0700: if (className.startsWith("java.lang.")) {
0701: restrict = ClassFilter.DONT_LOAD;
0702: } else if (filters != null) {
0703: int numFilters = filters.size();
0704: for (int idx = 0; idx < numFilters; idx++) {
0705: restrict = ((ClassFilter) filters.elementAt(idx))
0706: .loadCheck(className);
0707: if (restrict != ClassFilter.NORMAL_LOAD) {
0708: break;
0709: }
0710: }
0711: }
0712: // Log restriction, if any.
0713: if ((restrict != ClassFilter.NORMAL_LOAD) && loggingEnabled) {
0714: String msg = "";
0715: switch (restrict) {
0716: case ClassFilter.DONT_LOAD:
0717: msg = "Filter disallows loading by this classloader: ";
0718: break;
0719: case ClassFilter.CAN_LOAD:
0720: msg = "Filter allows loading by this classloader: ";
0721: break;
0722: case ClassFilter.MUST_LOAD:
0723: msg = "Filter requires loading by this classloader: ";
0724: break;
0725: }
0726: if (loggingEnabled) {
0727: logChannel.write(logLevel, msg + className);
0728: }
0729: }
0730: return restrict;
0731: }
0732:
0733: /**
0734: * Check the table of loaded classes to determine if a class is already
0735: * loaded. Purposely not synchronized, allowing a quick check for a class
0736: * being loaded. A second check, which higher level synchronization is
0737: * required if the class is not found. This function does logs a message
0738: * when a class is found.
0739: * @param className The class to look up.
0740: * @return The ClassResource object for the class, or null if not found.
0741: */
0742: private ClassResource checkForLoadedClass(String className) {
0743: ClassResource cr = (ClassResource) loadedClasses.get(className);
0744: if ((cr != null) && loggingEnabled) {
0745: logChannel.write(logLevel, "loadClass already loaded: "
0746: + className);
0747: }
0748: return cr;
0749: }
0750:
0751: //FIXME: method with resolve parameter should be protected (see base class).
0752: /**
0753: * Loads and, optionally, resolves the specified class. If the class
0754: * needs to be instantiated, the class must be resolved. If only the
0755: * existence of the class needs verification, class resolution is
0756: * unnecessary.
0757: *
0758: * Calling <CODE>loadClass(String className)</CODE> is equivalent to calling
0759: * this method with <CODE>resolve</CODE> set to <CODE>true</CODE>.
0760: *
0761: * Purposely not synchronized. If class is not found in loadedClasses table, then
0762: * doLoadClass will do the operation in a synchronized manner.
0763: *
0764: * @param className The name of the class to be loaded, e.g.
0765: * "com.lutris.util.Table".
0766: * @param resolve Set to <CODE>true</CODE> for class resolution,
0767: * <CODE>false</CODE> for no resolution.
0768: * @return the loaded Class.
0769: * @exception ClassNotFoundException if the class could not be load.
0770: * @see #setClassPath
0771: * @see #addClassPath
0772: */
0773: public Class loadClass(String className, boolean resolve)
0774: throws ClassNotFoundException {
0775: return loadClassResource(className, resolve).getClassObj();
0776: }
0777:
0778: public Class loadClass(String className)
0779: throws ClassNotFoundException {
0780: if (this .forceSecondaryLoader)
0781: return loadClassResource(className, false).getClassObj();
0782: else
0783: return super .loadClass(className);
0784: }
0785:
0786: /**
0787: * Log a class load failer.
0788: */
0789: private void logClassLoadFailure(String className, Throwable except) {
0790: if (loggingEnabled) {
0791: logChannel.write(logLevel, "load of class failed: "
0792: + className, except);
0793: }
0794: }
0795:
0796: /**
0797: * Log a resource read failer.
0798: */
0799: private void logReadFailure(String name, Throwable except) {
0800: if (loggingEnabled) {
0801: logChannel.write(logLevel, "read of resource failed: "
0802: + name, except);
0803: }
0804: }
0805:
0806: /**
0807: * Loads and, optionally, resolves the specified class, returning the
0808: * <CODE>ClassResource</CODE> object.
0809: *
0810: * @param className The name of the class to load.
0811: * @param resolve Set to <CODE>true</CODE> for class resolution,
0812: * <CODE>false</CODE> for no resolution.
0813: * @return Object containing class and resource.
0814: * @exception ClassNotFoundException if this loader and the system loader
0815: * can not find or successfully load the class.
0816: * @see #setClassPath
0817: * @see #addClassPath
0818: * @see #loadClass
0819: */
0820: private ClassResource loadClassResource(String className,
0821: boolean resolve) throws ClassNotFoundException {
0822: if (loggingEnabled) {
0823: logChannel.write(logLevel, "loadClass: " + className);
0824: }
0825: try {
0826: ClassResource cr = checkForLoadedClass(className);
0827: if (cr == null) {
0828: cr = doLoadClass(className);
0829: if (resolve) {
0830: resolveClass(cr.getClassObj());
0831: }
0832: }
0833: return cr;
0834: } catch (ClassNotFoundException except) {
0835: logClassLoadFailure(className, except);
0836: throw except;
0837: } catch (IOException except) {
0838: logClassLoadFailure(className, except);
0839: throw new ClassNotFoundException(except.getClass()
0840: .getName()
0841: + ": " + except.getMessage());
0842: } catch (RuntimeException except) {
0843: logClassLoadFailure(className, except);
0844: throw except;
0845: } catch (Error except) {
0846: logClassLoadFailure(className, except);
0847: throw except;
0848: }
0849: }
0850:
0851: /**
0852: * Get the resource for a class loaded by this class loader.
0853: */
0854: public Resource getClassResource(String className)
0855: throws ClassNotFoundException {
0856: ClassResource cr = checkForLoadedClass(className);
0857: if (cr == null) {
0858: throw new ClassNotFoundException("Class \"" + className
0859: + "\" is not loaded by this class loader");
0860: }
0861: return cr.getResource();
0862: }
0863:
0864: /**
0865: * Load a class with the parent or secondary class loader.
0866: * Must already be synchronized
0867: */
0868: private ClassResource loadClassOther(String className,
0869: ClassLoader loader, String loaderName) {
0870: ClassResource cr = null;
0871:
0872: if (loggingEnabled) {
0873: logChannel.write(logLevel, "checking " + loaderName
0874: + " class loader: " + className);
0875: }
0876: try {
0877: if (loader instanceof MultiClassLoader) {
0878: cr = ((MultiClassLoader) loader).loadClassResource(
0879: className, false);
0880: } else {
0881: Class c = loader.loadClass(className);
0882: cr = new ClassResource(c, null);
0883: }
0884: if (loggingEnabled) {
0885: logChannel.write(logLevel, "class loaded by: "
0886: + loaderName);
0887: }
0888: } catch (ClassNotFoundException except) {
0889: // Just log and return null
0890: if (loggingEnabled) {
0891: logChannel.write(logLevel, "class not loaded by "
0892: + loaderName + ": "
0893: + except.getClass().getName() + ": "
0894: + except.getMessage());
0895: }
0896: }
0897: return cr;
0898: }
0899:
0900: /**
0901: * Load a class with this class loader. Must already be
0902: * synchronized
0903: */
0904: private ClassResource loadClassHere(String className)
0905: throws IOException {
0906: ClassResource cr = null;
0907:
0908: if (loggingEnabled) {
0909: logChannel.write(logLevel, "checking MultiClassLoader: "
0910: + className);
0911: }
0912: String fileName = className.replace('.', '/').concat(".class");
0913: Resource resource = getResourceObject(fileName);
0914:
0915: if (resource != null) {
0916:
0917: Class c = null;
0918: try {
0919: // AutoReload with secondary loader
0920: if (isAutoReload && forceSecondaryLoader) {
0921: InputStream is = null;
0922: try {
0923: is = this .secondaryClassLoader.getResource(
0924: fileName).openConnection()
0925: .getInputStream();
0926: c = getClassFromStream(className, is);
0927: } catch (Throwable th) {
0928: try {
0929: is = this .secondaryClassLoader
0930: .getResourceAsStream(fileName);
0931: c = getClassFromStream(className, is);
0932: } catch (Throwable th1) {
0933: c = Class.forName(className, true,
0934: this .secondaryClassLoader);
0935: }
0936: }
0937:
0938: }
0939: // loading with secondary loader
0940: else if (forceSecondaryLoader) {
0941: c = Class.forName(className, true,
0942: this .secondaryClassLoader);
0943: }
0944: // standard use of MultiClassLoader
0945: else
0946: c = Class.forName(className);
0947: } catch (ClassNotFoundException e) {
0948: try {
0949: // second try to search the class among Dll-mapped classes
0950: // this requires the call Class.forName with system class
0951: // loader
0952: c = Class.forName(className, true, ClassLoader
0953: .getSystemClassLoader());
0954: } catch (ClassNotFoundException e1) {
0955: // if there is no precompiled class, try to compile it with JET JIT
0956: // NOTE! this will success only if -Djet.jit property is defined
0957: byte[] classBytes = resource.getBytes();
0958: c = defineClass(className, classBytes, 0,
0959: classBytes.length);
0960: }
0961: }
0962:
0963: cr = new ClassResource(c, resource);
0964: loadedClasses.put(className, cr);
0965:
0966: if (loggingEnabled) {
0967: logChannel.write(logLevel,
0968: "class loaded by MultiClassLoader: "
0969: + className);
0970: }
0971: }
0972: if ((cr == null) && loggingEnabled) {
0973: logChannel.write(logLevel,
0974: "class not loaded by MultiClassLoader: "
0975: + className);
0976: }
0977: return cr;
0978: }
0979:
0980: /**
0981: * Returns Class object for defined class name created by bytes obtained from
0982: * InputStream. Note that InputStrim will be closed in this method after it
0983: * is read.
0984: * @param className full name of class which has to be created
0985: * @param is InputStream which is used as origin of bytes used for class
0986: * generation.
0987: * @return created Class object
0988: * @throws IOException
0989: */
0990: private Class getClassFromStream(String className, InputStream is)
0991: throws IOException {
0992: ByteArrayOutputStream baos = new ByteArrayOutputStream();
0993: byte[] buf = new byte[1024];
0994: int i = is.read(buf);
0995: while (i == 1024) {
0996: baos.write(buf);
0997: i = is.read(buf);
0998: }
0999: if (i != -1)
1000: baos.write(buf, 0, i);
1001:
1002: buf = baos.toByteArray();
1003: baos.close();
1004: is.close();
1005:
1006: return defineClass(className, buf, 0, buf.length);
1007: }
1008:
1009: /**
1010: * Loads specified class. If possible, the class will loaded from the
1011: * class path defined previously with <CODE>setClassPath</CODE>,
1012: * <CODE>addClassPath</CODE>, and/or the constructor. Otherwise, the
1013: * class will be passed off to the parent class loader. Classes from
1014: * the java.* packages cannot be loaded by this class loader so will
1015: * be passed off to the parent class loader.
1016: * @param className The name of the class to be loaded, e.g.
1017: * "com.lutris.util.Table".
1018: * @return Object containing class and resource.
1019: * @exception ClassNotFoundException if this loader and the system loader
1020: * can not find or successfully load the class.
1021: * @see #setClassPath
1022: * @see #addClassPath
1023: */
1024: private synchronized ClassResource doLoadClass(String className)
1025: throws ClassNotFoundException, IOException {
1026: // Must check loaded class table again, as we are now synchronized.
1027: ClassResource cr = checkForLoadedClass(className);
1028: if (cr != null) {
1029: return cr;
1030: }
1031: // Get filter restriction to use (includes check for java.lang.*).
1032: int filterRestrict = checkFilters(className);
1033:
1034: // Delegate if parent exists and filter allows.
1035: if ((parentClassLoader != null)
1036: && (filterRestrict != ClassFilter.CAN_LOAD)
1037: && (filterRestrict != ClassFilter.MUST_LOAD)) {
1038: cr = loadClassOther(className, parentClassLoader, "parent");
1039: if (cr != null) {
1040: return cr;
1041: }
1042: }
1043:
1044: // Try loading with this class loader if filter allows.
1045: if ((filterRestrict == ClassFilter.NORMAL_LOAD)
1046: || (filterRestrict == ClassFilter.CAN_LOAD)
1047: || (filterRestrict == ClassFilter.MUST_LOAD)) {
1048: cr = loadClassHere(className);
1049: if (cr != null) {
1050: return cr;
1051: }
1052: }
1053:
1054: // Try secondary class loader.
1055: if ((secondaryClassLoader != null)
1056: && (filterRestrict != ClassFilter.MUST_LOAD)) {
1057: cr = loadClassOther(className, secondaryClassLoader,
1058: "secondary");
1059: if (cr != null) {
1060: return cr;
1061: }
1062: }
1063: throw new ClassNotFoundException(className);
1064: }
1065:
1066: /**
1067: * Gets specified resource as URL. Doing a getContent() on the URL may
1068: * return an Image, an AudioClip, or an InputStream.
1069: * @param name The name of the resource.
1070: * @return the resource represented by a URL, or null if not found.
1071: */
1072: public URL getResource(String name) {
1073: Resource resource = getResourceObject(name);
1074: if (resource != null) {
1075: ClassPathEntry location = resource.getLocation();
1076: try {
1077: return new URL(location.getURL() + resource.getName());
1078: } catch (MalformedURLException mue) {
1079: if (loggingEnabled) {
1080: logChannel.write(logLevel,
1081: "getResource not returned due to exception: "
1082: + name, mue);
1083: }
1084: return null;
1085: }
1086: }
1087: return null;
1088: }
1089:
1090: /**
1091: * Gets specified resource object.
1092: * @param name The name of the resource.
1093: * @return the resource if found, null if not.
1094: * @see Resource
1095: */
1096: public Resource getResourceObject(String name) {
1097: Resource resource;
1098: if (loggingEnabled) {
1099: logChannel.write(logLevel, "getResource loading: " + name);
1100: }
1101: try {
1102: resource = classPath.getResource(name);
1103: if (loggingEnabled) {
1104: if (resource == null) {
1105: logChannel.write(logLevel,
1106: "getResource not found: " + name);
1107: } else {
1108: logChannel.write(logLevel, "getResource finished: "
1109: + name);
1110: }
1111: }
1112: } catch (RuntimeException except) {
1113: logReadFailure(name, except);
1114: throw except;
1115: } catch (Error except) {
1116: logReadFailure(name, except);
1117: throw except;
1118: }
1119: return resource;
1120: }
1121:
1122: /**
1123: * Gets specified resource object.
1124: * @see deprecated Use getResourceObject()
1125: * @see getResourceObject
1126: */
1127: public Resource getResourceAsIs(String name) {
1128: return getResourceObject(name);
1129: }
1130:
1131: /**
1132: * Gets specified resource as input stream.
1133: * @param name The name of the resource.
1134: * @return an input stream representing the specified resource or
1135: * null if the resource is not found.
1136: */
1137: public InputStream getResourceAsStream(String name) {
1138: Resource resource = getResourceObject(name);
1139: if (resource != null) {
1140: try {
1141: return resource.getInputStream();
1142: } catch (IOException except) {
1143: logReadFailure(name, except);
1144: return null; // This is what getResourceAsStream does..
1145: }
1146: }
1147: return null;
1148: }
1149:
1150: /**
1151: * Gets specified resource as array of bytes.
1152: * @param name The name of the resource.
1153: * @return an array of bytes representing the specified resource or
1154: * null if the resource is not found.
1155: */
1156: public byte[] getResourceAsByteArray(String name) {
1157: Resource resource = getResourceObject(name);
1158: if (resource != null) {
1159: try {
1160: return resource.getBytes();
1161: } catch (IOException except) {
1162: logReadFailure(name, except);
1163: return null; // This is what getResourceAsStream does..
1164: }
1165: }
1166: return null;
1167: }
1168:
1169: /**
1170: * Determine if the classes loaded by this class loader have been modified.
1171: * If any file associated with loaded in the class loader's class path has
1172: * changed, the classes should be reloaded. If the classes need to be
1173: * reloaded, a new instance of this class loader must be created
1174: * because a particular class loader instance can only load classes
1175: * once.
1176: * @return <CODE>true</CODE> if the classes should be reloaded,
1177: * <CODE>false</CODE> if not.
1178: */
1179: public boolean shouldReload() {
1180: Enumeration classes = loadedClasses.elements();
1181: if (loggingEnabled) {
1182: logChannel.write(logLevel,
1183: "Checking for out-of-date classes");
1184: }
1185: while (classes.hasMoreElements()) {
1186: boolean isModified;
1187: try {
1188: isModified = ((ClassResource) classes.nextElement())
1189: .getResource().hasBeenModified();
1190: } catch (FileNotFoundException except) {
1191: if (loggingEnabled) {
1192: logChannel
1193: .write(
1194: logLevel,
1195: "File for loaded class can no longer be accessed",
1196: except);
1197: }
1198: isModified = true;
1199: }
1200: if (isModified) {
1201: if (loggingEnabled) {
1202: logChannel.write(logLevel,
1203: "Loaded classes have been modified");
1204: }
1205: return true;
1206: }
1207: }
1208: if (loggingEnabled) {
1209: logChannel.write(logLevel,
1210: "Loaded classes have not been modified");
1211: }
1212: return false;
1213: }
1214: }
|