Save This Page
Home » apache-tomcat-6.0.26-src » org.apache » catalina » loader » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   
   19   package org.apache.catalina.loader;
   20   
   21   
   22   import java.beans.PropertyChangeEvent;
   23   import java.beans.PropertyChangeListener;
   24   import java.beans.PropertyChangeSupport;
   25   import java.io.File;
   26   import java.io.FileOutputStream;
   27   import java.io.FilePermission;
   28   import java.io.IOException;
   29   import java.io.InputStream;
   30   import java.io.OutputStream;
   31   import java.lang.reflect.Constructor;
   32   import java.lang.reflect.Method;
   33   import java.net.MalformedURLException;
   34   import java.net.URL;
   35   import java.net.URLClassLoader;
   36   import java.net.URLStreamHandlerFactory;
   37   import java.util.ArrayList;
   38   import java.util.jar.JarFile;
   39   
   40   import javax.management.MBeanRegistration;
   41   import javax.management.MBeanServer;
   42   import javax.management.ObjectName;
   43   import javax.naming.NameClassPair;
   44   import javax.naming.NamingEnumeration;
   45   import javax.naming.NamingException;
   46   import javax.naming.directory.DirContext;
   47   import javax.servlet.ServletContext;
   48   
   49   import org.apache.catalina.Container;
   50   import org.apache.catalina.Context;
   51   import org.apache.catalina.Engine;
   52   import org.apache.catalina.Globals;
   53   import org.apache.catalina.Lifecycle;
   54   import org.apache.catalina.LifecycleException;
   55   import org.apache.catalina.LifecycleListener;
   56   import org.apache.catalina.Loader;
   57   import org.apache.catalina.core.StandardContext;
   58   import org.apache.catalina.util.LifecycleSupport;
   59   import org.apache.catalina.util.StringManager;
   60   import org.apache.naming.resources.DirContextURLStreamHandler;
   61   import org.apache.naming.resources.DirContextURLStreamHandlerFactory;
   62   import org.apache.naming.resources.Resource;
   63   import org.apache.tomcat.util.modeler.Registry;
   64   
   65   
   66   /**
   67    * Classloader implementation which is specialized for handling web
   68    * applications in the most efficient way, while being Catalina aware (all
   69    * accesses to resources are made through the DirContext interface).
   70    * This class loader supports detection of modified
   71    * Java classes, which can be used to implement auto-reload support.
   72    * <p>
   73    * This class loader is configured by adding the pathnames of directories,
   74    * JAR files, and ZIP files with the <code>addRepository()</code> method,
   75    * prior to calling <code>start()</code>.  When a new class is required,
   76    * these repositories will be consulted first to locate the class.  If it
   77    * is not present, the system class loader will be used instead.
   78    *
   79    * @author Craig R. McClanahan
   80    * @author Remy Maucherat
   81    * @version $Revision: 893564 $ $Date: 2009-12-23 17:31:31 +0100 (Wed, 23 Dec 2009) $
   82    */
   83   
   84   public class WebappLoader
   85       implements Lifecycle, Loader, PropertyChangeListener, MBeanRegistration  {
   86   
   87       // ----------------------------------------------------------- Constructors
   88   
   89   
   90       /**
   91        * Construct a new WebappLoader with no defined parent class loader
   92        * (so that the actual parent will be the system class loader).
   93        */
   94       public WebappLoader() {
   95   
   96           this(null);
   97   
   98       }
   99   
  100   
  101       /**
  102        * Construct a new WebappLoader with the specified class loader
  103        * to be defined as the parent of the ClassLoader we ultimately create.
  104        *
  105        * @param parent The parent class loader
  106        */
  107       public WebappLoader(ClassLoader parent) {
  108           super();
  109           this.parentClassLoader = parent;
  110       }
  111   
  112   
  113       // ----------------------------------------------------- Instance Variables
  114   
  115   
  116       /**
  117        * First load of the class.
  118        */
  119       private static boolean first = true;
  120   
  121   
  122       /**
  123        * The class loader being managed by this Loader component.
  124        */
  125       private WebappClassLoader classLoader = null;
  126   
  127   
  128       /**
  129        * The Container with which this Loader has been associated.
  130        */
  131       private Container container = null;
  132   
  133   
  134       /**
  135        * The "follow standard delegation model" flag that will be used to
  136        * configure our ClassLoader.
  137        */
  138       private boolean delegate = false;
  139   
  140   
  141       /**
  142        * The descriptive information about this Loader implementation.
  143        */
  144       private static final String info =
  145           "org.apache.catalina.loader.WebappLoader/1.0";
  146   
  147   
  148       /**
  149        * The lifecycle event support for this component.
  150        */
  151       protected LifecycleSupport lifecycle = new LifecycleSupport(this);
  152   
  153   
  154       /**
  155        * The Java class name of the ClassLoader implementation to be used.
  156        * This class should extend WebappClassLoader, otherwise, a different 
  157        * loader implementation must be used.
  158        */
  159       private String loaderClass =
  160           "org.apache.catalina.loader.WebappClassLoader";
  161   
  162   
  163       /**
  164        * The parent class loader of the class loader we will create.
  165        */
  166       private ClassLoader parentClassLoader = null;
  167   
  168   
  169       /**
  170        * The reloadable flag for this Loader.
  171        */
  172       private boolean reloadable = false;
  173   
  174   
  175       /**
  176        * The set of repositories associated with this class loader.
  177        */
  178       private String repositories[] = new String[0];
  179   
  180   
  181       /**
  182        * The string manager for this package.
  183        */
  184       protected static final StringManager sm =
  185           StringManager.getManager(Constants.Package);
  186   
  187   
  188       /**
  189        * Has this component been started?
  190        */
  191       private boolean started = false;
  192   
  193   
  194       /**
  195        * The property change support for this component.
  196        */
  197       protected PropertyChangeSupport support = new PropertyChangeSupport(this);
  198   
  199   
  200       /**
  201        * Classpath set in the loader.
  202        */
  203       private String classpath = null;
  204   
  205   
  206       /**
  207        * Repositories that are set in the loader, for JMX.
  208        */
  209       private ArrayList loaderRepositories = null;
  210   
  211   
  212       // ------------------------------------------------------------- Properties
  213   
  214   
  215       /**
  216        * Return the Java class loader to be used by this Container.
  217        */
  218       public ClassLoader getClassLoader() {
  219   
  220           return ((ClassLoader) classLoader);
  221   
  222       }
  223   
  224   
  225       /**
  226        * Return the Container with which this Logger has been associated.
  227        */
  228       public Container getContainer() {
  229   
  230           return (container);
  231   
  232       }
  233   
  234   
  235       /**
  236        * Set the Container with which this Logger has been associated.
  237        *
  238        * @param container The associated Container
  239        */
  240       public void setContainer(Container container) {
  241   
  242           // Deregister from the old Container (if any)
  243           if ((this.container != null) && (this.container instanceof Context))
  244               ((Context) this.container).removePropertyChangeListener(this);
  245   
  246           // Process this property change
  247           Container oldContainer = this.container;
  248           this.container = container;
  249           support.firePropertyChange("container", oldContainer, this.container);
  250   
  251           // Register with the new Container (if any)
  252           if ((this.container != null) && (this.container instanceof Context)) {
  253               setReloadable( ((Context) this.container).getReloadable() );
  254               ((Context) this.container).addPropertyChangeListener(this);
  255           }
  256   
  257       }
  258   
  259   
  260       /**
  261        * Return the "follow standard delegation model" flag used to configure
  262        * our ClassLoader.
  263        */
  264       public boolean getDelegate() {
  265   
  266           return (this.delegate);
  267   
  268       }
  269   
  270   
  271       /**
  272        * Set the "follow standard delegation model" flag used to configure
  273        * our ClassLoader.
  274        *
  275        * @param delegate The new flag
  276        */
  277       public void setDelegate(boolean delegate) {
  278   
  279           boolean oldDelegate = this.delegate;
  280           this.delegate = delegate;
  281           support.firePropertyChange("delegate", new Boolean(oldDelegate),
  282                                      new Boolean(this.delegate));
  283   
  284       }
  285   
  286   
  287       /**
  288        * Return descriptive information about this Loader implementation and
  289        * the corresponding version number, in the format
  290        * <code>&lt;description&gt;/&lt;version&gt;</code>.
  291        */
  292       public String getInfo() {
  293   
  294           return (info);
  295   
  296       }
  297   
  298   
  299       /**
  300        * Return the ClassLoader class name.
  301        */
  302       public String getLoaderClass() {
  303   
  304           return (this.loaderClass);
  305   
  306       }
  307   
  308   
  309       /**
  310        * Set the ClassLoader class name.
  311        *
  312        * @param loaderClass The new ClassLoader class name
  313        */
  314       public void setLoaderClass(String loaderClass) {
  315   
  316           this.loaderClass = loaderClass;
  317   
  318       }
  319   
  320   
  321       /**
  322        * Return the reloadable flag for this Loader.
  323        */
  324       public boolean getReloadable() {
  325   
  326           return (this.reloadable);
  327   
  328       }
  329   
  330   
  331       /**
  332        * Set the reloadable flag for this Loader.
  333        *
  334        * @param reloadable The new reloadable flag
  335        */
  336       public void setReloadable(boolean reloadable) {
  337   
  338           // Process this property change
  339           boolean oldReloadable = this.reloadable;
  340           this.reloadable = reloadable;
  341           support.firePropertyChange("reloadable",
  342                                      new Boolean(oldReloadable),
  343                                      new Boolean(this.reloadable));
  344   
  345       }
  346   
  347   
  348       // --------------------------------------------------------- Public Methods
  349   
  350   
  351       /**
  352        * Add a property change listener to this component.
  353        *
  354        * @param listener The listener to add
  355        */
  356       public void addPropertyChangeListener(PropertyChangeListener listener) {
  357   
  358           support.addPropertyChangeListener(listener);
  359   
  360       }
  361   
  362   
  363       /**
  364        * Add a new repository to the set of repositories for this class loader.
  365        *
  366        * @param repository Repository to be added
  367        */
  368       public void addRepository(String repository) {
  369   
  370           if (log.isDebugEnabled())
  371               log.debug(sm.getString("webappLoader.addRepository", repository));
  372   
  373           for (int i = 0; i < repositories.length; i++) {
  374               if (repository.equals(repositories[i]))
  375                   return;
  376           }
  377           String results[] = new String[repositories.length + 1];
  378           for (int i = 0; i < repositories.length; i++)
  379               results[i] = repositories[i];
  380           results[repositories.length] = repository;
  381           repositories = results;
  382   
  383           if (started && (classLoader != null)) {
  384               classLoader.addRepository(repository);
  385               if( loaderRepositories != null ) loaderRepositories.add(repository);
  386               setClassPath();
  387           }
  388   
  389       }
  390   
  391   
  392       /**
  393        * Execute a periodic task, such as reloading, etc. This method will be
  394        * invoked inside the classloading context of this container. Unexpected
  395        * throwables will be caught and logged.
  396        */
  397       public void backgroundProcess() {
  398           if (reloadable && modified()) {
  399               try {
  400                   Thread.currentThread().setContextClassLoader
  401                       (WebappLoader.class.getClassLoader());
  402                   if (container instanceof StandardContext) {
  403                       ((StandardContext) container).reload();
  404                   }
  405               } finally {
  406                   if (container.getLoader() != null) {
  407                       Thread.currentThread().setContextClassLoader
  408                           (container.getLoader().getClassLoader());
  409                   }
  410               }
  411           } else {
  412               closeJARs(false);
  413           }
  414       }
  415   
  416   
  417       /**
  418        * Return the set of repositories defined for this class loader.
  419        * If none are defined, a zero-length array is returned.
  420        * For security reason, returns a clone of the Array (since 
  421        * String are immutable).
  422        */
  423       public String[] findRepositories() {
  424   
  425           return ((String[])repositories.clone());
  426   
  427       }
  428   
  429       public String[] getRepositories() {
  430           return ((String[])repositories.clone());
  431       }
  432   
  433       /** Extra repositories for this loader
  434        */
  435       public String getRepositoriesString() {
  436           StringBuffer sb=new StringBuffer();
  437           for( int i=0; i<repositories.length ; i++ ) {
  438               sb.append( repositories[i]).append(":");
  439           }
  440           return sb.toString();
  441       }
  442   
  443       public String[] getLoaderRepositories() {
  444           if( loaderRepositories==null ) return  null;
  445           String res[]=new String[ loaderRepositories.size()];
  446           loaderRepositories.toArray(res);
  447           return res;
  448       }
  449   
  450       public String getLoaderRepositoriesString() {
  451           String repositories[]=getLoaderRepositories();
  452           StringBuffer sb=new StringBuffer();
  453           for( int i=0; i<repositories.length ; i++ ) {
  454               sb.append( repositories[i]).append(":");
  455           }
  456           return sb.toString();
  457       }
  458   
  459   
  460       /** 
  461        * Classpath, as set in org.apache.catalina.jsp_classpath context
  462        * property
  463        *
  464        * @return The classpath
  465        */
  466       public String getClasspath() {
  467           return classpath;
  468       }
  469   
  470   
  471       /**
  472        * Has the internal repository associated with this Loader been modified,
  473        * such that the loaded classes should be reloaded?
  474        */
  475       public boolean modified() {
  476   
  477           return (classLoader.modified());
  478   
  479       }
  480   
  481   
  482       /**
  483        * Used to periodically signal to the classloader to release JAR resources.
  484        */
  485       public void closeJARs(boolean force) {
  486           if (classLoader !=null){
  487               classLoader.closeJARs(force);
  488           }
  489       }
  490   
  491   
  492       /**
  493        * Remove a property change listener from this component.
  494        *
  495        * @param listener The listener to remove
  496        */
  497       public void removePropertyChangeListener(PropertyChangeListener listener) {
  498   
  499           support.removePropertyChangeListener(listener);
  500   
  501       }
  502   
  503   
  504       /**
  505        * Return a String representation of this component.
  506        */
  507       public String toString() {
  508   
  509           StringBuffer sb = new StringBuffer("WebappLoader[");
  510           if (container != null)
  511               sb.append(container.getName());
  512           sb.append("]");
  513           return (sb.toString());
  514   
  515       }
  516   
  517   
  518       // ------------------------------------------------------ Lifecycle Methods
  519   
  520   
  521       /**
  522        * Add a lifecycle event listener to this component.
  523        *
  524        * @param listener The listener to add
  525        */
  526       public void addLifecycleListener(LifecycleListener listener) {
  527   
  528           lifecycle.addLifecycleListener(listener);
  529   
  530       }
  531   
  532   
  533       /**
  534        * Get the lifecycle listeners associated with this lifecycle. If this 
  535        * Lifecycle has no listeners registered, a zero-length array is returned.
  536        */
  537       public LifecycleListener[] findLifecycleListeners() {
  538   
  539           return lifecycle.findLifecycleListeners();
  540   
  541       }
  542   
  543   
  544       /**
  545        * Remove a lifecycle event listener from this component.
  546        *
  547        * @param listener The listener to remove
  548        */
  549       public void removeLifecycleListener(LifecycleListener listener) {
  550   
  551           lifecycle.removeLifecycleListener(listener);
  552   
  553       }
  554   
  555       private boolean initialized=false;
  556   
  557       public void init() {
  558           initialized=true;
  559   
  560           if( oname==null ) {
  561               // not registered yet - standalone or API
  562               if( container instanceof StandardContext) {
  563                   // Register ourself. The container must be a webapp
  564                   try {
  565                       StandardContext ctx=(StandardContext)container;
  566                       Engine eng=(Engine)ctx.getParent().getParent();
  567                       String path = ctx.getPath();
  568                       if (path.equals("")) {
  569                           path = "/";
  570                       }   
  571                       oname=new ObjectName(ctx.getEngineName() + ":type=Loader,path=" +
  572                                   path + ",host=" + ctx.getParent().getName());
  573                       Registry.getRegistry(null, null).registerComponent(this, oname, null);
  574                       controller=oname;
  575                   } catch (Exception e) {
  576                       log.error("Error registering loader", e );
  577                   }
  578               }
  579           }
  580   
  581           if( container == null ) {
  582               // JMX created the loader
  583               // TODO
  584   
  585           }
  586       }
  587   
  588       public void destroy() {
  589           if( controller==oname ) {
  590               // Self-registration, undo it
  591               Registry.getRegistry(null, null).unregisterComponent(oname);
  592               oname = null;
  593           }
  594           initialized = false;
  595   
  596       }
  597   
  598       /**
  599        * Start this component, initializing our associated class loader.
  600        *
  601        * @exception LifecycleException if a lifecycle error occurs
  602        */
  603       public void start() throws LifecycleException {
  604           // Validate and update our current component state
  605           if( ! initialized ) init();
  606           if (started)
  607               throw new LifecycleException
  608                   (sm.getString("webappLoader.alreadyStarted"));
  609           if (log.isDebugEnabled())
  610               log.debug(sm.getString("webappLoader.starting"));
  611           lifecycle.fireLifecycleEvent(START_EVENT, null);
  612           started = true;
  613   
  614           if (container.getResources() == null) {
  615               log.info("No resources for " + container);
  616               return;
  617           }
  618           // Register a stream handler factory for the JNDI protocol
  619           URLStreamHandlerFactory streamHandlerFactory =
  620               new DirContextURLStreamHandlerFactory();
  621           if (first) {
  622               first = false;
  623               try {
  624                   URL.setURLStreamHandlerFactory(streamHandlerFactory);
  625               } catch (Exception e) {
  626                   // Log and continue anyway, this is not critical
  627                   log.error("Error registering jndi stream handler", e);
  628               } catch (Throwable t) {
  629                   // This is likely a dual registration
  630                   log.info("Dual registration of jndi stream handler: " 
  631                            + t.getMessage());
  632               }
  633           }
  634   
  635           // Construct a class loader based on our current repositories list
  636           try {
  637   
  638               classLoader = createClassLoader();
  639               classLoader.setResources(container.getResources());
  640               classLoader.setDelegate(this.delegate);
  641               if (container instanceof StandardContext) {
  642                   classLoader.setAntiJARLocking(
  643                           ((StandardContext) container).getAntiJARLocking());
  644                   classLoader.setClearReferencesStopThreads(
  645                           ((StandardContext) container).getClearReferencesStopThreads());
  646               }
  647   
  648               for (int i = 0; i < repositories.length; i++) {
  649                   classLoader.addRepository(repositories[i]);
  650               }
  651   
  652               // Configure our repositories
  653               setRepositories();
  654               setClassPath();
  655   
  656               setPermissions();
  657   
  658               if (classLoader instanceof Lifecycle)
  659                   ((Lifecycle) classLoader).start();
  660   
  661               // Binding the Webapp class loader to the directory context
  662               DirContextURLStreamHandler.bind
  663                   ((ClassLoader) classLoader, this.container.getResources());
  664   
  665               StandardContext ctx=(StandardContext)container;
  666               Engine eng=(Engine)ctx.getParent().getParent();
  667               String path = ctx.getPath();
  668               if (path.equals("")) {
  669                   path = "/";
  670               }   
  671               ObjectName cloname = new ObjectName
  672                   (ctx.getEngineName() + ":type=WebappClassLoader,path="
  673                    + path + ",host=" + ctx.getParent().getName());
  674               Registry.getRegistry(null, null)
  675                   .registerComponent(classLoader, cloname, null);
  676   
  677           } catch (Throwable t) {
  678               log.error( "LifecycleException ", t );
  679               throw new LifecycleException("start: ", t);
  680           }
  681   
  682       }
  683   
  684   
  685       /**
  686        * Stop this component, finalizing our associated class loader.
  687        *
  688        * @exception LifecycleException if a lifecycle error occurs
  689        */
  690       public void stop() throws LifecycleException {
  691   
  692           // Validate and update our current component state
  693           if (!started)
  694               throw new LifecycleException
  695                   (sm.getString("webappLoader.notStarted"));
  696           if (log.isDebugEnabled())
  697               log.debug(sm.getString("webappLoader.stopping"));
  698           lifecycle.fireLifecycleEvent(STOP_EVENT, null);
  699           started = false;
  700   
  701           // Remove context attributes as appropriate
  702           if (container instanceof Context) {
  703               ServletContext servletContext =
  704                   ((Context) container).getServletContext();
  705               servletContext.removeAttribute(Globals.CLASS_PATH_ATTR);
  706           }
  707   
  708           // Throw away our current class loader
  709           if (classLoader instanceof Lifecycle)
  710               ((Lifecycle) classLoader).stop();
  711           DirContextURLStreamHandler.unbind((ClassLoader) classLoader);
  712   
  713           try {
  714               StandardContext ctx=(StandardContext)container;
  715               Engine eng=(Engine)ctx.getParent().getParent();
  716               String path = ctx.getPath();
  717               if (path.equals("")) {
  718                   path = "/";
  719               }
  720               ObjectName cloname = new ObjectName
  721                   (ctx.getEngineName() + ":type=WebappClassLoader,path="
  722                    + path + ",host=" + ctx.getParent().getName());
  723               Registry.getRegistry(null, null).unregisterComponent(cloname);
  724           } catch (Throwable t) {
  725               log.error( "LifecycleException ", t );
  726           }
  727   
  728           classLoader = null;
  729   
  730           destroy();
  731   
  732       }
  733   
  734   
  735       // ----------------------------------------- PropertyChangeListener Methods
  736   
  737   
  738       /**
  739        * Process property change events from our associated Context.
  740        *
  741        * @param event The property change event that has occurred
  742        */
  743       public void propertyChange(PropertyChangeEvent event) {
  744   
  745           // Validate the source of this event
  746           if (!(event.getSource() instanceof Context))
  747               return;
  748           Context context = (Context) event.getSource();
  749   
  750           // Process a relevant property change
  751           if (event.getPropertyName().equals("reloadable")) {
  752               try {
  753                   setReloadable
  754                       ( ((Boolean) event.getNewValue()).booleanValue() );
  755               } catch (NumberFormatException e) {
  756                   log.error(sm.getString("webappLoader.reloadable",
  757                                    event.getNewValue().toString()));
  758               }
  759           }
  760   
  761       }
  762   
  763   
  764       // ------------------------------------------------------- Private Methods
  765   
  766   
  767       /**
  768        * Create associated classLoader.
  769        */
  770       private WebappClassLoader createClassLoader()
  771           throws Exception {
  772   
  773           Class clazz = Class.forName(loaderClass);
  774           WebappClassLoader classLoader = null;
  775   
  776           if (parentClassLoader == null) {
  777               parentClassLoader = container.getParentClassLoader();
  778           }
  779           Class[] argTypes = { ClassLoader.class };
  780           Object[] args = { parentClassLoader };
  781           Constructor constr = clazz.getConstructor(argTypes);
  782           classLoader = (WebappClassLoader) constr.newInstance(args);
  783   
  784           return classLoader;
  785   
  786       }
  787   
  788   
  789       /**
  790        * Configure associated class loader permissions.
  791        */
  792       private void setPermissions() {
  793   
  794           if (!Globals.IS_SECURITY_ENABLED)
  795               return;
  796           if (!(container instanceof Context))
  797               return;
  798   
  799           // Tell the class loader the root of the context
  800           ServletContext servletContext =
  801               ((Context) container).getServletContext();
  802   
  803           // Assigning permissions for the work directory
  804           File workDir =
  805               (File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
  806           if (workDir != null) {
  807               try {
  808                   String workDirPath = workDir.getCanonicalPath();
  809                   classLoader.addPermission
  810                       (new FilePermission(workDirPath, "read,write"));
  811                   classLoader.addPermission
  812                       (new FilePermission(workDirPath + File.separator + "-", 
  813                                           "read,write,delete"));
  814               } catch (IOException e) {
  815                   // Ignore
  816               }
  817           }
  818   
  819           try {
  820   
  821               URL rootURL = servletContext.getResource("/");
  822               classLoader.addPermission(rootURL);
  823   
  824               String contextRoot = servletContext.getRealPath("/");
  825               if (contextRoot != null) {
  826                   try {
  827                       contextRoot = (new File(contextRoot)).getCanonicalPath();
  828                       classLoader.addPermission(contextRoot);
  829                   } catch (IOException e) {
  830                       // Ignore
  831                   }
  832               }
  833   
  834               URL classesURL = servletContext.getResource("/WEB-INF/classes/");
  835               classLoader.addPermission(classesURL);
  836               URL libURL = servletContext.getResource("/WEB-INF/lib/");
  837               classLoader.addPermission(libURL);
  838   
  839               if (contextRoot != null) {
  840   
  841                   if (libURL != null) {
  842                       File rootDir = new File(contextRoot);
  843                       File libDir = new File(rootDir, "WEB-INF/lib/");
  844                       try {
  845                           String path = libDir.getCanonicalPath();
  846                           classLoader.addPermission(path);
  847                       } catch (IOException e) {
  848                       }
  849                   }
  850   
  851               } else {
  852   
  853                   if (workDir != null) {
  854                       if (libURL != null) {
  855                           File libDir = new File(workDir, "WEB-INF/lib/");
  856                           try {
  857                               String path = libDir.getCanonicalPath();
  858                               classLoader.addPermission(path);
  859                           } catch (IOException e) {
  860                           }
  861                       }
  862                       if (classesURL != null) {
  863                           File classesDir = new File(workDir, "WEB-INF/classes/");
  864                           try {
  865                               String path = classesDir.getCanonicalPath();
  866                               classLoader.addPermission(path);
  867                           } catch (IOException e) {
  868                           }
  869                       }
  870                   }
  871   
  872               }
  873   
  874           } catch (MalformedURLException e) {
  875           }
  876   
  877       }
  878   
  879   
  880       /**
  881        * Configure the repositories for our class loader, based on the
  882        * associated Context.
  883        * @throws IOException 
  884        */
  885       private void setRepositories() throws IOException {
  886   
  887           if (!(container instanceof Context))
  888               return;
  889           ServletContext servletContext =
  890               ((Context) container).getServletContext();
  891           if (servletContext == null)
  892               return;
  893   
  894           loaderRepositories=new ArrayList();
  895           // Loading the work directory
  896           File workDir =
  897               (File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);
  898           if (workDir == null) {
  899               log.info("No work dir for " + servletContext);
  900           }
  901   
  902           if( log.isDebugEnabled()) 
  903               log.debug(sm.getString("webappLoader.deploy", workDir.getAbsolutePath()));
  904   
  905           classLoader.setWorkDir(workDir);
  906   
  907           DirContext resources = container.getResources();
  908   
  909           // Setting up the class repository (/WEB-INF/classes), if it exists
  910   
  911           String classesPath = "/WEB-INF/classes";
  912           DirContext classes = null;
  913   
  914           try {
  915               Object object = resources.lookup(classesPath);
  916               if (object instanceof DirContext) {
  917                   classes = (DirContext) object;
  918               }
  919           } catch(NamingException e) {
  920               // Silent catch: it's valid that no /WEB-INF/classes collection
  921               // exists
  922           }
  923   
  924           if (classes != null) {
  925   
  926               File classRepository = null;
  927   
  928               String absoluteClassesPath =
  929                   servletContext.getRealPath(classesPath);
  930   
  931               if (absoluteClassesPath != null) {
  932   
  933                   classRepository = new File(absoluteClassesPath);
  934   
  935               } else {
  936   
  937                   classRepository = new File(workDir, classesPath);
  938                   classRepository.mkdirs();
  939                   if (!copyDir(classes, classRepository)) {
  940                       throw new IOException(
  941                               sm.getString("webappLoader.copyFailure"));
  942                   }
  943   
  944               }
  945   
  946               if(log.isDebugEnabled())
  947                   log.debug(sm.getString("webappLoader.classDeploy", classesPath,
  948                                classRepository.getAbsolutePath()));
  949   
  950   
  951               // Adding the repository to the class loader
  952               classLoader.addRepository(classesPath + "/", classRepository);
  953               loaderRepositories.add(classesPath + "/" );
  954   
  955           }
  956   
  957           // Setting up the JAR repository (/WEB-INF/lib), if it exists
  958   
  959           String libPath = "/WEB-INF/lib";
  960   
  961           classLoader.setJarPath(libPath);
  962   
  963           DirContext libDir = null;
  964           // Looking up directory /WEB-INF/lib in the context
  965           try {
  966               Object object = resources.lookup(libPath);
  967               if (object instanceof DirContext)
  968                   libDir = (DirContext) object;
  969           } catch(NamingException e) {
  970               // Silent catch: it's valid that no /WEB-INF/lib collection
  971               // exists
  972           }
  973   
  974           if (libDir != null) {
  975   
  976               boolean copyJars = false;
  977               String absoluteLibPath = servletContext.getRealPath(libPath);
  978   
  979               File destDir = null;
  980   
  981               if (absoluteLibPath != null) {
  982                   destDir = new File(absoluteLibPath);
  983               } else {
  984                   copyJars = true;
  985                   destDir = new File(workDir, libPath);
  986                   destDir.mkdirs();
  987               }
  988   
  989               // Looking up directory /WEB-INF/lib in the context
  990               NamingEnumeration<NameClassPair> enumeration = null;
  991               try {
  992                   enumeration = libDir.list("");
  993               } catch (NamingException e) {
  994                   IOException ioe = new IOException(sm.getString(
  995                           "webappLoader.namingFailure", libPath));
  996                   ioe.initCause(e);
  997                   throw ioe;
  998               }
  999                   while (enumeration.hasMoreElements()) {
 1000                   NameClassPair ncPair = enumeration.nextElement();
 1001                   String filename = libPath + "/" + ncPair.getName();
 1002                       if (!filename.endsWith(".jar"))
 1003                           continue;
 1004   
 1005                       // Copy JAR in the work directory, always (the JAR file
 1006                       // would get locked otherwise, which would make it
 1007                       // impossible to update it or remove it at runtime)
 1008                   File destFile = new File(destDir, ncPair.getName());
 1009   
 1010                       if( log.isDebugEnabled())
 1011                       log.debug(sm.getString("webappLoader.jarDeploy", filename,
 1012                                        destFile.getAbsolutePath()));
 1013   
 1014                   // Bug 45403 - Explicitly call lookup() on the name to check
 1015                   // that the resource is readable. We cannot use resources
 1016                   // returned by listBindings(), because that lists all of them,
 1017                   // but does not perform the necessary checks on each.
 1018                   Object obj = null;
 1019                   try {
 1020                       obj = libDir.lookup(ncPair.getName());
 1021                   } catch (NamingException e) {
 1022                       IOException ioe = new IOException(sm.getString(
 1023                               "webappLoader.namingFailure", filename));
 1024                       ioe.initCause(e);
 1025                       throw ioe;
 1026                   }
 1027                       
 1028                       if (!(obj instanceof Resource))
 1029                           continue;
 1030                       
 1031                       Resource jarResource = (Resource) obj;
 1032                       
 1033                       if (copyJars) {
 1034                           if (!copy(jarResource.streamContent(),
 1035                                 new FileOutputStream(destFile))) {
 1036                           throw new IOException(
 1037                                   sm.getString("webappLoader.copyFailure"));
 1038                       }
 1039                   }
 1040   
 1041                       try {
 1042                           JarFile jarFile = new JarFile(destFile);
 1043                           classLoader.addJar(filename, jarFile, destFile);
 1044                       } catch (Exception ex) {
 1045                           // Catch the exception if there is an empty jar file
 1046                       // Should ignore and continue loading other jar files 
 1047                           // in the dir
 1048                       }
 1049                       
 1050                       loaderRepositories.add( filename );
 1051                   }
 1052               }
 1053           }
 1054   
 1055   
 1056       /**
 1057        * Set the appropriate context attribute for our class path.  This
 1058        * is required only because Jasper depends on it.
 1059        */
 1060       private void setClassPath() {
 1061   
 1062           // Validate our current state information
 1063           if (!(container instanceof Context))
 1064               return;
 1065           ServletContext servletContext =
 1066               ((Context) container).getServletContext();
 1067           if (servletContext == null)
 1068               return;
 1069   
 1070           if (container instanceof StandardContext) {
 1071               String baseClasspath = 
 1072                   ((StandardContext) container).getCompilerClasspath();
 1073               if (baseClasspath != null) {
 1074                   servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
 1075                                               baseClasspath);
 1076                   return;
 1077               }
 1078           }
 1079   
 1080           StringBuffer classpath = new StringBuffer();
 1081   
 1082           // Assemble the class path information from our class loader chain
 1083           ClassLoader loader = getClassLoader();
 1084           int layers = 0;
 1085           int n = 0;
 1086           while (loader != null) {
 1087               if (!(loader instanceof URLClassLoader)) {
 1088                   String cp=getClasspath( loader );
 1089                   if( cp==null ) {
 1090                       log.info( "Unknown loader " + loader + " " + loader.getClass());
 1091                       break;
 1092                   } else {
 1093                       if (n > 0) 
 1094                           classpath.append(File.pathSeparator);
 1095                       classpath.append(cp);
 1096                       n++;
 1097                   }
 1098                   break;
 1099                   //continue;
 1100               }
 1101               URL repositories[] =
 1102                   ((URLClassLoader) loader).getURLs();
 1103               for (int i = 0; i < repositories.length; i++) {
 1104                   String repository = repositories[i].toString();
 1105                   if (repository.startsWith("file://"))
 1106                       repository = repository.substring(7);
 1107                   else if (repository.startsWith("file:"))
 1108                       repository = repository.substring(5);
 1109                   else if (repository.startsWith("jndi:"))
 1110                       repository =
 1111                           servletContext.getRealPath(repository.substring(5));
 1112                   else
 1113                       continue;
 1114                   if (repository == null)
 1115                       continue;
 1116                   if (n > 0)
 1117                       classpath.append(File.pathSeparator);
 1118                   classpath.append(repository);
 1119                   n++;
 1120               }
 1121               loader = loader.getParent();
 1122               layers++;
 1123           }
 1124   
 1125           this.classpath=classpath.toString();
 1126   
 1127           // Store the assembled class path as a servlet context attribute
 1128           servletContext.setAttribute(Globals.CLASS_PATH_ATTR,
 1129                                       classpath.toString());
 1130   
 1131       }
 1132   
 1133       // try to extract the classpath from a loader that is not URLClassLoader
 1134       private String getClasspath( ClassLoader loader ) {
 1135           try {
 1136               Method m=loader.getClass().getMethod("getClasspath", new Class[] {});
 1137               if( log.isTraceEnabled())
 1138                   log.trace("getClasspath " + m );
 1139               if( m==null ) return null;
 1140               Object o=m.invoke( loader, new Object[] {} );
 1141               if( log.isDebugEnabled() )
 1142                   log.debug("gotClasspath " + o);
 1143               if( o instanceof String )
 1144                   return (String)o;
 1145               return null;
 1146           } catch( Exception ex ) {
 1147               if (log.isDebugEnabled())
 1148                   log.debug("getClasspath ", ex);
 1149           }
 1150           return null;
 1151       }
 1152   
 1153       /**
 1154        * Copy directory.
 1155        */
 1156       private boolean copyDir(DirContext srcDir, File destDir) {
 1157   
 1158           try {
 1159   
 1160               NamingEnumeration enumeration = srcDir.list("");
 1161               while (enumeration.hasMoreElements()) {
 1162                   NameClassPair ncPair =
 1163                       (NameClassPair) enumeration.nextElement();
 1164                   String name = ncPair.getName();
 1165                   Object object = srcDir.lookup(name);
 1166                   File currentFile = new File(destDir, name);
 1167                   if (object instanceof Resource) {
 1168                       InputStream is = ((Resource) object).streamContent();
 1169                       OutputStream os = new FileOutputStream(currentFile);
 1170                       if (!copy(is, os))
 1171                           return false;
 1172                   } else if (object instanceof InputStream) {
 1173                       OutputStream os = new FileOutputStream(currentFile);
 1174                       if (!copy((InputStream) object, os))
 1175                           return false;
 1176                   } else if (object instanceof DirContext) {
 1177                       currentFile.mkdir();
 1178                       if (!copyDir((DirContext) object, currentFile))
 1179                           return false;
 1180                   }
 1181               }
 1182   
 1183           } catch (NamingException e) {
 1184               return false;
 1185           } catch (IOException e) {
 1186               return false;
 1187           }
 1188   
 1189           return true;
 1190   
 1191       }
 1192   
 1193   
 1194       /**
 1195        * Copy a file to the specified temp directory. This is required only
 1196        * because Jasper depends on it.
 1197        */
 1198       private boolean copy(InputStream is, OutputStream os) {
 1199   
 1200           try {
 1201               byte[] buf = new byte[4096];
 1202               while (true) {
 1203                   int len = is.read(buf);
 1204                   if (len < 0)
 1205                       break;
 1206                   os.write(buf, 0, len);
 1207               }
 1208               is.close();
 1209               os.close();
 1210           } catch (IOException e) {
 1211               return false;
 1212           }
 1213   
 1214           return true;
 1215   
 1216       }
 1217   
 1218   
 1219       private static org.apache.juli.logging.Log log=
 1220           org.apache.juli.logging.LogFactory.getLog( WebappLoader.class );
 1221   
 1222       private ObjectName oname;
 1223       private MBeanServer mserver;
 1224       private String domain;
 1225       private ObjectName controller;
 1226   
 1227       public ObjectName preRegister(MBeanServer server,
 1228                                     ObjectName name) throws Exception {
 1229           oname=name;
 1230           mserver=server;
 1231           domain=name.getDomain();
 1232   
 1233           return name;
 1234       }
 1235   
 1236       public void postRegister(Boolean registrationDone) {
 1237       }
 1238   
 1239       public void preDeregister() throws Exception {
 1240       }
 1241   
 1242       public void postDeregister() {
 1243       }
 1244   
 1245       public ObjectName getController() {
 1246           return controller;
 1247       }
 1248   
 1249       public void setController(ObjectName controller) {
 1250           this.controller = controller;
 1251       }
 1252   
 1253   }

Save This Page
Home » apache-tomcat-6.0.26-src » org.apache » catalina » loader » [javadoc | source]