0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package org.apache.catalina.loader;
0019:
0020: import java.beans.PropertyChangeEvent;
0021: import java.beans.PropertyChangeListener;
0022: import java.beans.PropertyChangeSupport;
0023: import java.io.File;
0024: import java.io.FileOutputStream;
0025: import java.io.FilePermission;
0026: import java.io.IOException;
0027: import java.io.InputStream;
0028: import java.io.OutputStream;
0029: import java.lang.reflect.Constructor;
0030: import java.lang.reflect.Method;
0031: import java.net.MalformedURLException;
0032: import java.net.URL;
0033: import java.net.URLClassLoader;
0034: import java.net.URLStreamHandlerFactory;
0035: import java.util.ArrayList;
0036: import java.util.jar.JarFile;
0037:
0038: import javax.management.MBeanRegistration;
0039: import javax.management.MBeanServer;
0040: import javax.management.ObjectName;
0041: import javax.naming.Binding;
0042: import javax.naming.NameClassPair;
0043: import javax.naming.NamingEnumeration;
0044: import javax.naming.NamingException;
0045: import javax.naming.directory.DirContext;
0046: import javax.servlet.ServletContext;
0047:
0048: import org.apache.catalina.Container;
0049: import org.apache.catalina.Context;
0050: import org.apache.catalina.Engine;
0051: import org.apache.catalina.Globals;
0052: import org.apache.catalina.Lifecycle;
0053: import org.apache.catalina.LifecycleException;
0054: import org.apache.catalina.LifecycleListener;
0055: import org.apache.catalina.Loader;
0056: import org.apache.catalina.core.StandardContext;
0057: import org.apache.catalina.util.LifecycleSupport;
0058: import org.apache.catalina.util.StringManager;
0059: import org.apache.naming.resources.DirContextURLStreamHandler;
0060: import org.apache.naming.resources.DirContextURLStreamHandlerFactory;
0061: import org.apache.naming.resources.Resource;
0062: import org.apache.tomcat.util.modeler.Registry;
0063:
0064: /**
0065: * Classloader implementation which is specialized for handling web
0066: * applications in the most efficient way, while being Catalina aware (all
0067: * accesses to resources are made through the DirContext interface).
0068: * This class loader supports detection of modified
0069: * Java classes, which can be used to implement auto-reload support.
0070: * <p>
0071: * This class loader is configured by adding the pathnames of directories,
0072: * JAR files, and ZIP files with the <code>addRepository()</code> method,
0073: * prior to calling <code>start()</code>. When a new class is required,
0074: * these repositories will be consulted first to locate the class. If it
0075: * is not present, the system class loader will be used instead.
0076: *
0077: * @author Craig R. McClanahan
0078: * @author Remy Maucherat
0079: * @version $Revision: 505593 $ $Date: 2007-02-10 01:54:56 +0100 (sam., 10 févr. 2007) $
0080: */
0081:
0082: public class WebappLoader implements Lifecycle, Loader,
0083: PropertyChangeListener, MBeanRegistration {
0084:
0085: // ----------------------------------------------------------- Constructors
0086:
0087: /**
0088: * Construct a new WebappLoader with no defined parent class loader
0089: * (so that the actual parent will be the system class loader).
0090: */
0091: public WebappLoader() {
0092:
0093: this (null);
0094:
0095: }
0096:
0097: /**
0098: * Construct a new WebappLoader with the specified class loader
0099: * to be defined as the parent of the ClassLoader we ultimately create.
0100: *
0101: * @param parent The parent class loader
0102: */
0103: public WebappLoader(ClassLoader parent) {
0104: super ();
0105: this .parentClassLoader = parent;
0106: }
0107:
0108: // ----------------------------------------------------- Instance Variables
0109:
0110: /**
0111: * First load of the class.
0112: */
0113: private static boolean first = true;
0114:
0115: /**
0116: * The class loader being managed by this Loader component.
0117: */
0118: private WebappClassLoader classLoader = null;
0119:
0120: /**
0121: * The Container with which this Loader has been associated.
0122: */
0123: private Container container = null;
0124:
0125: /**
0126: * The "follow standard delegation model" flag that will be used to
0127: * configure our ClassLoader.
0128: */
0129: private boolean delegate = false;
0130:
0131: /**
0132: * The descriptive information about this Loader implementation.
0133: */
0134: private static final String info = "org.apache.catalina.loader.WebappLoader/1.0";
0135:
0136: /**
0137: * The lifecycle event support for this component.
0138: */
0139: protected LifecycleSupport lifecycle = new LifecycleSupport(this );
0140:
0141: /**
0142: * The Java class name of the ClassLoader implementation to be used.
0143: * This class should extend WebappClassLoader, otherwise, a different
0144: * loader implementation must be used.
0145: */
0146: private String loaderClass = "org.apache.catalina.loader.WebappClassLoader";
0147:
0148: /**
0149: * The parent class loader of the class loader we will create.
0150: */
0151: private ClassLoader parentClassLoader = null;
0152:
0153: /**
0154: * The reloadable flag for this Loader.
0155: */
0156: private boolean reloadable = false;
0157:
0158: /**
0159: * The set of repositories associated with this class loader.
0160: */
0161: private String repositories[] = new String[0];
0162:
0163: /**
0164: * The string manager for this package.
0165: */
0166: protected static final StringManager sm = StringManager
0167: .getManager(Constants.Package);
0168:
0169: /**
0170: * Has this component been started?
0171: */
0172: private boolean started = false;
0173:
0174: /**
0175: * The property change support for this component.
0176: */
0177: protected PropertyChangeSupport support = new PropertyChangeSupport(
0178: this );
0179:
0180: /**
0181: * Classpath set in the loader.
0182: */
0183: private String classpath = null;
0184:
0185: /**
0186: * Repositories that are set in the loader, for JMX.
0187: */
0188: private ArrayList loaderRepositories = null;
0189:
0190: // ------------------------------------------------------------- Properties
0191:
0192: /**
0193: * Return the Java class loader to be used by this Container.
0194: */
0195: public ClassLoader getClassLoader() {
0196:
0197: return ((ClassLoader) classLoader);
0198:
0199: }
0200:
0201: /**
0202: * Return the Container with which this Logger has been associated.
0203: */
0204: public Container getContainer() {
0205:
0206: return (container);
0207:
0208: }
0209:
0210: /**
0211: * Set the Container with which this Logger has been associated.
0212: *
0213: * @param container The associated Container
0214: */
0215: public void setContainer(Container container) {
0216:
0217: // Deregister from the old Container (if any)
0218: if ((this .container != null)
0219: && (this .container instanceof Context))
0220: ((Context) this .container)
0221: .removePropertyChangeListener(this );
0222:
0223: // Process this property change
0224: Container oldContainer = this .container;
0225: this .container = container;
0226: support.firePropertyChange("container", oldContainer,
0227: this .container);
0228:
0229: // Register with the new Container (if any)
0230: if ((this .container != null)
0231: && (this .container instanceof Context)) {
0232: setReloadable(((Context) this .container).getReloadable());
0233: ((Context) this .container).addPropertyChangeListener(this );
0234: }
0235:
0236: }
0237:
0238: /**
0239: * Return the "follow standard delegation model" flag used to configure
0240: * our ClassLoader.
0241: */
0242: public boolean getDelegate() {
0243:
0244: return (this .delegate);
0245:
0246: }
0247:
0248: /**
0249: * Set the "follow standard delegation model" flag used to configure
0250: * our ClassLoader.
0251: *
0252: * @param delegate The new flag
0253: */
0254: public void setDelegate(boolean delegate) {
0255:
0256: boolean oldDelegate = this .delegate;
0257: this .delegate = delegate;
0258: support.firePropertyChange("delegate",
0259: new Boolean(oldDelegate), new Boolean(this .delegate));
0260:
0261: }
0262:
0263: /**
0264: * Return descriptive information about this Loader implementation and
0265: * the corresponding version number, in the format
0266: * <code><description>/<version></code>.
0267: */
0268: public String getInfo() {
0269:
0270: return (info);
0271:
0272: }
0273:
0274: /**
0275: * Return the ClassLoader class name.
0276: */
0277: public String getLoaderClass() {
0278:
0279: return (this .loaderClass);
0280:
0281: }
0282:
0283: /**
0284: * Set the ClassLoader class name.
0285: *
0286: * @param loaderClass The new ClassLoader class name
0287: */
0288: public void setLoaderClass(String loaderClass) {
0289:
0290: this .loaderClass = loaderClass;
0291:
0292: }
0293:
0294: /**
0295: * Return the reloadable flag for this Loader.
0296: */
0297: public boolean getReloadable() {
0298:
0299: return (this .reloadable);
0300:
0301: }
0302:
0303: /**
0304: * Set the reloadable flag for this Loader.
0305: *
0306: * @param reloadable The new reloadable flag
0307: */
0308: public void setReloadable(boolean reloadable) {
0309:
0310: // Process this property change
0311: boolean oldReloadable = this .reloadable;
0312: this .reloadable = reloadable;
0313: support.firePropertyChange("reloadable", new Boolean(
0314: oldReloadable), new Boolean(this .reloadable));
0315:
0316: }
0317:
0318: // --------------------------------------------------------- Public Methods
0319:
0320: /**
0321: * Add a property change listener to this component.
0322: *
0323: * @param listener The listener to add
0324: */
0325: public void addPropertyChangeListener(
0326: PropertyChangeListener listener) {
0327:
0328: support.addPropertyChangeListener(listener);
0329:
0330: }
0331:
0332: /**
0333: * Add a new repository to the set of repositories for this class loader.
0334: *
0335: * @param repository Repository to be added
0336: */
0337: public void addRepository(String repository) {
0338:
0339: if (log.isDebugEnabled())
0340: log.debug(sm.getString("webappLoader.addRepository",
0341: repository));
0342:
0343: for (int i = 0; i < repositories.length; i++) {
0344: if (repository.equals(repositories[i]))
0345: return;
0346: }
0347: String results[] = new String[repositories.length + 1];
0348: for (int i = 0; i < repositories.length; i++)
0349: results[i] = repositories[i];
0350: results[repositories.length] = repository;
0351: repositories = results;
0352:
0353: if (started && (classLoader != null)) {
0354: classLoader.addRepository(repository);
0355: if (loaderRepositories != null)
0356: loaderRepositories.add(repository);
0357: setClassPath();
0358: }
0359:
0360: }
0361:
0362: /**
0363: * Execute a periodic task, such as reloading, etc. This method will be
0364: * invoked inside the classloading context of this container. Unexpected
0365: * throwables will be caught and logged.
0366: */
0367: public void backgroundProcess() {
0368: if (reloadable && modified()) {
0369: try {
0370: Thread.currentThread().setContextClassLoader(
0371: WebappLoader.class.getClassLoader());
0372: if (container instanceof StandardContext) {
0373: ((StandardContext) container).reload();
0374: }
0375: } finally {
0376: if (container.getLoader() != null) {
0377: Thread.currentThread().setContextClassLoader(
0378: container.getLoader().getClassLoader());
0379: }
0380: }
0381: } else {
0382: closeJARs(false);
0383: }
0384: }
0385:
0386: /**
0387: * Return the set of repositories defined for this class loader.
0388: * If none are defined, a zero-length array is returned.
0389: * For security reason, returns a clone of the Array (since
0390: * String are immutable).
0391: */
0392: public String[] findRepositories() {
0393:
0394: return ((String[]) repositories.clone());
0395:
0396: }
0397:
0398: public String[] getRepositories() {
0399: return ((String[]) repositories.clone());
0400: }
0401:
0402: /** Extra repositories for this loader
0403: */
0404: public String getRepositoriesString() {
0405: StringBuffer sb = new StringBuffer();
0406: for (int i = 0; i < repositories.length; i++) {
0407: sb.append(repositories[i]).append(":");
0408: }
0409: return sb.toString();
0410: }
0411:
0412: public String[] getLoaderRepositories() {
0413: if (loaderRepositories == null)
0414: return null;
0415: String res[] = new String[loaderRepositories.size()];
0416: loaderRepositories.toArray(res);
0417: return res;
0418: }
0419:
0420: public String getLoaderRepositoriesString() {
0421: String repositories[] = getLoaderRepositories();
0422: StringBuffer sb = new StringBuffer();
0423: for (int i = 0; i < repositories.length; i++) {
0424: sb.append(repositories[i]).append(":");
0425: }
0426: return sb.toString();
0427: }
0428:
0429: /**
0430: * Classpath, as set in org.apache.catalina.jsp_classpath context
0431: * property
0432: *
0433: * @return The classpath
0434: */
0435: public String getClasspath() {
0436: return classpath;
0437: }
0438:
0439: /**
0440: * Has the internal repository associated with this Loader been modified,
0441: * such that the loaded classes should be reloaded?
0442: */
0443: public boolean modified() {
0444:
0445: return (classLoader.modified());
0446:
0447: }
0448:
0449: /**
0450: * Used to periodically signal to the classloader to release JAR resources.
0451: */
0452: public void closeJARs(boolean force) {
0453: if (classLoader != null) {
0454: classLoader.closeJARs(force);
0455: }
0456: }
0457:
0458: /**
0459: * Remove a property change listener from this component.
0460: *
0461: * @param listener The listener to remove
0462: */
0463: public void removePropertyChangeListener(
0464: PropertyChangeListener listener) {
0465:
0466: support.removePropertyChangeListener(listener);
0467:
0468: }
0469:
0470: /**
0471: * Return a String representation of this component.
0472: */
0473: public String toString() {
0474:
0475: StringBuffer sb = new StringBuffer("WebappLoader[");
0476: if (container != null)
0477: sb.append(container.getName());
0478: sb.append("]");
0479: return (sb.toString());
0480:
0481: }
0482:
0483: // ------------------------------------------------------ Lifecycle Methods
0484:
0485: /**
0486: * Add a lifecycle event listener to this component.
0487: *
0488: * @param listener The listener to add
0489: */
0490: public void addLifecycleListener(LifecycleListener listener) {
0491:
0492: lifecycle.addLifecycleListener(listener);
0493:
0494: }
0495:
0496: /**
0497: * Get the lifecycle listeners associated with this lifecycle. If this
0498: * Lifecycle has no listeners registered, a zero-length array is returned.
0499: */
0500: public LifecycleListener[] findLifecycleListeners() {
0501:
0502: return lifecycle.findLifecycleListeners();
0503:
0504: }
0505:
0506: /**
0507: * Remove a lifecycle event listener from this component.
0508: *
0509: * @param listener The listener to remove
0510: */
0511: public void removeLifecycleListener(LifecycleListener listener) {
0512:
0513: lifecycle.removeLifecycleListener(listener);
0514:
0515: }
0516:
0517: private boolean initialized = false;
0518:
0519: public void init() {
0520: initialized = true;
0521:
0522: if (oname == null) {
0523: // not registered yet - standalone or API
0524: if (container instanceof StandardContext) {
0525: // Register ourself. The container must be a webapp
0526: try {
0527: StandardContext ctx = (StandardContext) container;
0528: Engine eng = (Engine) ctx.getParent().getParent();
0529: String path = ctx.getPath();
0530: if (path.equals("")) {
0531: path = "/";
0532: }
0533: oname = new ObjectName(ctx.getEngineName()
0534: + ":type=Loader,path=" + path + ",host="
0535: + ctx.getParent().getName());
0536: Registry.getRegistry(null, null).registerComponent(
0537: this , oname, null);
0538: controller = oname;
0539: } catch (Exception e) {
0540: log.error("Error registering loader", e);
0541: }
0542: }
0543: }
0544:
0545: if (container == null) {
0546: // JMX created the loader
0547: // TODO
0548:
0549: }
0550: }
0551:
0552: public void destroy() {
0553: if (controller == oname) {
0554: // Self-registration, undo it
0555: Registry.getRegistry(null, null).unregisterComponent(oname);
0556: oname = null;
0557: }
0558: initialized = false;
0559:
0560: }
0561:
0562: /**
0563: * Start this component, initializing our associated class loader.
0564: *
0565: * @exception LifecycleException if a lifecycle error occurs
0566: */
0567: public void start() throws LifecycleException {
0568: // Validate and update our current component state
0569: if (!initialized)
0570: init();
0571: if (started)
0572: throw new LifecycleException(sm
0573: .getString("webappLoader.alreadyStarted"));
0574: if (log.isDebugEnabled())
0575: log.debug(sm.getString("webappLoader.starting"));
0576: lifecycle.fireLifecycleEvent(START_EVENT, null);
0577: started = true;
0578:
0579: if (container.getResources() == null) {
0580: log.info("No resources for " + container);
0581: return;
0582: }
0583: // Register a stream handler factory for the JNDI protocol
0584: URLStreamHandlerFactory streamHandlerFactory = new DirContextURLStreamHandlerFactory();
0585: if (first) {
0586: first = false;
0587: try {
0588: URL.setURLStreamHandlerFactory(streamHandlerFactory);
0589: } catch (Exception e) {
0590: // Log and continue anyway, this is not critical
0591: log.error("Error registering jndi stream handler", e);
0592: } catch (Throwable t) {
0593: // This is likely a dual registration
0594: log.info("Dual registration of jndi stream handler: "
0595: + t.getMessage());
0596: }
0597: }
0598:
0599: // Construct a class loader based on our current repositories list
0600: try {
0601:
0602: classLoader = createClassLoader();
0603: classLoader.setResources(container.getResources());
0604: classLoader.setDelegate(this .delegate);
0605: if (container instanceof StandardContext)
0606: classLoader
0607: .setAntiJARLocking(((StandardContext) container)
0608: .getAntiJARLocking());
0609:
0610: for (int i = 0; i < repositories.length; i++) {
0611: classLoader.addRepository(repositories[i]);
0612: }
0613:
0614: // Configure our repositories
0615: setRepositories();
0616: setClassPath();
0617:
0618: setPermissions();
0619:
0620: if (classLoader instanceof Lifecycle)
0621: ((Lifecycle) classLoader).start();
0622:
0623: // Binding the Webapp class loader to the directory context
0624: DirContextURLStreamHandler.bind((ClassLoader) classLoader,
0625: this .container.getResources());
0626:
0627: StandardContext ctx = (StandardContext) container;
0628: Engine eng = (Engine) ctx.getParent().getParent();
0629: String path = ctx.getPath();
0630: if (path.equals("")) {
0631: path = "/";
0632: }
0633: ObjectName cloname = new ObjectName(ctx.getEngineName()
0634: + ":type=WebappClassLoader,path=" + path + ",host="
0635: + ctx.getParent().getName());
0636: Registry.getRegistry(null, null).registerComponent(
0637: classLoader, cloname, null);
0638:
0639: } catch (Throwable t) {
0640: log.error("LifecycleException ", t);
0641: throw new LifecycleException("start: ", t);
0642: }
0643:
0644: }
0645:
0646: /**
0647: * Stop this component, finalizing our associated class loader.
0648: *
0649: * @exception LifecycleException if a lifecycle error occurs
0650: */
0651: public void stop() throws LifecycleException {
0652:
0653: // Validate and update our current component state
0654: if (!started)
0655: throw new LifecycleException(sm
0656: .getString("webappLoader.notStarted"));
0657: if (log.isDebugEnabled())
0658: log.debug(sm.getString("webappLoader.stopping"));
0659: lifecycle.fireLifecycleEvent(STOP_EVENT, null);
0660: started = false;
0661:
0662: // Remove context attributes as appropriate
0663: if (container instanceof Context) {
0664: ServletContext servletContext = ((Context) container)
0665: .getServletContext();
0666: servletContext.removeAttribute(Globals.CLASS_PATH_ATTR);
0667: }
0668:
0669: // Throw away our current class loader
0670: if (classLoader instanceof Lifecycle)
0671: ((Lifecycle) classLoader).stop();
0672: DirContextURLStreamHandler.unbind((ClassLoader) classLoader);
0673:
0674: try {
0675: StandardContext ctx = (StandardContext) container;
0676: Engine eng = (Engine) ctx.getParent().getParent();
0677: String path = ctx.getPath();
0678: if (path.equals("")) {
0679: path = "/";
0680: }
0681: ObjectName cloname = new ObjectName(ctx.getEngineName()
0682: + ":type=WebappClassLoader,path=" + path + ",host="
0683: + ctx.getParent().getName());
0684: Registry.getRegistry(null, null).unregisterComponent(
0685: cloname);
0686: } catch (Throwable t) {
0687: log.error("LifecycleException ", t);
0688: }
0689:
0690: classLoader = null;
0691:
0692: destroy();
0693:
0694: }
0695:
0696: // ----------------------------------------- PropertyChangeListener Methods
0697:
0698: /**
0699: * Process property change events from our associated Context.
0700: *
0701: * @param event The property change event that has occurred
0702: */
0703: public void propertyChange(PropertyChangeEvent event) {
0704:
0705: // Validate the source of this event
0706: if (!(event.getSource() instanceof Context))
0707: return;
0708: Context context = (Context) event.getSource();
0709:
0710: // Process a relevant property change
0711: if (event.getPropertyName().equals("reloadable")) {
0712: try {
0713: setReloadable(((Boolean) event.getNewValue())
0714: .booleanValue());
0715: } catch (NumberFormatException e) {
0716: log.error(sm.getString("webappLoader.reloadable", event
0717: .getNewValue().toString()));
0718: }
0719: }
0720:
0721: }
0722:
0723: // ------------------------------------------------------- Private Methods
0724:
0725: /**
0726: * Create associated classLoader.
0727: */
0728: private WebappClassLoader createClassLoader() throws Exception {
0729:
0730: Class clazz = Class.forName(loaderClass);
0731: WebappClassLoader classLoader = null;
0732:
0733: if (parentClassLoader == null) {
0734: parentClassLoader = container.getParentClassLoader();
0735: }
0736: Class[] argTypes = { ClassLoader.class };
0737: Object[] args = { parentClassLoader };
0738: Constructor constr = clazz.getConstructor(argTypes);
0739: classLoader = (WebappClassLoader) constr.newInstance(args);
0740:
0741: return classLoader;
0742:
0743: }
0744:
0745: /**
0746: * Configure associated class loader permissions.
0747: */
0748: private void setPermissions() {
0749:
0750: if (!Globals.IS_SECURITY_ENABLED)
0751: return;
0752: if (!(container instanceof Context))
0753: return;
0754:
0755: // Tell the class loader the root of the context
0756: ServletContext servletContext = ((Context) container)
0757: .getServletContext();
0758:
0759: // Assigning permissions for the work directory
0760: File workDir = (File) servletContext
0761: .getAttribute(Globals.WORK_DIR_ATTR);
0762: if (workDir != null) {
0763: try {
0764: String workDirPath = workDir.getCanonicalPath();
0765: classLoader.addPermission(new FilePermission(
0766: workDirPath, "read,write"));
0767: classLoader.addPermission(new FilePermission(
0768: workDirPath + File.separator + "-",
0769: "read,write,delete"));
0770: } catch (IOException e) {
0771: // Ignore
0772: }
0773: }
0774:
0775: try {
0776:
0777: URL rootURL = servletContext.getResource("/");
0778: classLoader.addPermission(rootURL);
0779:
0780: String contextRoot = servletContext.getRealPath("/");
0781: if (contextRoot != null) {
0782: try {
0783: contextRoot = (new File(contextRoot))
0784: .getCanonicalPath();
0785: classLoader.addPermission(contextRoot);
0786: } catch (IOException e) {
0787: // Ignore
0788: }
0789: }
0790:
0791: URL classesURL = servletContext
0792: .getResource("/WEB-INF/classes/");
0793: classLoader.addPermission(classesURL);
0794: URL libURL = servletContext.getResource("/WEB-INF/lib/");
0795: classLoader.addPermission(libURL);
0796:
0797: if (contextRoot != null) {
0798:
0799: if (libURL != null) {
0800: File rootDir = new File(contextRoot);
0801: File libDir = new File(rootDir, "WEB-INF/lib/");
0802: try {
0803: String path = libDir.getCanonicalPath();
0804: classLoader.addPermission(path);
0805: } catch (IOException e) {
0806: }
0807: }
0808:
0809: } else {
0810:
0811: if (workDir != null) {
0812: if (libURL != null) {
0813: File libDir = new File(workDir, "WEB-INF/lib/");
0814: try {
0815: String path = libDir.getCanonicalPath();
0816: classLoader.addPermission(path);
0817: } catch (IOException e) {
0818: }
0819: }
0820: if (classesURL != null) {
0821: File classesDir = new File(workDir,
0822: "WEB-INF/classes/");
0823: try {
0824: String path = classesDir.getCanonicalPath();
0825: classLoader.addPermission(path);
0826: } catch (IOException e) {
0827: }
0828: }
0829: }
0830:
0831: }
0832:
0833: } catch (MalformedURLException e) {
0834: }
0835:
0836: }
0837:
0838: /**
0839: * Configure the repositories for our class loader, based on the
0840: * associated Context.
0841: */
0842: private void setRepositories() {
0843:
0844: if (!(container instanceof Context))
0845: return;
0846: ServletContext servletContext = ((Context) container)
0847: .getServletContext();
0848: if (servletContext == null)
0849: return;
0850:
0851: loaderRepositories = new ArrayList();
0852: // Loading the work directory
0853: File workDir = (File) servletContext
0854: .getAttribute(Globals.WORK_DIR_ATTR);
0855: if (workDir == null) {
0856: log.info("No work dir for " + servletContext);
0857: }
0858:
0859: if (log.isDebugEnabled())
0860: log.debug(sm.getString("webappLoader.deploy", workDir
0861: .getAbsolutePath()));
0862:
0863: classLoader.setWorkDir(workDir);
0864:
0865: DirContext resources = container.getResources();
0866:
0867: // Setting up the class repository (/WEB-INF/classes), if it exists
0868:
0869: String classesPath = "/WEB-INF/classes";
0870: DirContext classes = null;
0871:
0872: try {
0873: Object object = resources.lookup(classesPath);
0874: if (object instanceof DirContext) {
0875: classes = (DirContext) object;
0876: }
0877: } catch (NamingException e) {
0878: // Silent catch: it's valid that no /WEB-INF/classes collection
0879: // exists
0880: }
0881:
0882: if (classes != null) {
0883:
0884: File classRepository = null;
0885:
0886: String absoluteClassesPath = servletContext
0887: .getRealPath(classesPath);
0888:
0889: if (absoluteClassesPath != null) {
0890:
0891: classRepository = new File(absoluteClassesPath);
0892:
0893: } else {
0894:
0895: classRepository = new File(workDir, classesPath);
0896: classRepository.mkdirs();
0897: copyDir(classes, classRepository);
0898:
0899: }
0900:
0901: if (log.isDebugEnabled())
0902: log
0903: .debug(sm.getString("webappLoader.classDeploy",
0904: classesPath, classRepository
0905: .getAbsolutePath()));
0906:
0907: // Adding the repository to the class loader
0908: classLoader.addRepository(classesPath + "/",
0909: classRepository);
0910: loaderRepositories.add(classesPath + "/");
0911:
0912: }
0913:
0914: // Setting up the JAR repository (/WEB-INF/lib), if it exists
0915:
0916: String libPath = "/WEB-INF/lib";
0917:
0918: classLoader.setJarPath(libPath);
0919:
0920: DirContext libDir = null;
0921: // Looking up directory /WEB-INF/lib in the context
0922: try {
0923: Object object = resources.lookup(libPath);
0924: if (object instanceof DirContext)
0925: libDir = (DirContext) object;
0926: } catch (NamingException e) {
0927: // Silent catch: it's valid that no /WEB-INF/lib collection
0928: // exists
0929: }
0930:
0931: if (libDir != null) {
0932:
0933: boolean copyJars = false;
0934: String absoluteLibPath = servletContext
0935: .getRealPath(libPath);
0936:
0937: File destDir = null;
0938:
0939: if (absoluteLibPath != null) {
0940: destDir = new File(absoluteLibPath);
0941: } else {
0942: copyJars = true;
0943: destDir = new File(workDir, libPath);
0944: destDir.mkdirs();
0945: }
0946:
0947: // Looking up directory /WEB-INF/lib in the context
0948: try {
0949: NamingEnumeration enumeration = resources
0950: .listBindings(libPath);
0951: while (enumeration.hasMoreElements()) {
0952:
0953: Binding binding = (Binding) enumeration
0954: .nextElement();
0955: String filename = libPath + "/" + binding.getName();
0956: if (!filename.endsWith(".jar"))
0957: continue;
0958:
0959: // Copy JAR in the work directory, always (the JAR file
0960: // would get locked otherwise, which would make it
0961: // impossible to update it or remove it at runtime)
0962: File destFile = new File(destDir, binding.getName());
0963:
0964: if (log.isDebugEnabled())
0965: log.debug(sm.getString(
0966: "webappLoader.jarDeploy", filename,
0967: destFile.getAbsolutePath()));
0968:
0969: Resource jarResource = (Resource) binding
0970: .getObject();
0971: if (copyJars) {
0972: if (!copy(jarResource.streamContent(),
0973: new FileOutputStream(destFile)))
0974: continue;
0975: }
0976:
0977: try {
0978: JarFile jarFile = new JarFile(destFile);
0979: classLoader.addJar(filename, jarFile, destFile);
0980: } catch (Exception ex) {
0981: // Catch the exception if there is an empty jar file
0982: // Should ignore and continute loading other jar files
0983: // in the dir
0984: }
0985:
0986: loaderRepositories.add(filename);
0987:
0988: }
0989: } catch (NamingException e) {
0990: // Silent catch: it's valid that no /WEB-INF/lib directory
0991: // exists
0992: } catch (IOException e) {
0993: e.printStackTrace();
0994: }
0995:
0996: }
0997:
0998: }
0999:
1000: /**
1001: * Set the appropriate context attribute for our class path. This
1002: * is required only because Jasper depends on it.
1003: */
1004: private void setClassPath() {
1005:
1006: // Validate our current state information
1007: if (!(container instanceof Context))
1008: return;
1009: ServletContext servletContext = ((Context) container)
1010: .getServletContext();
1011: if (servletContext == null)
1012: return;
1013:
1014: if (container instanceof StandardContext) {
1015: String baseClasspath = ((StandardContext) container)
1016: .getCompilerClasspath();
1017: if (baseClasspath != null) {
1018: servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
1019: baseClasspath);
1020: return;
1021: }
1022: }
1023:
1024: StringBuffer classpath = new StringBuffer();
1025:
1026: // Assemble the class path information from our class loader chain
1027: ClassLoader loader = getClassLoader();
1028: int layers = 0;
1029: int n = 0;
1030: while (loader != null) {
1031: if (!(loader instanceof URLClassLoader)) {
1032: String cp = getClasspath(loader);
1033: if (cp == null) {
1034: log.info("Unknown loader " + loader + " "
1035: + loader.getClass());
1036: break;
1037: } else {
1038: if (n > 0)
1039: classpath.append(File.pathSeparator);
1040: classpath.append(cp);
1041: n++;
1042: }
1043: break;
1044: //continue;
1045: }
1046: URL repositories[] = ((URLClassLoader) loader).getURLs();
1047: for (int i = 0; i < repositories.length; i++) {
1048: String repository = repositories[i].toString();
1049: if (repository.startsWith("file://"))
1050: repository = repository.substring(7);
1051: else if (repository.startsWith("file:"))
1052: repository = repository.substring(5);
1053: else if (repository.startsWith("jndi:"))
1054: repository = servletContext.getRealPath(repository
1055: .substring(5));
1056: else
1057: continue;
1058: if (repository == null)
1059: continue;
1060: if (n > 0)
1061: classpath.append(File.pathSeparator);
1062: classpath.append(repository);
1063: n++;
1064: }
1065: loader = loader.getParent();
1066: layers++;
1067: }
1068:
1069: this .classpath = classpath.toString();
1070:
1071: // Store the assembled class path as a servlet context attribute
1072: servletContext.setAttribute(Globals.CLASS_PATH_ATTR, classpath
1073: .toString());
1074:
1075: }
1076:
1077: // try to extract the classpath from a loader that is not URLClassLoader
1078: private String getClasspath(ClassLoader loader) {
1079: try {
1080: Method m = loader.getClass().getMethod("getClasspath",
1081: new Class[] {});
1082: if (log.isTraceEnabled())
1083: log.trace("getClasspath " + m);
1084: if (m == null)
1085: return null;
1086: Object o = m.invoke(loader, new Object[] {});
1087: if (log.isDebugEnabled())
1088: log.debug("gotClasspath " + o);
1089: if (o instanceof String)
1090: return (String) o;
1091: return null;
1092: } catch (Exception ex) {
1093: if (log.isDebugEnabled())
1094: log.debug("getClasspath ", ex);
1095: }
1096: return null;
1097: }
1098:
1099: /**
1100: * Copy directory.
1101: */
1102: private boolean copyDir(DirContext srcDir, File destDir) {
1103:
1104: try {
1105:
1106: NamingEnumeration enumeration = srcDir.list("");
1107: while (enumeration.hasMoreElements()) {
1108: NameClassPair ncPair = (NameClassPair) enumeration
1109: .nextElement();
1110: String name = ncPair.getName();
1111: Object object = srcDir.lookup(name);
1112: File currentFile = new File(destDir, name);
1113: if (object instanceof Resource) {
1114: InputStream is = ((Resource) object)
1115: .streamContent();
1116: OutputStream os = new FileOutputStream(currentFile);
1117: if (!copy(is, os))
1118: return false;
1119: } else if (object instanceof InputStream) {
1120: OutputStream os = new FileOutputStream(currentFile);
1121: if (!copy((InputStream) object, os))
1122: return false;
1123: } else if (object instanceof DirContext) {
1124: currentFile.mkdir();
1125: copyDir((DirContext) object, currentFile);
1126: }
1127: }
1128:
1129: } catch (NamingException e) {
1130: return false;
1131: } catch (IOException e) {
1132: return false;
1133: }
1134:
1135: return true;
1136:
1137: }
1138:
1139: /**
1140: * Copy a file to the specified temp directory. This is required only
1141: * because Jasper depends on it.
1142: */
1143: private boolean copy(InputStream is, OutputStream os) {
1144:
1145: try {
1146: byte[] buf = new byte[4096];
1147: while (true) {
1148: int len = is.read(buf);
1149: if (len < 0)
1150: break;
1151: os.write(buf, 0, len);
1152: }
1153: is.close();
1154: os.close();
1155: } catch (IOException e) {
1156: return false;
1157: }
1158:
1159: return true;
1160:
1161: }
1162:
1163: private static org.apache.juli.logging.Log log = org.apache.juli.logging.LogFactory
1164: .getLog(WebappLoader.class);
1165:
1166: private ObjectName oname;
1167: private MBeanServer mserver;
1168: private String domain;
1169: private ObjectName controller;
1170:
1171: public ObjectName preRegister(MBeanServer server, ObjectName name)
1172: throws Exception {
1173: oname = name;
1174: mserver = server;
1175: domain = name.getDomain();
1176:
1177: return name;
1178: }
1179:
1180: public void postRegister(Boolean registrationDone) {
1181: }
1182:
1183: public void preDeregister() throws Exception {
1184: }
1185:
1186: public void postDeregister() {
1187: }
1188:
1189: public ObjectName getController() {
1190: return controller;
1191: }
1192:
1193: public void setController(ObjectName controller) {
1194: this.controller = controller;
1195: }
1196:
1197: }
|