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