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

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