0001: /*
0002: * $Header: /home/cvs/jakarta-tomcat-4.0/catalina/src/share/org/apache/catalina/loader/WebappClassLoader.java,v 1.45 2002/08/09 10:01:48 remm Exp $
0003: * $Revision: 1.45 $
0004: * $Date: 2002/08/09 10:01:48 $
0005: *
0006: * ====================================================================
0007: *
0008: * The Apache Software License, Version 1.1
0009: *
0010: * Copyright (c) 1999 The Apache Software Foundation. All rights
0011: * reserved.
0012: *
0013: * Redistribution and use in source and binary forms, with or without
0014: * modification, are permitted provided that the following conditions
0015: * are met:
0016: *
0017: * 1. Redistributions of source code must retain the above copyright
0018: * notice, this list of conditions and the following disclaimer.
0019: *
0020: * 2. Redistributions in binary form must reproduce the above copyright
0021: * notice, this list of conditions and the following disclaimer in
0022: * the documentation and/or other materials provided with the
0023: * distribution.
0024: *
0025: * 3. The end-user documentation included with the redistribution, if
0026: * any, must include the following acknowlegement:
0027: * "This product includes software developed by the
0028: * Apache Software Foundation (http://www.apache.org/)."
0029: * Alternately, this acknowlegement may appear in the software itself,
0030: * if and wherever such third-party acknowlegements normally appear.
0031: *
0032: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
0033: * Foundation" must not be used to endorse or promote products derived
0034: * from this software without prior written permission. For written
0035: * permission, please contact apache@apache.org.
0036: *
0037: * 5. Products derived from this software may not be called "Apache"
0038: * nor may "Apache" appear in their names without prior written
0039: * permission of the Apache Group.
0040: *
0041: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0042: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0043: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0044: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
0045: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0046: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0047: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0048: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0049: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0050: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0051: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0052: * SUCH DAMAGE.
0053: * ====================================================================
0054: *
0055: * This software consists of voluntary contributions made by many
0056: * individuals on behalf of the Apache Software Foundation. For more
0057: * information on the Apache Software Foundation, please see
0058: * <http://www.apache.org/>.
0059: *
0060: * [Additional notices, if required by prior licensing conditions]
0061: *
0062: */
0063:
0064: package org.apache.catalina.loader;
0065:
0066: import java.io.File;
0067: import java.io.FilePermission;
0068: import java.io.InputStream;
0069: import java.io.ByteArrayInputStream;
0070: import java.io.IOException;
0071: import java.net.JarURLConnection;
0072: import java.net.MalformedURLException;
0073: import java.net.URL;
0074: import java.net.URLClassLoader;
0075: import java.net.URLConnection;
0076: import java.net.URLStreamHandlerFactory;
0077: import java.net.URLStreamHandler;
0078: import java.security.AccessControlException;
0079: import java.security.AccessController;
0080: import java.security.CodeSource;
0081: import java.security.Permission;
0082: import java.security.PermissionCollection;
0083: import java.security.Policy;
0084: import java.security.PrivilegedAction;
0085: import java.security.cert.Certificate;
0086: import java.util.ArrayList;
0087: import java.util.Enumeration;
0088: import java.util.HashMap;
0089: import java.util.Iterator;
0090: import java.util.Vector;
0091: import java.util.jar.JarFile;
0092: import java.util.jar.JarEntry;
0093: import java.util.jar.JarInputStream;
0094: import java.util.jar.Manifest;
0095: import java.util.jar.Attributes;
0096: import java.util.jar.Attributes.Name;
0097:
0098: import javax.naming.directory.DirContext;
0099: import javax.naming.NamingException;
0100: import javax.naming.NamingEnumeration;
0101: import javax.naming.NameClassPair;
0102:
0103: import org.apache.catalina.Lifecycle;
0104: import org.apache.catalina.LifecycleEvent;
0105: import org.apache.catalina.LifecycleException;
0106: import org.apache.catalina.LifecycleListener;
0107:
0108: import org.apache.naming.JndiPermission;
0109: import org.apache.naming.resources.ResourceAttributes;
0110: import org.apache.naming.resources.Resource;
0111:
0112: /**
0113: * Specialized web application class loader.
0114: * <p>
0115: * This class loader is a full reimplementation of the
0116: * <code>URLClassLoader</code> from the JDK. It is desinged to be fully
0117: * compatible with a normal <code>URLClassLoader</code>, although its internal
0118: * behavior may be completely different.
0119: * <p>
0120: * <strong>IMPLEMENTATION NOTE</strong> - This class loader faithfully follows
0121: * the delegation model recommended in the specification. The system class
0122: * loader will be queried first, then the local repositories, and only then
0123: * delegation to the parent class loader will occur. This allows the web
0124: * application to override any shared class except the classes from J2SE.
0125: * Special handling is provided from the JAXP XML parser interfaces, the JNDI
0126: * interfaces, and the classes from the servlet API, which are never loaded
0127: * from the webapp repository.
0128: * <p>
0129: * <strong>IMPLEMENTATION NOTE</strong> - Due to limitations in Jasper
0130: * compilation technology, any repository which contains classes from
0131: * the servlet API will be ignored by the class loader.
0132: * <p>
0133: * <strong>IMPLEMENTATION NOTE</strong> - The class loader generates source
0134: * URLs which include the full JAR URL when a class is loaded from a JAR file,
0135: * which allows setting security permission at the class level, even when a
0136: * class is contained inside a JAR.
0137: * <p>
0138: * <strong>IMPLEMENTATION NOTE</strong> - Local repositories are searched in
0139: * the order they are added via the initial constructor and/or any subsequent
0140: * calls to <code>addRepository()</code> or <code>addJar()</code>.
0141: * <p>
0142: * <strong>IMPLEMENTATION NOTE</strong> - No check for sealing violations or
0143: * security is made unless a security manager is present.
0144: *
0145: * @author Remy Maucherat
0146: * @author Craig R. McClanahan
0147: * @version $Revision: 1.45 $ $Date: 2002/08/09 10:01:48 $
0148: */
0149: public class WebappClassLoader extends URLClassLoader implements
0150: Reloader, Lifecycle {
0151:
0152: protected class PrivilegedFindResource implements PrivilegedAction {
0153:
0154: private String name;
0155: private String path;
0156:
0157: PrivilegedFindResource(String name, String path) {
0158: this .name = name;
0159: this .path = path;
0160: }
0161:
0162: public Object run() {
0163: return findResourceInternal(name, path);
0164: }
0165:
0166: }
0167:
0168: // ------------------------------------------------------- Static Variables
0169:
0170: /**
0171: * The set of trigger classes that will cause a proposed repository not
0172: * to be added if this class is visible to the class loader that loaded
0173: * this factory class. Typically, trigger classes will be listed for
0174: * components that have been integrated into the JDK for later versions,
0175: * but where the corresponding JAR files are required to run on
0176: * earlier versions.
0177: */
0178: private static final String[] triggers = { "javax.servlet.Servlet" // Servlet API
0179: };
0180:
0181: /**
0182: * Set of package names which are not allowed to be loaded from a webapp
0183: * class loader without delegating first.
0184: */
0185: private static final String[] packageTriggers = { "javax", // Java extensions
0186: "org.xml.sax", // SAX 1 & 2
0187: "org.w3c.dom", // DOM 1 & 2
0188: "org.apache.xerces", // Xerces 1 & 2
0189: "org.apache.xalan" // Xalan
0190: };
0191:
0192: // ----------------------------------------------------------- Constructors
0193:
0194: /**
0195: * Construct a new ClassLoader with no defined repositories and no
0196: * parent ClassLoader.
0197: */
0198: public WebappClassLoader() {
0199:
0200: super (new URL[0]);
0201: this .parent = getParent();
0202: system = getSystemClassLoader();
0203: securityManager = System.getSecurityManager();
0204:
0205: if (securityManager != null) {
0206: refreshPolicy();
0207: }
0208:
0209: }
0210:
0211: /**
0212: * Construct a new ClassLoader with no defined repositories and no
0213: * parent ClassLoader.
0214: */
0215: public WebappClassLoader(ClassLoader parent) {
0216:
0217: super (new URL[0], parent);
0218: this .parent = getParent();
0219: system = getSystemClassLoader();
0220: securityManager = System.getSecurityManager();
0221:
0222: if (securityManager != null) {
0223: refreshPolicy();
0224: }
0225:
0226: }
0227:
0228: // ----------------------------------------------------- Instance Variables
0229:
0230: /**
0231: * Associated directory context giving access to the resources in this
0232: * webapp.
0233: */
0234: protected DirContext resources = null;
0235:
0236: /**
0237: * The set of optional packages (formerly standard extensions) that
0238: * are available in the repositories associated with this class loader.
0239: * Each object in this list is of type
0240: * <code>org.apache.catalina.loader.Extension</code>.
0241: */
0242: protected ArrayList available = new ArrayList();
0243:
0244: /**
0245: * The cache of ResourceEntry for classes and resources we have loaded,
0246: * keyed by resource name.
0247: */
0248: protected HashMap resourceEntries = new HashMap();
0249:
0250: /**
0251: * The list of not found resources.
0252: */
0253: protected HashMap notFoundResources = new HashMap();
0254:
0255: /**
0256: * The debugging detail level of this component.
0257: */
0258: protected int debug = 0;
0259:
0260: /**
0261: * Should this class loader delegate to the parent class loader
0262: * <strong>before</strong> searching its own repositories (i.e. the
0263: * usual Java2 delegation model)? If set to <code>false</code>,
0264: * this class loader will search its own repositories first, and
0265: * delegate to the parent only if the class or resource is not
0266: * found locally.
0267: */
0268: protected boolean delegate = false;
0269:
0270: /**
0271: * The list of local repositories, in the order they should be searched
0272: * for locally loaded classes or resources.
0273: */
0274: protected String[] repositories = new String[0];
0275:
0276: /**
0277: * Repositories translated as path in the work directory (for Jasper
0278: * originally), but which is used to generate fake URLs should getURLs be
0279: * called.
0280: */
0281: protected File[] files = new File[0];
0282:
0283: /**
0284: * The list of JARs, in the order they should be searched
0285: * for locally loaded classes or resources.
0286: */
0287: protected JarFile[] jarFiles = new JarFile[0];
0288:
0289: /**
0290: * The list of JARs, in the order they should be searched
0291: * for locally loaded classes or resources.
0292: */
0293: protected File[] jarRealFiles = new File[0];
0294:
0295: /**
0296: * The path which will be monitored for added Jar files.
0297: */
0298: protected String jarPath = null;
0299:
0300: /**
0301: * The list of JARs, in the order they should be searched
0302: * for locally loaded classes or resources.
0303: */
0304: protected String[] jarNames = new String[0];
0305:
0306: /**
0307: * The list of JARs last modified dates, in the order they should be
0308: * searched for locally loaded classes or resources.
0309: */
0310: protected long[] lastModifiedDates = new long[0];
0311:
0312: /**
0313: * The list of resources which should be checked when checking for
0314: * modifications.
0315: */
0316: protected String[] paths = new String[0];
0317:
0318: /**
0319: * The set of optional packages (formerly standard extensions) that
0320: * are required in the repositories associated with this class loader.
0321: * Each object in this list is of type
0322: * <code>org.apache.catalina.loader.Extension</code>.
0323: */
0324: protected ArrayList required = new ArrayList();
0325:
0326: /**
0327: * A list of read File and Jndi Permission's required if this loader
0328: * is for a web application context.
0329: */
0330: private ArrayList permissionList = new ArrayList();
0331:
0332: /**
0333: * The PermissionCollection for each CodeSource for a web
0334: * application context.
0335: */
0336: private HashMap loaderPC = new HashMap();
0337:
0338: /**
0339: * Instance of the SecurityManager installed.
0340: */
0341: private SecurityManager securityManager = null;
0342:
0343: /**
0344: * The parent class loader.
0345: */
0346: private ClassLoader parent = null;
0347:
0348: /**
0349: * The system class loader.
0350: */
0351: private ClassLoader system = null;
0352:
0353: /**
0354: * Has this component been started?
0355: */
0356: protected boolean started = false;
0357:
0358: /**
0359: * Has external repositories.
0360: */
0361: protected boolean hasExternalRepositories = false;
0362:
0363: /**
0364: * All permission.
0365: */
0366: private Permission allPermission = new java.security.AllPermission();
0367:
0368: // ------------------------------------------------------------- Properties
0369:
0370: /**
0371: * Get associated resources.
0372: */
0373: public DirContext getResources() {
0374:
0375: return this .resources;
0376:
0377: }
0378:
0379: /**
0380: * Set associated resources.
0381: */
0382: public void setResources(DirContext resources) {
0383:
0384: this .resources = resources;
0385:
0386: }
0387:
0388: /**
0389: * Return the debugging detail level for this component.
0390: */
0391: public int getDebug() {
0392:
0393: return (this .debug);
0394:
0395: }
0396:
0397: /**
0398: * Set the debugging detail level for this component.
0399: *
0400: * @param debug The new debugging detail level
0401: */
0402: public void setDebug(int debug) {
0403:
0404: this .debug = debug;
0405:
0406: }
0407:
0408: /**
0409: * Return the "delegate first" flag for this class loader.
0410: */
0411: public boolean getDelegate() {
0412:
0413: return (this .delegate);
0414:
0415: }
0416:
0417: /**
0418: * Set the "delegate first" flag for this class loader.
0419: *
0420: * @param delegate The new "delegate first" flag
0421: */
0422: public void setDelegate(boolean delegate) {
0423:
0424: this .delegate = delegate;
0425:
0426: }
0427:
0428: /**
0429: * If there is a Java SecurityManager create a read FilePermission
0430: * or JndiPermission for the file directory path.
0431: *
0432: * @param path file directory path
0433: */
0434: public void addPermission(String path) {
0435: if (securityManager != null) {
0436: Permission permission = null;
0437: if (path.startsWith("jndi:")
0438: || path.startsWith("jar:jndi:")) {
0439: permission = new JndiPermission(path + "*");
0440: } else {
0441: permission = new FilePermission(path + "-", "read");
0442: }
0443: addPermission(permission);
0444: }
0445: }
0446:
0447: /**
0448: * If there is a Java SecurityManager create a read FilePermission
0449: * or JndiPermission for URL.
0450: *
0451: * @param url URL for a file or directory on local system
0452: */
0453: public void addPermission(URL url) {
0454: addPermission(url.toString());
0455: }
0456:
0457: /**
0458: * If there is a Java SecurityManager create a Permission.
0459: *
0460: * @param url URL for a file or directory on local system
0461: */
0462: public void addPermission(Permission permission) {
0463: if ((securityManager != null) && (permission != null)) {
0464: permissionList.add(permission);
0465: }
0466: }
0467:
0468: /**
0469: * Return the JAR path.
0470: */
0471: public String getJarPath() {
0472:
0473: return this .jarPath;
0474:
0475: }
0476:
0477: /**
0478: * Change the Jar path.
0479: */
0480: public void setJarPath(String jarPath) {
0481:
0482: this .jarPath = jarPath;
0483:
0484: }
0485:
0486: // ------------------------------------------------------- Reloader Methods
0487:
0488: /**
0489: * Add a new repository to the set of places this ClassLoader can look for
0490: * classes to be loaded.
0491: *
0492: * @param repository Name of a source of classes to be loaded, such as a
0493: * directory pathname, a JAR file pathname, or a ZIP file pathname
0494: *
0495: * @exception IllegalArgumentException if the specified repository is
0496: * invalid or does not exist
0497: */
0498: public void addRepository(String repository) {
0499:
0500: // Ignore any of the standard repositories, as they are set up using
0501: // either addJar or addRepository
0502: if (repository.startsWith("/WEB-INF/lib")
0503: || repository.startsWith("/WEB-INF/classes"))
0504: return;
0505:
0506: // Add this repository to our underlying class loader
0507: try {
0508: URL url = new URL(repository);
0509: super .addURL(url);
0510: hasExternalRepositories = true;
0511: } catch (MalformedURLException e) {
0512: throw new IllegalArgumentException(e.toString());
0513: }
0514:
0515: }
0516:
0517: /**
0518: * Add a new repository to the set of places this ClassLoader can look for
0519: * classes to be loaded.
0520: *
0521: * @param repository Name of a source of classes to be loaded, such as a
0522: * directory pathname, a JAR file pathname, or a ZIP file pathname
0523: *
0524: * @exception IllegalArgumentException if the specified repository is
0525: * invalid or does not exist
0526: */
0527: synchronized void addRepository(String repository, File file) {
0528:
0529: // Note : There should be only one (of course), but I think we should
0530: // keep this a bit generic
0531:
0532: if (repository == null)
0533: return;
0534:
0535: if (debug >= 1)
0536: log("addRepository(" + repository + ")");
0537:
0538: int i;
0539:
0540: // Add this repository to our internal list
0541: String[] result = new String[repositories.length + 1];
0542: for (i = 0; i < repositories.length; i++) {
0543: result[i] = repositories[i];
0544: }
0545: result[repositories.length] = repository;
0546: repositories = result;
0547:
0548: // Add the file to the list
0549: File[] result2 = new File[files.length + 1];
0550: for (i = 0; i < files.length; i++) {
0551: result2[i] = files[i];
0552: }
0553: result2[files.length] = file;
0554: files = result2;
0555:
0556: }
0557:
0558: synchronized void addJar(String jar, JarFile jarFile, File file)
0559: throws IOException {
0560:
0561: if (jar == null)
0562: return;
0563: if (jarFile == null)
0564: return;
0565: if (file == null)
0566: return;
0567:
0568: if (debug >= 1)
0569: log("addJar(" + jar + ")");
0570:
0571: int i;
0572:
0573: if ((jarPath != null) && (jar.startsWith(jarPath))) {
0574:
0575: String jarName = jar.substring(jarPath.length());
0576: while (jarName.startsWith("/"))
0577: jarName = jarName.substring(1);
0578:
0579: String[] result = new String[jarNames.length + 1];
0580: for (i = 0; i < jarNames.length; i++) {
0581: result[i] = jarNames[i];
0582: }
0583: result[jarNames.length] = jarName;
0584: jarNames = result;
0585:
0586: }
0587:
0588: try {
0589:
0590: // Register the JAR for tracking
0591:
0592: long lastModified = ((ResourceAttributes) resources
0593: .getAttributes(jar)).getLastModified();
0594:
0595: String[] result = new String[paths.length + 1];
0596: for (i = 0; i < paths.length; i++) {
0597: result[i] = paths[i];
0598: }
0599: result[paths.length] = jar;
0600: paths = result;
0601:
0602: long[] result3 = new long[lastModifiedDates.length + 1];
0603: for (i = 0; i < lastModifiedDates.length; i++) {
0604: result3[i] = lastModifiedDates[i];
0605: }
0606: result3[lastModifiedDates.length] = lastModified;
0607: lastModifiedDates = result3;
0608:
0609: } catch (NamingException e) {
0610: // Ignore
0611: }
0612:
0613: // If the JAR currently contains invalid classes, don't actually use it
0614: // for classloading
0615: if (!validateJarFile(file))
0616: return;
0617:
0618: JarFile[] result2 = new JarFile[jarFiles.length + 1];
0619: for (i = 0; i < jarFiles.length; i++) {
0620: result2[i] = jarFiles[i];
0621: }
0622: result2[jarFiles.length] = jarFile;
0623: jarFiles = result2;
0624:
0625: // Add the file to the list
0626: File[] result4 = new File[jarRealFiles.length + 1];
0627: for (i = 0; i < jarRealFiles.length; i++) {
0628: result4[i] = jarRealFiles[i];
0629: }
0630: result4[jarRealFiles.length] = file;
0631: jarRealFiles = result4;
0632:
0633: // Load manifest
0634: Manifest manifest = jarFile.getManifest();
0635: if (manifest != null) {
0636: Iterator extensions = Extension.getAvailable(manifest)
0637: .iterator();
0638: while (extensions.hasNext()) {
0639: available.add(extensions.next());
0640: }
0641: extensions = Extension.getRequired(manifest).iterator();
0642: while (extensions.hasNext()) {
0643: required.add(extensions.next());
0644: }
0645: }
0646:
0647: }
0648:
0649: /**
0650: * Return a list of "optional packages" (formerly "standard extensions")
0651: * that have been declared to be available in the repositories associated
0652: * with this class loader, plus any parent class loader implemented with
0653: * the same class.
0654: */
0655: public Extension[] findAvailable() {
0656:
0657: // Initialize the results with our local available extensions
0658: ArrayList results = new ArrayList();
0659: Iterator available = this .available.iterator();
0660: while (available.hasNext())
0661: results.add(available.next());
0662:
0663: // Trace our parentage tree and add declared extensions when possible
0664: ClassLoader loader = this ;
0665: while (true) {
0666: loader = loader.getParent();
0667: if (loader == null)
0668: break;
0669: if (!(loader instanceof WebappClassLoader))
0670: continue;
0671: Extension extensions[] = ((WebappClassLoader) loader)
0672: .findAvailable();
0673: for (int i = 0; i < extensions.length; i++)
0674: results.add(extensions[i]);
0675: }
0676:
0677: // Return the results as an array
0678: Extension extensions[] = new Extension[results.size()];
0679: return ((Extension[]) results.toArray(extensions));
0680:
0681: }
0682:
0683: /**
0684: * Return a String array of the current repositories for this class
0685: * loader. If there are no repositories, a zero-length array is
0686: * returned.
0687: */
0688: public String[] findRepositories() {
0689:
0690: return (repositories);
0691:
0692: }
0693:
0694: /**
0695: * Return a list of "optional packages" (formerly "standard extensions")
0696: * that have been declared to be required in the repositories associated
0697: * with this class loader, plus any parent class loader implemented with
0698: * the same class.
0699: */
0700: public Extension[] findRequired() {
0701:
0702: // Initialize the results with our local required extensions
0703: ArrayList results = new ArrayList();
0704: Iterator required = this .required.iterator();
0705: while (required.hasNext())
0706: results.add(required.next());
0707:
0708: // Trace our parentage tree and add declared extensions when possible
0709: ClassLoader loader = this ;
0710: while (true) {
0711: loader = loader.getParent();
0712: if (loader == null)
0713: break;
0714: if (!(loader instanceof WebappClassLoader))
0715: continue;
0716: Extension extensions[] = ((WebappClassLoader) loader)
0717: .findRequired();
0718: for (int i = 0; i < extensions.length; i++)
0719: results.add(extensions[i]);
0720: }
0721:
0722: // Return the results as an array
0723: Extension extensions[] = new Extension[results.size()];
0724: return ((Extension[]) results.toArray(extensions));
0725:
0726: }
0727:
0728: /**
0729: * Have one or more classes or resources been modified so that a reload
0730: * is appropriate?
0731: */
0732: public boolean modified() {
0733:
0734: if (debug >= 2)
0735: log("modified()");
0736:
0737: // Checking for modified loaded resources
0738: int length = paths.length;
0739:
0740: // A rare race condition can occur in the updates of the two arrays
0741: // It's totally ok if the latest class added is not checked (it will
0742: // be checked the next time
0743: int length2 = lastModifiedDates.length;
0744: if (length > length2)
0745: length = length2;
0746:
0747: for (int i = 0; i < length; i++) {
0748: try {
0749: long lastModified =
0750: ((ResourceAttributes) resources.getAttributes(paths[i]))
0751: .getLastModified();
0752: if (lastModified != lastModifiedDates[i]) {
0753: log(" Resource '" + paths[i]
0754: + "' was modified; Date is now: "
0755: + new java.util.Date(lastModified) + " Was: "
0756: + new java.util.Date(lastModifiedDates[i]));
0757: return (true);
0758: }
0759: } catch (NamingException e) {
0760: log(" Resource '" + paths[i] + "' is missing");
0761: return (true);
0762: }
0763: }
0764:
0765: length = jarNames.length;
0766:
0767: // Check if JARs have been added or removed
0768: if (getJarPath() != null) {
0769:
0770: try {
0771: NamingEnumeration enum = resources.listBindings(getJarPath());
0772: int i = 0;
0773: while (enum.hasMoreElements() && (i < length)) {
0774: NameClassPair ncPair = (NameClassPair) enum.nextElement();
0775: String name = ncPair.getName();
0776: // Ignore non JARs present in the lib folder
0777: if (!name.endsWith(".jar"))
0778: continue;
0779: if (!name.equals(jarNames[i])) {
0780: // Missing JAR
0781: log(" Additional JARs have been added : '"
0782: + name + "'");
0783: return (true);
0784: }
0785: i++;
0786: }
0787: if (enum.hasMoreElements()) {
0788: while (enum.hasMoreElements()) {
0789: NameClassPair ncPair =
0790: (NameClassPair) enum.nextElement();
0791: String name = ncPair.getName();
0792: // Additional non-JAR files are allowed
0793: if (name.endsWith(".jar")) {
0794: // There was more JARs
0795: log(" Additional JARs have been added");
0796: return (true);
0797: }
0798: }
0799: } else if (i < jarNames.length) {
0800: // There was less JARs
0801: log(" Additional JARs have been added");
0802: return (true);
0803: }
0804: } catch (NamingException e) {
0805: if (debug > 2)
0806: log(" Failed tracking modifications of '"
0807: + getJarPath() + "'");
0808: } catch (ClassCastException e) {
0809: log(" Failed tracking modifications of '"
0810: + getJarPath() + "' : " + e.getMessage());
0811: }
0812:
0813: }
0814:
0815: // No classes have been modified
0816: return (false);
0817:
0818: }
0819:
0820: /**
0821: * Render a String representation of this object.
0822: */
0823: public String toString() {
0824:
0825: StringBuffer sb = new StringBuffer("WebappClassLoader\r\n");
0826: sb.append(" available:\r\n");
0827: Iterator available = this .available.iterator();
0828: while (available.hasNext()) {
0829: sb.append(" ");
0830: sb.append(available.next().toString());
0831: sb.append("\r\n");
0832: }
0833: sb.append(" delegate: ");
0834: sb.append(delegate);
0835: sb.append("\r\n");
0836: sb.append(" repositories:\r\n");
0837: for (int i = 0; i < repositories.length; i++) {
0838: sb.append(" ");
0839: sb.append(repositories[i]);
0840: sb.append("\r\n");
0841: }
0842: sb.append(" required:\r\n");
0843: Iterator required = this .required.iterator();
0844: while (required.hasNext()) {
0845: sb.append(" ");
0846: sb.append(required.next().toString());
0847: sb.append("\r\n");
0848: }
0849: if (this .parent != null) {
0850: sb.append("----------> Parent Classloader:\r\n");
0851: sb.append(this .parent.toString());
0852: sb.append("\r\n");
0853: }
0854: return (sb.toString());
0855:
0856: }
0857:
0858: // ---------------------------------------------------- ClassLoader Methods
0859:
0860: /**
0861: * Find the specified class in our local repositories, if possible. If
0862: * not found, throw <code>ClassNotFoundException</code>.
0863: *
0864: * @param name Name of the class to be loaded
0865: *
0866: * @exception ClassNotFoundException if the class was not found
0867: */
0868: public Class findClass(String name) throws ClassNotFoundException {
0869:
0870: if (debug >= 3)
0871: log(" findClass(" + name + ")");
0872:
0873: // (1) Permission to define this class when using a SecurityManager
0874: if (securityManager != null) {
0875: int i = name.lastIndexOf('.');
0876: if (i >= 0) {
0877: try {
0878: if (debug >= 4)
0879: log(" securityManager.checkPackageDefinition");
0880: securityManager.checkPackageDefinition(name
0881: .substring(0, i));
0882: } catch (Exception se) {
0883: if (debug >= 4)
0884: log(
0885: " -->Exception-->ClassNotFoundException",
0886: se);
0887: throw new ClassNotFoundException(name);
0888: }
0889: }
0890: }
0891:
0892: // Ask our superclass to locate this class, if possible
0893: // (throws ClassNotFoundException if it is not found)
0894: Class clazz = null;
0895: try {
0896: if (debug >= 4)
0897: log(" findClassInternal(" + name + ")");
0898: try {
0899: clazz = findClassInternal(name);
0900: } catch (AccessControlException ace) {
0901: ace.printStackTrace();
0902: throw new ClassNotFoundException(name);
0903: } catch (RuntimeException e) {
0904: if (debug >= 4)
0905: log(" -->RuntimeException Rethrown", e);
0906: throw e;
0907: }
0908: if ((clazz == null) && hasExternalRepositories) {
0909: try {
0910: clazz = super .findClass(name);
0911: } catch (AccessControlException ace) {
0912: throw new ClassNotFoundException(name);
0913: } catch (RuntimeException e) {
0914: if (debug >= 4)
0915: log(" -->RuntimeException Rethrown", e);
0916: throw e;
0917: }
0918: }
0919: if (clazz == null) {
0920: if (debug >= 3)
0921: log(" --> Returning ClassNotFoundException");
0922: throw new ClassNotFoundException(name);
0923: }
0924: } catch (ClassNotFoundException e) {
0925: if (debug >= 3)
0926: log(" --> Passing on ClassNotFoundException", e);
0927: throw e;
0928: }
0929:
0930: // Return the class we have located
0931: if (debug >= 4)
0932: log(" Returning class " + clazz);
0933: if ((debug >= 4) && (clazz != null))
0934: log(" Loaded by " + clazz.getClassLoader());
0935: return (clazz);
0936:
0937: }
0938:
0939: /**
0940: * Find the specified resource in our local repository, and return a
0941: * <code>URL</code> refering to it, or <code>null</code> if this resource
0942: * cannot be found.
0943: *
0944: * @param name Name of the resource to be found
0945: */
0946: public URL findResource(final String name) {
0947:
0948: if (debug >= 3)
0949: log(" findResource(" + name + ")");
0950:
0951: URL url = null;
0952:
0953: ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
0954: if (entry == null) {
0955: if (securityManager != null) {
0956: PrivilegedAction dp = new PrivilegedFindResource(name,
0957: name);
0958: entry = (ResourceEntry) AccessController
0959: .doPrivileged(dp);
0960: } else {
0961: entry = findResourceInternal(name, name);
0962: }
0963: }
0964: if (entry != null) {
0965: url = entry.source;
0966: }
0967:
0968: if ((url == null) && hasExternalRepositories)
0969: url = super .findResource(name);
0970:
0971: if (debug >= 3) {
0972: if (url != null)
0973: log(" --> Returning '" + url.toString() + "'");
0974: else
0975: log(" --> Resource not found, returning null");
0976: }
0977: return (url);
0978:
0979: }
0980:
0981: /**
0982: * Return an enumeration of <code>URLs</code> representing all of the
0983: * resources with the given name. If no resources with this name are
0984: * found, return an empty enumeration.
0985: *
0986: * @param name Name of the resources to be found
0987: *
0988: * @exception IOException if an input/output error occurs
0989: */
0990: public Enumeration findResources(String name) throws IOException {
0991:
0992: if (debug >= 3)
0993: log(" findResources(" + name + ")");
0994:
0995: Vector result = new Vector();
0996:
0997: int jarFilesLength = jarFiles.length;
0998: int repositoriesLength = repositories.length;
0999:
1000: int i;
1001:
1002: // Looking at the repositories
1003: for (i = 0; i < repositoriesLength; i++) {
1004: try {
1005: String fullPath = repositories[i] + name;
1006: resources.lookup(fullPath);
1007: // Note : Not getting an exception here means the resource was
1008: // found
1009: try {
1010: result.addElement(getURL(new File(files[i], name)));
1011: } catch (MalformedURLException e) {
1012: // Ignore
1013: }
1014: } catch (NamingException e) {
1015: }
1016: }
1017:
1018: // Looking at the JAR files
1019: for (i = 0; i < jarFilesLength; i++) {
1020: JarEntry jarEntry = jarFiles[i].getJarEntry(name);
1021: if (jarEntry != null) {
1022: try {
1023: String jarFakeUrl = getURL(jarRealFiles[i])
1024: .toString();
1025: jarFakeUrl = "jar:" + jarFakeUrl + "!/" + name;
1026: result.addElement(new URL(jarFakeUrl));
1027: } catch (MalformedURLException e) {
1028: // Ignore
1029: }
1030: }
1031: }
1032:
1033: // Adding the results of a call to the superclass
1034: if (hasExternalRepositories) {
1035:
1036: Enumeration otherResourcePaths = super .findResources(name);
1037:
1038: while (otherResourcePaths.hasMoreElements()) {
1039: result.addElement(otherResourcePaths.nextElement());
1040: }
1041:
1042: }
1043:
1044: return result.elements();
1045:
1046: }
1047:
1048: /**
1049: * Find the resource with the given name. A resource is some data
1050: * (images, audio, text, etc.) that can be accessed by class code in a
1051: * way that is independent of the location of the code. The name of a
1052: * resource is a "/"-separated path name that identifies the resource.
1053: * If the resource cannot be found, return <code>null</code>.
1054: * <p>
1055: * This method searches according to the following algorithm, returning
1056: * as soon as it finds the appropriate URL. If the resource cannot be
1057: * found, returns <code>null</code>.
1058: * <ul>
1059: * <li>If the <code>delegate</code> property is set to <code>true</code>,
1060: * call the <code>getResource()</code> method of the parent class
1061: * loader, if any.</li>
1062: * <li>Call <code>findResource()</code> to find this resource in our
1063: * locally defined repositories.</li>
1064: * <li>Call the <code>getResource()</code> method of the parent class
1065: * loader, if any.</li>
1066: * </ul>
1067: *
1068: * @param name Name of the resource to return a URL for
1069: */
1070: public URL getResource(String name) {
1071:
1072: if (debug >= 2)
1073: log("getResource(" + name + ")");
1074: URL url = null;
1075:
1076: // (1) Delegate to parent if requested
1077: if (delegate) {
1078: if (debug >= 3)
1079: log(" Delegating to parent classloader");
1080: ClassLoader loader = parent;
1081: if (loader == null)
1082: loader = system;
1083: url = loader.getResource(name);
1084: if (url != null) {
1085: if (debug >= 2)
1086: log(" --> Returning '" + url.toString() + "'");
1087: return (url);
1088: }
1089: }
1090:
1091: // (2) Search local repositories
1092: if (debug >= 3)
1093: log(" Searching local repositories");
1094: url = findResource(name);
1095: if (url != null) {
1096: if (debug >= 2)
1097: log(" --> Returning '" + url.toString() + "'");
1098: return (url);
1099: }
1100:
1101: // (3) Delegate to parent unconditionally if not already attempted
1102: if (!delegate) {
1103: ClassLoader loader = parent;
1104: if (loader == null)
1105: loader = system;
1106: url = loader.getResource(name);
1107: if (url != null) {
1108: if (debug >= 2)
1109: log(" --> Returning '" + url.toString() + "'");
1110: return (url);
1111: }
1112: }
1113:
1114: // (4) Resource was not found
1115: if (debug >= 2)
1116: log(" --> Resource not found, returning null");
1117: return (null);
1118:
1119: }
1120:
1121: /**
1122: * Find the resource with the given name, and return an input stream
1123: * that can be used for reading it. The search order is as described
1124: * for <code>getResource()</code>, after checking to see if the resource
1125: * data has been previously cached. If the resource cannot be found,
1126: * return <code>null</code>.
1127: *
1128: * @param name Name of the resource to return an input stream for
1129: */
1130: public InputStream getResourceAsStream(String name) {
1131:
1132: if (debug >= 2)
1133: log("getResourceAsStream(" + name + ")");
1134: InputStream stream = null;
1135:
1136: // (0) Check for a cached copy of this resource
1137: stream = findLoadedResource(name);
1138: if (stream != null) {
1139: if (debug >= 2)
1140: log(" --> Returning stream from cache");
1141: return (stream);
1142: }
1143:
1144: // (1) Delegate to parent if requested
1145: if (delegate) {
1146: if (debug >= 3)
1147: log(" Delegating to parent classloader");
1148: ClassLoader loader = parent;
1149: if (loader == null)
1150: loader = system;
1151: stream = loader.getResourceAsStream(name);
1152: if (stream != null) {
1153: // FIXME - cache???
1154: if (debug >= 2)
1155: log(" --> Returning stream from parent");
1156: return (stream);
1157: }
1158: }
1159:
1160: // (2) Search local repositories
1161: if (debug >= 3)
1162: log(" Searching local repositories");
1163: URL url = findResource(name);
1164: if (url != null) {
1165: // FIXME - cache???
1166: if (debug >= 2)
1167: log(" --> Returning stream from local");
1168: stream = findLoadedResource(name);
1169: try {
1170: if (hasExternalRepositories && (stream == null))
1171: stream = url.openStream();
1172: } catch (IOException e) {
1173: ; // Ignore
1174: }
1175: if (stream != null)
1176: return (stream);
1177: }
1178:
1179: // (3) Delegate to parent unconditionally
1180: if (!delegate) {
1181: if (debug >= 3)
1182: log(" Delegating to parent classloader");
1183: ClassLoader loader = parent;
1184: if (loader == null)
1185: loader = system;
1186: stream = loader.getResourceAsStream(name);
1187: if (stream != null) {
1188: // FIXME - cache???
1189: if (debug >= 2)
1190: log(" --> Returning stream from parent");
1191: return (stream);
1192: }
1193: }
1194:
1195: // (4) Resource was not found
1196: if (debug >= 2)
1197: log(" --> Resource not found, returning null");
1198: return (null);
1199:
1200: }
1201:
1202: /**
1203: * Load the class with the specified name. This method searches for
1204: * classes in the same manner as <code>loadClass(String, boolean)</code>
1205: * with <code>false</code> as the second argument.
1206: *
1207: * @param name Name of the class to be loaded
1208: *
1209: * @exception ClassNotFoundException if the class was not found
1210: */
1211: public Class loadClass(String name) throws ClassNotFoundException {
1212:
1213: return (loadClass(name, false));
1214:
1215: }
1216:
1217: /**
1218: * Load the class with the specified name, searching using the following
1219: * algorithm until it finds and returns the class. If the class cannot
1220: * be found, returns <code>ClassNotFoundException</code>.
1221: * <ul>
1222: * <li>Call <code>findLoadedClass(String)</code> to check if the
1223: * class has already been loaded. If it has, the same
1224: * <code>Class</code> object is returned.</li>
1225: * <li>If the <code>delegate</code> property is set to <code>true</code>,
1226: * call the <code>loadClass()</code> method of the parent class
1227: * loader, if any.</li>
1228: * <li>Call <code>findClass()</code> to find this class in our locally
1229: * defined repositories.</li>
1230: * <li>Call the <code>loadClass()</code> method of our parent
1231: * class loader, if any.</li>
1232: * </ul>
1233: * If the class was found using the above steps, and the
1234: * <code>resolve</code> flag is <code>true</code>, this method will then
1235: * call <code>resolveClass(Class)</code> on the resulting Class object.
1236: *
1237: * @param name Name of the class to be loaded
1238: * @param resolve If <code>true</code> then resolve the class
1239: *
1240: * @exception ClassNotFoundException if the class was not found
1241: */
1242: public Class loadClass(String name, boolean resolve)
1243: throws ClassNotFoundException {
1244:
1245: if (debug >= 2)
1246: log("loadClass(" + name + ", " + resolve + ")");
1247: Class clazz = null;
1248:
1249: // Don't load classes if class loader is stopped
1250: if (!started) {
1251: log("Lifecycle error : CL stopped");
1252: throw new ClassNotFoundException(name);
1253: }
1254:
1255: // (0) Check our previously loaded local class cache
1256: clazz = findLoadedClass0(name);
1257: if (clazz != null) {
1258: if (debug >= 3)
1259: log(" Returning class from cache");
1260: if (resolve)
1261: resolveClass(clazz);
1262: return (clazz);
1263: }
1264:
1265: // (0.1) Check our previously loaded class cache
1266: clazz = findLoadedClass(name);
1267: if (clazz != null) {
1268: if (debug >= 3)
1269: log(" Returning class from cache");
1270: if (resolve)
1271: resolveClass(clazz);
1272: return (clazz);
1273: }
1274:
1275: // (0.2) Try loading the class with the system class loader, to prevent
1276: // the webapp from overriding J2SE classes
1277: try {
1278: clazz = system.loadClass(name);
1279: if (clazz != null) {
1280: if (resolve)
1281: resolveClass(clazz);
1282: return (clazz);
1283: }
1284: } catch (ClassNotFoundException e) {
1285: // Ignore
1286: }
1287:
1288: // (0.5) Permission to access this class when using a SecurityManager
1289: if (securityManager != null) {
1290: int i = name.lastIndexOf('.');
1291: if (i >= 0) {
1292: try {
1293: securityManager.checkPackageAccess(name.substring(
1294: 0, i));
1295: } catch (SecurityException se) {
1296: String error = "Security Violation, attempt to use "
1297: + "Restricted Class: " + name;
1298: System.out.println(error);
1299: se.printStackTrace();
1300: log(error);
1301: throw new ClassNotFoundException(error);
1302: }
1303: }
1304: }
1305:
1306: boolean delegateLoad = delegate || filter(name);
1307:
1308: // (1) Delegate to our parent if requested
1309: if (delegateLoad) {
1310: if (debug >= 3)
1311: log(" Delegating to parent classloader");
1312: ClassLoader loader = parent;
1313: if (loader == null)
1314: loader = system;
1315: try {
1316: clazz = loader.loadClass(name);
1317: if (clazz != null) {
1318: if (debug >= 3)
1319: log(" Loading class from parent");
1320: if (resolve)
1321: resolveClass(clazz);
1322: return (clazz);
1323: }
1324: } catch (ClassNotFoundException e) {
1325: ;
1326: }
1327: }
1328:
1329: // (2) Search local repositories
1330: if (debug >= 3)
1331: log(" Searching local repositories");
1332: try {
1333: clazz = findClass(name);
1334: if (clazz != null) {
1335: if (debug >= 3)
1336: log(" Loading class from local repository");
1337: if (resolve)
1338: resolveClass(clazz);
1339: return (clazz);
1340: }
1341: } catch (ClassNotFoundException e) {
1342: ;
1343: }
1344:
1345: // (3) Delegate to parent unconditionally
1346: if (!delegateLoad) {
1347: if (debug >= 3)
1348: log(" Delegating to parent classloader");
1349: ClassLoader loader = parent;
1350: if (loader == null)
1351: loader = system;
1352: try {
1353: clazz = loader.loadClass(name);
1354: if (clazz != null) {
1355: if (debug >= 3)
1356: log(" Loading class from parent");
1357: if (resolve)
1358: resolveClass(clazz);
1359: return (clazz);
1360: }
1361: } catch (ClassNotFoundException e) {
1362: ;
1363: }
1364: }
1365:
1366: // This class was not found
1367: throw new ClassNotFoundException(name);
1368:
1369: }
1370:
1371: /**
1372: * Get the Permissions for a CodeSource. If this instance
1373: * of WebappClassLoader is for a web application context,
1374: * add read FilePermission or JndiPermissions for the base
1375: * directory (if unpacked),
1376: * the context URL, and jar file resources.
1377: *
1378: * @param CodeSource where the code was loaded from
1379: * @return PermissionCollection for CodeSource
1380: */
1381: protected PermissionCollection getPermissions(CodeSource codeSource) {
1382:
1383: String codeUrl = codeSource.getLocation().toString();
1384: PermissionCollection pc;
1385: if ((pc = (PermissionCollection) loaderPC.get(codeUrl)) == null) {
1386: pc = super .getPermissions(codeSource);
1387: if (pc != null) {
1388: Iterator perms = permissionList.iterator();
1389: while (perms.hasNext()) {
1390: Permission p = (Permission) perms.next();
1391: pc.add(p);
1392: }
1393: loaderPC.put(codeUrl, pc);
1394: }
1395: }
1396: return (pc);
1397:
1398: }
1399:
1400: /**
1401: * Returns the search path of URLs for loading classes and resources.
1402: * This includes the original list of URLs specified to the constructor,
1403: * along with any URLs subsequently appended by the addURL() method.
1404: * @return the search path of URLs for loading classes and resources.
1405: */
1406: public URL[] getURLs() {
1407:
1408: URL[] external = super .getURLs();
1409:
1410: int filesLength = files.length;
1411: int jarFilesLength = jarRealFiles.length;
1412: int length = filesLength + jarFilesLength + external.length;
1413: int i;
1414:
1415: try {
1416:
1417: URL[] urls = new URL[length];
1418: for (i = 0; i < length; i++) {
1419: if (i < filesLength) {
1420: urls[i] = getURL(files[i]);
1421: } else if (i < filesLength + jarFilesLength) {
1422: urls[i] = getURL(jarRealFiles[i - filesLength]);
1423: } else {
1424: urls[i] = external[i - filesLength - jarFilesLength];
1425: }
1426: }
1427:
1428: return urls;
1429:
1430: } catch (MalformedURLException e) {
1431: return (new URL[0]);
1432: }
1433:
1434: }
1435:
1436: // ------------------------------------------------------ Lifecycle Methods
1437:
1438: /**
1439: * Add a lifecycle event listener to this component.
1440: *
1441: * @param listener The listener to add
1442: */
1443: public void addLifecycleListener(LifecycleListener listener) {
1444: }
1445:
1446: /**
1447: * Get the lifecycle listeners associated with this lifecycle. If this
1448: * Lifecycle has no listeners registered, a zero-length array is returned.
1449: */
1450: public LifecycleListener[] findLifecycleListeners() {
1451: return new LifecycleListener[0];
1452: }
1453:
1454: /**
1455: * Remove a lifecycle event listener from this component.
1456: *
1457: * @param listener The listener to remove
1458: */
1459: public void removeLifecycleListener(LifecycleListener listener) {
1460: }
1461:
1462: /**
1463: * Start the class loader.
1464: *
1465: * @exception LifecycleException if a lifecycle error occurs
1466: */
1467: public void start() throws LifecycleException {
1468:
1469: started = true;
1470:
1471: }
1472:
1473: /**
1474: * Stop the class loader.
1475: *
1476: * @exception LifecycleException if a lifecycle error occurs
1477: */
1478: public void stop() throws LifecycleException {
1479:
1480: started = false;
1481:
1482: int length = jarFiles.length;
1483: for (int i = 0; i < length; i++) {
1484: try {
1485: jarFiles[i].close();
1486: jarFiles[i] = null;
1487: } catch (IOException e) {
1488: // Ignore
1489: }
1490: }
1491:
1492: notFoundResources.clear();
1493: resourceEntries.clear();
1494: repositories = new String[0];
1495: files = new File[0];
1496: jarFiles = new JarFile[0];
1497: jarRealFiles = new File[0];
1498: jarPath = null;
1499: jarNames = new String[0];
1500: lastModifiedDates = new long[0];
1501: paths = new String[0];
1502: hasExternalRepositories = false;
1503:
1504: required.clear();
1505: permissionList.clear();
1506: loaderPC.clear();
1507:
1508: }
1509:
1510: // ------------------------------------------------------ Protected Methods
1511:
1512: /**
1513: * Find specified class in local repositories.
1514: *
1515: * @return the loaded class, or null if the class isn't found
1516: */
1517: protected Class findClassInternal(String name)
1518: throws ClassNotFoundException {
1519:
1520: if (!validate(name))
1521: throw new ClassNotFoundException(name);
1522:
1523: String tempPath = name.replace('.', '/');
1524: String classPath = tempPath + ".class";
1525:
1526: ResourceEntry entry = null;
1527:
1528: if (securityManager != null) {
1529: PrivilegedAction dp = new PrivilegedFindResource(name,
1530: classPath);
1531: entry = (ResourceEntry) AccessController.doPrivileged(dp);
1532: } else {
1533: entry = findResourceInternal(name, classPath);
1534: }
1535:
1536: if ((entry == null) || (entry.binaryContent == null))
1537: throw new ClassNotFoundException(name);
1538:
1539: Class clazz = entry.loadedClass;
1540: if (clazz != null)
1541: return clazz;
1542:
1543: // Looking up the package
1544: String packageName = null;
1545: int pos = name.lastIndexOf('.');
1546: if (pos != -1)
1547: packageName = name.substring(0, pos);
1548:
1549: Package pkg = null;
1550:
1551: if (packageName != null) {
1552:
1553: pkg = getPackage(packageName);
1554:
1555: // Define the package (if null)
1556: if (pkg == null) {
1557: if (entry.manifest == null) {
1558: definePackage(packageName, null, null, null, null,
1559: null, null, null);
1560: } else {
1561: definePackage(packageName, entry.manifest,
1562: entry.codeBase);
1563: }
1564: }
1565:
1566: }
1567:
1568: // Create the code source object
1569: CodeSource codeSource = new CodeSource(entry.codeBase,
1570: entry.certificates);
1571:
1572: if (securityManager != null) {
1573:
1574: // Checking sealing
1575: if (pkg != null) {
1576: boolean sealCheck = true;
1577: if (pkg.isSealed()) {
1578: sealCheck = pkg.isSealed(entry.codeBase);
1579: } else {
1580: sealCheck = (entry.manifest == null)
1581: || !isPackageSealed(packageName,
1582: entry.manifest);
1583: }
1584: if (!sealCheck)
1585: throw new SecurityException(
1586: "Sealing violation loading " + name
1587: + " : Package " + packageName
1588: + " is sealed.");
1589: }
1590:
1591: }
1592:
1593: if (entry.loadedClass == null) {
1594: synchronized (this ) {
1595: if (entry.loadedClass == null) {
1596: clazz = defineClass(name, entry.binaryContent, 0,
1597: entry.binaryContent.length, codeSource);
1598: entry.loadedClass = clazz;
1599: } else {
1600: clazz = entry.loadedClass;
1601: }
1602: }
1603: } else {
1604: clazz = entry.loadedClass;
1605: }
1606:
1607: return clazz;
1608:
1609: }
1610:
1611: /**
1612: * Find specified resource in local repositories.
1613: *
1614: * @return the loaded resource, or null if the resource isn't found
1615: */
1616: protected ResourceEntry findResourceInternal(String name,
1617: String path) {
1618:
1619: if (!started) {
1620: log("Lifecycle error : CL stopped");
1621: return null;
1622: }
1623:
1624: if ((name == null) || (path == null))
1625: return null;
1626:
1627: ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
1628: if (entry != null)
1629: return entry;
1630:
1631: int contentLength = -1;
1632: InputStream binaryStream = null;
1633:
1634: int jarFilesLength = jarFiles.length;
1635: int repositoriesLength = repositories.length;
1636:
1637: int i;
1638:
1639: Resource resource = null;
1640:
1641: for (i = 0; (entry == null) && (i < repositoriesLength); i++) {
1642: try {
1643:
1644: String fullPath = repositories[i] + path;
1645:
1646: Object lookupResult = resources.lookup(fullPath);
1647: if (lookupResult instanceof Resource) {
1648: resource = (Resource) lookupResult;
1649: }
1650:
1651: // Note : Not getting an exception here means the resource was
1652: // found
1653:
1654: entry = new ResourceEntry();
1655: try {
1656: entry.source = getURL(new File(files[i], path));
1657: entry.codeBase = entry.source;
1658: } catch (MalformedURLException e) {
1659: return null;
1660: }
1661: ResourceAttributes attributes = (ResourceAttributes) resources
1662: .getAttributes(fullPath);
1663: contentLength = (int) attributes.getContentLength();
1664: entry.lastModified = attributes.getLastModified();
1665:
1666: if (resource != null) {
1667:
1668: try {
1669: binaryStream = resource.streamContent();
1670: } catch (IOException e) {
1671: return null;
1672: }
1673:
1674: // Register the full path for modification checking
1675: // Note: Only syncing on a 'constant' object is needed
1676: synchronized (allPermission) {
1677:
1678: int j;
1679:
1680: long[] result2 = new long[lastModifiedDates.length + 1];
1681: for (j = 0; j < lastModifiedDates.length; j++) {
1682: result2[j] = lastModifiedDates[j];
1683: }
1684: result2[lastModifiedDates.length] = entry.lastModified;
1685: lastModifiedDates = result2;
1686:
1687: String[] result = new String[paths.length + 1];
1688: for (j = 0; j < paths.length; j++) {
1689: result[j] = paths[j];
1690: }
1691: result[paths.length] = fullPath;
1692: paths = result;
1693:
1694: }
1695:
1696: }
1697:
1698: } catch (NamingException e) {
1699: }
1700: }
1701:
1702: if ((entry == null) && (notFoundResources.containsKey(name)))
1703: return null;
1704:
1705: JarEntry jarEntry = null;
1706:
1707: for (i = 0; (entry == null) && (i < jarFilesLength); i++) {
1708:
1709: jarEntry = jarFiles[i].getJarEntry(path);
1710:
1711: if (jarEntry != null) {
1712:
1713: entry = new ResourceEntry();
1714: try {
1715: entry.codeBase = getURL(jarRealFiles[i]);
1716: String jarFakeUrl = entry.codeBase.toString();
1717: jarFakeUrl = "jar:" + jarFakeUrl + "!/" + path;
1718: entry.source = new URL(jarFakeUrl);
1719: } catch (MalformedURLException e) {
1720: return null;
1721: }
1722: contentLength = (int) jarEntry.getSize();
1723: try {
1724: entry.manifest = jarFiles[i].getManifest();
1725: binaryStream = jarFiles[i].getInputStream(jarEntry);
1726: } catch (IOException e) {
1727: return null;
1728: }
1729: }
1730:
1731: }
1732:
1733: if (entry == null) {
1734: synchronized (notFoundResources) {
1735: notFoundResources.put(name, name);
1736: }
1737: return null;
1738: }
1739:
1740: if (binaryStream != null) {
1741:
1742: byte[] binaryContent = new byte[contentLength];
1743:
1744: try {
1745: int pos = 0;
1746: while (true) {
1747: int n = binaryStream.read(binaryContent, pos,
1748: binaryContent.length - pos);
1749: if (n <= 0)
1750: break;
1751: pos += n;
1752: }
1753: binaryStream.close();
1754: } catch (IOException e) {
1755: e.printStackTrace();
1756: return null;
1757: } catch (Exception e) {
1758: e.printStackTrace();
1759: return null;
1760: }
1761:
1762: entry.binaryContent = binaryContent;
1763:
1764: // The certificates are only available after the JarEntry
1765: // associated input stream has been fully read
1766: if (jarEntry != null) {
1767: entry.certificates = jarEntry.getCertificates();
1768: }
1769:
1770: }
1771:
1772: // Add the entry in the local resource repository
1773: synchronized (resourceEntries) {
1774: // Ensures that all the threads which may be in a race to load
1775: // a particular class all end up with the same ResourceEntry
1776: // instance
1777: ResourceEntry entry2 = (ResourceEntry) resourceEntries
1778: .get(name);
1779: if (entry2 == null) {
1780: resourceEntries.put(name, entry);
1781: } else {
1782: entry = entry2;
1783: }
1784: }
1785:
1786: return entry;
1787:
1788: }
1789:
1790: /**
1791: * Returns true if the specified package name is sealed according to the
1792: * given manifest.
1793: */
1794: protected boolean isPackageSealed(String name, Manifest man) {
1795:
1796: String path = name + "/";
1797: Attributes attr = man.getAttributes(path);
1798: String sealed = null;
1799: if (attr != null) {
1800: sealed = attr.getValue(Name.SEALED);
1801: }
1802: if (sealed == null) {
1803: if ((attr = man.getMainAttributes()) != null) {
1804: sealed = attr.getValue(Name.SEALED);
1805: }
1806: }
1807: return "true".equalsIgnoreCase(sealed);
1808:
1809: }
1810:
1811: /**
1812: * Finds the resource with the given name if it has previously been
1813: * loaded and cached by this class loader, and return an input stream
1814: * to the resource data. If this resource has not been cached, return
1815: * <code>null</code>.
1816: *
1817: * @param name Name of the resource to return
1818: */
1819: protected InputStream findLoadedResource(String name) {
1820:
1821: ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
1822: if (entry != null) {
1823: if (entry.binaryContent != null)
1824: return new ByteArrayInputStream(entry.binaryContent);
1825: }
1826: return (null);
1827:
1828: }
1829:
1830: /**
1831: * Finds the class with the given name if it has previously been
1832: * loaded and cached by this class loader, and return the Class object.
1833: * If this class has not been cached, return <code>null</code>.
1834: *
1835: * @param name Name of the resource to return
1836: */
1837: protected Class findLoadedClass0(String name) {
1838:
1839: ResourceEntry entry = (ResourceEntry) resourceEntries.get(name);
1840: if (entry != null) {
1841: return entry.loadedClass;
1842: }
1843: return (null); // FIXME - findLoadedResource()
1844:
1845: }
1846:
1847: /**
1848: * Refresh the system policy file, to pick up eventual changes.
1849: */
1850: protected void refreshPolicy() {
1851:
1852: try {
1853: // The policy file may have been modified to adjust
1854: // permissions, so we're reloading it when loading or
1855: // reloading a Context
1856: Policy policy = Policy.getPolicy();
1857: policy.refresh();
1858: } catch (AccessControlException e) {
1859: // Some policy files may restrict this, even for the core,
1860: // so this exception is ignored
1861: }
1862:
1863: }
1864:
1865: /**
1866: * Filter classes.
1867: *
1868: * @param name class name
1869: * @return true if the class should be filtered
1870: */
1871: protected boolean filter(String name) {
1872:
1873: if (name == null)
1874: return false;
1875:
1876: // Looking up the package
1877: String packageName = null;
1878: int pos = name.lastIndexOf('.');
1879: if (pos != -1)
1880: packageName = name.substring(0, pos);
1881: else
1882: return false;
1883:
1884: for (int i = 0; i < packageTriggers.length; i++) {
1885: if (packageName.startsWith(packageTriggers[i]))
1886: return true;
1887: }
1888:
1889: return false;
1890:
1891: }
1892:
1893: /**
1894: * Validate a classname. As per SRV.9.7.2, we must restict loading of
1895: * classes from J2SE (java.*) and classes of the servlet API
1896: * (javax.servlet.*). That should enhance robustness and prevent a number
1897: * of user error (where an older version of servlet.jar would be present
1898: * in /WEB-INF/lib).
1899: *
1900: * @param name class name
1901: * @return true if the name is valid
1902: */
1903: protected boolean validate(String name) {
1904:
1905: if (name == null)
1906: return false;
1907: if (name.startsWith("java."))
1908: return false;
1909:
1910: return true;
1911:
1912: }
1913:
1914: /**
1915: * Check the specified JAR file, and return <code>true</code> if it does
1916: * not contain any of the trigger classes.
1917: *
1918: * @param jarFile The JAR file to be checked
1919: *
1920: * @exception IOException if an input/output error occurs
1921: */
1922: private boolean validateJarFile(File jarfile) throws IOException {
1923:
1924: if (triggers == null)
1925: return (true);
1926: JarFile jarFile = new JarFile(jarfile);
1927: for (int i = 0; i < triggers.length; i++) {
1928: Class clazz = null;
1929: try {
1930: if (parent != null) {
1931: clazz = parent.loadClass(triggers[i]);
1932: } else {
1933: clazz = Class.forName(triggers[i]);
1934: }
1935: } catch (Throwable t) {
1936: clazz = null;
1937: }
1938: if (clazz == null)
1939: continue;
1940: String name = triggers[i].replace('.', '/') + ".class";
1941: if (debug >= 2)
1942: log(" Checking for " + name);
1943: JarEntry jarEntry = jarFile.getJarEntry(name);
1944: if (jarEntry != null) {
1945: log("validateJarFile(" + jarfile
1946: + ") - jar not loaded. See Servlet Spec 2.3, "
1947: + "section 9.7.2. Offending class: " + name);
1948: jarFile.close();
1949: return (false);
1950: }
1951: }
1952: jarFile.close();
1953: return (true);
1954:
1955: }
1956:
1957: /**
1958: * Get URL.
1959: */
1960: protected URL getURL(File file) throws MalformedURLException {
1961:
1962: File realFile = file;
1963: try {
1964: realFile = realFile.getCanonicalFile();
1965: } catch (IOException e) {
1966: // Ignore
1967: }
1968: return new URL("file:" + realFile.getPath());
1969:
1970: }
1971:
1972: /**
1973: * Log a debugging output message.
1974: *
1975: * @param message Message to be logged
1976: */
1977: private void log(String message) {
1978:
1979: System.out.println("WebappClassLoader: " + message);
1980:
1981: }
1982:
1983: /**
1984: * Log a debugging output message with an exception.
1985: *
1986: * @param message Message to be logged
1987: * @param throwable Exception to be logged
1988: */
1989: private void log(String message, Throwable throwable) {
1990:
1991: System.out.println("WebappClassLoader: " + message);
1992: throwable.printStackTrace(System.out);
1993:
1994: }
1995:
1996: }
|