Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » catalina » startup » [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.startup;
   20   
   21   
   22   import java.io.BufferedOutputStream;
   23   import java.io.File;
   24   import java.io.FileOutputStream;
   25   import java.io.IOException;
   26   import java.io.InputStream;
   27   import java.util.ArrayList;
   28   import java.util.HashMap;
   29   import java.util.LinkedHashMap;
   30   import java.util.jar.JarEntry;
   31   import java.util.jar.JarFile;
   32   
   33   import javax.management.ObjectName;
   34   
   35   import org.apache.catalina.Container;
   36   import org.apache.catalina.Context;
   37   import org.apache.catalina.Engine;
   38   import org.apache.catalina.Host;
   39   import org.apache.catalina.Lifecycle;
   40   import org.apache.catalina.LifecycleEvent;
   41   import org.apache.catalina.LifecycleListener;
   42   import org.apache.catalina.core.ContainerBase;
   43   import org.apache.catalina.core.StandardHost;
   44   import org.apache.catalina.util.StringManager;
   45   import org.apache.tomcat.util.digester.Digester;
   46   import org.apache.tomcat.util.modeler.Registry;
   47   
   48   
   49   /**
   50    * Startup event listener for a <b>Host</b> that configures the properties
   51    * of that Host, and the associated defined contexts.
   52    *
   53    * @author Craig R. McClanahan
   54    * @author Remy Maucherat
   55    * @version $Revision: 613796 $ $Date: 2008-01-21 09:41:31 +0100 (lun., 21 janv. 2008) $
   56    */
   57   public class HostConfig
   58       implements LifecycleListener {
   59       
   60       protected static org.apache.juli.logging.Log log=
   61            org.apache.juli.logging.LogFactory.getLog( HostConfig.class );
   62   
   63       // ----------------------------------------------------- Instance Variables
   64   
   65   
   66       /**
   67        * App base.
   68        */
   69       protected File appBase = null;
   70   
   71   
   72       /**
   73        * Config base.
   74        */
   75       protected File configBase = null;
   76   
   77   
   78       /**
   79        * The Java class name of the Context configuration class we should use.
   80        */
   81       protected String configClass = "org.apache.catalina.startup.ContextConfig";
   82   
   83   
   84       /**
   85        * The Java class name of the Context implementation we should use.
   86        */
   87       protected String contextClass = "org.apache.catalina.core.StandardContext";
   88   
   89   
   90       /**
   91        * The Host we are associated with.
   92        */
   93       protected Host host = null;
   94   
   95       
   96       /**
   97        * The JMX ObjectName of this component.
   98        */
   99       protected ObjectName oname = null;
  100       
  101   
  102       /**
  103        * The string resources for this package.
  104        */
  105       protected static final StringManager sm =
  106           StringManager.getManager(Constants.Package);
  107   
  108   
  109       /**
  110        * Should we deploy XML Context config files?
  111        */
  112       protected boolean deployXML = false;
  113   
  114   
  115       /**
  116        * Should we unpack WAR files when auto-deploying applications in the
  117        * <code>appBase</code> directory?
  118        */
  119       protected boolean unpackWARs = false;
  120   
  121   
  122       /**
  123        * Map of deployed applications.
  124        */
  125       protected HashMap deployed = new HashMap();
  126   
  127       
  128       /**
  129        * List of applications which are being serviced, and shouldn't be 
  130        * deployed/undeployed/redeployed at the moment.
  131        */
  132       protected ArrayList serviced = new ArrayList();
  133       
  134   
  135       /**
  136        * Attribute value used to turn on/off XML validation
  137        */
  138       protected boolean xmlValidation = false;
  139   
  140   
  141       /**
  142        * Attribute value used to turn on/off XML namespace awarenes.
  143        */
  144       protected boolean xmlNamespaceAware = false;
  145   
  146   
  147       /**
  148        * The <code>Digester</code> instance used to parse context descriptors.
  149        */
  150       protected static Digester digester = createDigester();
  151   
  152   
  153       // ------------------------------------------------------------- Properties
  154   
  155   
  156       /**
  157        * Return the Context configuration class name.
  158        */
  159       public String getConfigClass() {
  160   
  161           return (this.configClass);
  162   
  163       }
  164   
  165   
  166       /**
  167        * Set the Context configuration class name.
  168        *
  169        * @param configClass The new Context configuration class name.
  170        */
  171       public void setConfigClass(String configClass) {
  172   
  173           this.configClass = configClass;
  174   
  175       }
  176   
  177   
  178       /**
  179        * Return the Context implementation class name.
  180        */
  181       public String getContextClass() {
  182   
  183           return (this.contextClass);
  184   
  185       }
  186   
  187   
  188       /**
  189        * Set the Context implementation class name.
  190        *
  191        * @param contextClass The new Context implementation class name.
  192        */
  193       public void setContextClass(String contextClass) {
  194   
  195           this.contextClass = contextClass;
  196   
  197       }
  198   
  199   
  200       /**
  201        * Return the deploy XML config file flag for this component.
  202        */
  203       public boolean isDeployXML() {
  204   
  205           return (this.deployXML);
  206   
  207       }
  208   
  209   
  210       /**
  211        * Set the deploy XML config file flag for this component.
  212        *
  213        * @param deployXML The new deploy XML flag
  214        */
  215       public void setDeployXML(boolean deployXML) {
  216   
  217           this.deployXML= deployXML;
  218   
  219       }
  220   
  221   
  222       /**
  223        * Return the unpack WARs flag.
  224        */
  225       public boolean isUnpackWARs() {
  226   
  227           return (this.unpackWARs);
  228   
  229       }
  230   
  231   
  232       /**
  233        * Set the unpack WARs flag.
  234        *
  235        * @param unpackWARs The new unpack WARs flag
  236        */
  237       public void setUnpackWARs(boolean unpackWARs) {
  238   
  239           this.unpackWARs = unpackWARs;
  240   
  241       }
  242       
  243       
  244        /**
  245        * Set the validation feature of the XML parser used when
  246        * parsing xml instances.
  247        * @param xmlValidation true to enable xml instance validation
  248        */
  249       public void setXmlValidation(boolean xmlValidation){
  250           this.xmlValidation = xmlValidation;
  251       }
  252   
  253       /**
  254        * Get the server.xml <host> attribute's xmlValidation.
  255        * @return true if validation is enabled.
  256        *
  257        */
  258       public boolean getXmlValidation(){
  259           return xmlValidation;
  260       }
  261   
  262       /**
  263        * Get the server.xml <host> attribute's xmlNamespaceAware.
  264        * @return true if namespace awarenes is enabled.
  265        *
  266        */
  267       public boolean getXmlNamespaceAware(){
  268           return xmlNamespaceAware;
  269       }
  270   
  271   
  272       /**
  273        * Set the namespace aware feature of the XML parser used when
  274        * parsing xml instances.
  275        * @param xmlNamespaceAware true to enable namespace awareness
  276        */
  277       public void setXmlNamespaceAware(boolean xmlNamespaceAware){
  278           this.xmlNamespaceAware=xmlNamespaceAware;
  279       }    
  280   
  281   
  282       // --------------------------------------------------------- Public Methods
  283   
  284   
  285       /**
  286        * Process the START event for an associated Host.
  287        *
  288        * @param event The lifecycle event that has occurred
  289        */
  290       public void lifecycleEvent(LifecycleEvent event) {
  291   
  292           if (event.getType().equals(Lifecycle.PERIODIC_EVENT))
  293               check();
  294   
  295           // Identify the host we are associated with
  296           try {
  297               host = (Host) event.getLifecycle();
  298               if (host instanceof StandardHost) {
  299                   setDeployXML(((StandardHost) host).isDeployXML());
  300                   setUnpackWARs(((StandardHost) host).isUnpackWARs());
  301                   setXmlNamespaceAware(((StandardHost) host).getXmlNamespaceAware());
  302                   setXmlValidation(((StandardHost) host).getXmlValidation());
  303               }
  304           } catch (ClassCastException e) {
  305               log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
  306               return;
  307           }
  308   
  309           // Process the event that has occurred
  310           if (event.getType().equals(Lifecycle.START_EVENT))
  311               start();
  312           else if (event.getType().equals(Lifecycle.STOP_EVENT))
  313               stop();
  314   
  315       }
  316   
  317       
  318       /**
  319        * Add a serviced application to the list.
  320        */
  321       public synchronized void addServiced(String name) {
  322           serviced.add(name);
  323       }
  324       
  325       
  326       /**
  327        * Is application serviced ?
  328        * @return state of the application
  329        */
  330       public synchronized boolean isServiced(String name) {
  331           return (serviced.contains(name));
  332       }
  333       
  334   
  335       /**
  336        * Removed a serviced application from the list.
  337        */
  338       public synchronized void removeServiced(String name) {
  339           serviced.remove(name);
  340       }
  341   
  342       
  343       /**
  344        * Get the instant where an application was deployed.
  345        * @return 0L if no application with that name is deployed, or the instant
  346        * on which the application was deployed
  347        */
  348       public long getDeploymentTime(String name) {
  349       	DeployedApplication app = (DeployedApplication) deployed.get(name);
  350       	if (app == null) {
  351       		return 0L;
  352       	} else {
  353       		return app.timestamp;
  354       	}
  355       }
  356       
  357       
  358       /**
  359        * Has the specified application been deployed? Note applications defined
  360        * in server.xml will not have been deployed.
  361        * @return <code>true</code> if the application has been deployed and
  362        * <code>false</code> if the applciation has not been deployed or does not
  363        * exist
  364        */
  365       public boolean isDeployed(String name) {
  366           DeployedApplication app = (DeployedApplication) deployed.get(name);
  367           if (app == null) {
  368               return false;
  369           } else {
  370               return true;
  371           }
  372       }
  373       
  374       
  375       // ------------------------------------------------------ Protected Methods
  376   
  377       
  378       /**
  379        * Create the digester which will be used to parse context config files.
  380        */
  381       protected static Digester createDigester() {
  382           Digester digester = new Digester();
  383           digester.setValidating(false);
  384           // Add object creation rule
  385           digester.addObjectCreate("Context", "org.apache.catalina.core.StandardContext",
  386               "className");
  387           // Set the properties on that object (it doesn't matter if extra 
  388           // properties are set)
  389           digester.addSetProperties("Context");
  390           return (digester);
  391       }
  392       
  393   
  394       /**
  395        * Return a File object representing the "application root" directory
  396        * for our associated Host.
  397        */
  398       protected File appBase() {
  399   
  400           if (appBase != null) {
  401               return appBase;
  402           }
  403   
  404           File file = new File(host.getAppBase());
  405           if (!file.isAbsolute())
  406               file = new File(System.getProperty("catalina.base"),
  407                               host.getAppBase());
  408           try {
  409               appBase = file.getCanonicalFile();
  410           } catch (IOException e) {
  411               appBase = file;
  412           }
  413           return (appBase);
  414   
  415       }
  416   
  417   
  418       /**
  419        * Return a File object representing the "configuration root" directory
  420        * for our associated Host.
  421        */
  422       protected File configBase() {
  423   
  424           if (configBase != null) {
  425               return configBase;
  426           }
  427   
  428           File file = new File(System.getProperty("catalina.base"), "conf");
  429           Container parent = host.getParent();
  430           if ((parent != null) && (parent instanceof Engine)) {
  431               file = new File(file, parent.getName());
  432           }
  433           file = new File(file, host.getName());
  434           try {
  435               configBase = file.getCanonicalFile();
  436           } catch (IOException e) {
  437               configBase = file;
  438           }
  439           return (configBase);
  440   
  441       }
  442   
  443       /**
  444        * Get the name of the configBase.
  445        * For use with JMX management.
  446        */
  447       public String getConfigBaseName() {
  448           return configBase().getAbsolutePath();
  449       }
  450   
  451       /**
  452        * Given a context path, get the config file name.
  453        */
  454       protected String getConfigFile(String path) {
  455           String basename = null;
  456           if (path.equals("")) {
  457               basename = "ROOT";
  458           } else {
  459               basename = path.substring(1).replace('/', '#');
  460           }
  461           return (basename);
  462       }
  463   
  464       
  465       /**
  466        * Given a context path, get the config file name.
  467        */
  468       protected String getDocBase(String path) {
  469           String basename = null;
  470           if (path.equals("")) {
  471               basename = "ROOT";
  472           } else {
  473               basename = path.substring(1);
  474           }
  475           return (basename);
  476       }
  477   
  478       
  479       /**
  480        * Deploy applications for any directories or WAR files that are found
  481        * in our "application root" directory.
  482        */
  483       protected void deployApps() {
  484   
  485           File appBase = appBase();
  486           File configBase = configBase();
  487           // Deploy XML descriptors from configBase
  488           deployDescriptors(configBase, configBase.list());
  489           // Deploy WARs, and loop if additional descriptors are found
  490           deployWARs(appBase, appBase.list());
  491           // Deploy expanded folders
  492           deployDirectories(appBase, appBase.list());
  493           
  494       }
  495   
  496   
  497       /**
  498        * Deploy applications for any directories or WAR files that are found
  499        * in our "application root" directory.
  500        */
  501       protected void deployApps(String name) {
  502   
  503           File appBase = appBase();
  504           File configBase = configBase();
  505           String baseName = getConfigFile(name);
  506           String docBase = getConfigFile(name);
  507           
  508           // Deploy XML descriptors from configBase
  509           File xml = new File(configBase, baseName + ".xml");
  510           if (xml.exists())
  511               deployDescriptor(name, xml, baseName + ".xml");
  512           // Deploy WARs, and loop if additional descriptors are found
  513           File war = new File(appBase, docBase + ".war");
  514           if (war.exists())
  515               deployWAR(name, war, docBase + ".war");
  516           // Deploy expanded folders
  517           File dir = new File(appBase, docBase);
  518           if (dir.exists())
  519               deployDirectory(name, dir, docBase);
  520           
  521       }
  522   
  523   
  524       /**
  525        * Deploy XML context descriptors.
  526        */
  527       protected void deployDescriptors(File configBase, String[] files) {
  528   
  529           if (files == null)
  530               return;
  531           
  532           for (int i = 0; i < files.length; i++) {
  533   
  534               if (files[i].equalsIgnoreCase("META-INF"))
  535                   continue;
  536               if (files[i].equalsIgnoreCase("WEB-INF"))
  537                   continue;
  538               File contextXml = new File(configBase, files[i]);
  539               if (files[i].toLowerCase().endsWith(".xml")) {
  540   
  541                   // Calculate the context path and make sure it is unique
  542                   String nameTmp = files[i].substring(0, files[i].length() - 4);
  543                   String contextPath = "/" + nameTmp.replace('#', '/');
  544                   if (nameTmp.equals("ROOT")) {
  545                       contextPath = "";
  546                   }
  547   
  548                   if (isServiced(contextPath))
  549                       continue;
  550                   
  551                   String file = files[i];
  552   
  553                   deployDescriptor(contextPath, contextXml, file);
  554                   
  555               }
  556   
  557           }
  558   
  559       }
  560   
  561   
  562       /**
  563        * @param contextPath
  564        * @param contextXml
  565        * @param file
  566        */
  567       protected void deployDescriptor(String contextPath, File contextXml, String file) {
  568           if (deploymentExists(contextPath)) {
  569               return;
  570           }
  571           
  572           DeployedApplication deployedApp = new DeployedApplication(contextPath);
  573   
  574           // Assume this is a configuration descriptor and deploy it
  575           if(log.isDebugEnabled()) {
  576               log.debug(sm.getString("hostConfig.deployDescriptor", file));
  577           }
  578   
  579           Context context = null;
  580           try {
  581               synchronized (digester) {
  582                   try {
  583                       context = (Context) digester.parse(contextXml);
  584                       if (context == null) {
  585                           log.error(sm.getString("hostConfig.deployDescriptor.error",
  586                                   file));
  587                           return;
  588                       }
  589                   } finally {
  590                       digester.reset();
  591                   }
  592               }
  593               if (context instanceof Lifecycle) {
  594                   Class clazz = Class.forName(host.getConfigClass());
  595                   LifecycleListener listener =
  596                       (LifecycleListener) clazz.newInstance();
  597                   ((Lifecycle) context).addLifecycleListener(listener);
  598               }
  599               context.setConfigFile(contextXml.getAbsolutePath());
  600               context.setPath(contextPath);
  601               // Add the associated docBase to the redeployed list if it's a WAR
  602               boolean isWar = false;
  603               boolean isExternal = false;
  604               if (context.getDocBase() != null) {
  605                   File docBase = new File(context.getDocBase());
  606                   if (!docBase.isAbsolute()) {
  607                       docBase = new File(appBase(), context.getDocBase());
  608                   }
  609                   // If external docBase, register .xml as redeploy first
  610                   if (!docBase.getCanonicalPath().startsWith(appBase().getAbsolutePath())) {
  611                       isExternal = true;
  612                       deployedApp.redeployResources.put
  613                           (contextXml.getAbsolutePath(), new Long(contextXml.lastModified()));
  614                       deployedApp.redeployResources.put(docBase.getAbsolutePath(),
  615                           new Long(docBase.lastModified()));
  616                       if (docBase.getAbsolutePath().toLowerCase().endsWith(".war")) {
  617                           isWar = true;
  618                       }
  619                   } else {
  620                       log.warn(sm.getString("hostConfig.deployDescriptor.localDocBaseSpecified",
  621                                docBase));
  622                       // Ignore specified docBase
  623                       context.setDocBase(null);
  624                   }
  625               }
  626               host.addChild(context);
  627               // Get paths for WAR and expanded WAR in appBase
  628               String name = null;
  629               String path = context.getPath();
  630               if (path.equals("")) {
  631                   name = "ROOT";
  632               } else {
  633                   if (path.startsWith("/")) {
  634                       name = path.substring(1);
  635                   } else {
  636                       name = path;
  637                   }
  638               }
  639               File expandedDocBase = new File(appBase(), name);
  640               if (context.getDocBase() != null) {
  641                   // first assume docBase is absolute
  642                   expandedDocBase = new File(context.getDocBase());
  643                   if (!expandedDocBase.isAbsolute()) {
  644                       // if docBase specified and relative, it must be relative to appBase
  645                       expandedDocBase = new File(appBase(), context.getDocBase());
  646                   }
  647               }
  648               // Add the eventual unpacked WAR and all the resources which will be
  649               // watched inside it
  650               if (isWar && unpackWARs) {
  651                   deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
  652                           new Long(expandedDocBase.lastModified()));
  653                   deployedApp.redeployResources.put
  654                       (contextXml.getAbsolutePath(), new Long(contextXml.lastModified()));
  655                   addWatchedResources(deployedApp, expandedDocBase.getAbsolutePath(), context);
  656               } else {
  657                   // Find an existing matching war and expanded folder
  658                  File warDocBase = new File(expandedDocBase.getAbsolutePath() + ".war");
  659                  if (warDocBase.exists()) {
  660                       deployedApp.redeployResources.put(warDocBase.getAbsolutePath(),
  661                               new Long(warDocBase.lastModified()));
  662                   }
  663                   if (expandedDocBase.exists()) {
  664                       deployedApp.redeployResources.put(expandedDocBase.getAbsolutePath(),
  665                               new Long(expandedDocBase.lastModified()));
  666                       addWatchedResources(deployedApp, 
  667                               expandedDocBase.getAbsolutePath(), context);
  668                   } else {
  669                       addWatchedResources(deployedApp, null, context);
  670                   }
  671                   // Add the context XML to the list of files which should trigger a redeployment
  672                   if (!isExternal) {
  673                       deployedApp.redeployResources.put
  674                           (contextXml.getAbsolutePath(), new Long(contextXml.lastModified()));
  675                   }
  676               }
  677           } catch (Throwable t) {
  678               log.error(sm.getString("hostConfig.deployDescriptor.error",
  679                                      file), t);
  680           }
  681   
  682           if (context != null && host.findChild(context.getName()) != null) {
  683               deployed.put(contextPath, deployedApp);
  684           }
  685       }
  686   
  687   
  688       /**
  689        * Deploy WAR files.
  690        */
  691       protected void deployWARs(File appBase, String[] files) {
  692           
  693           if (files == null)
  694               return;
  695           
  696           for (int i = 0; i < files.length; i++) {
  697               
  698               if (files[i].equalsIgnoreCase("META-INF"))
  699                   continue;
  700               if (files[i].equalsIgnoreCase("WEB-INF"))
  701                   continue;
  702               File dir = new File(appBase, files[i]);
  703               if (files[i].toLowerCase().endsWith(".war")) {
  704                   
  705                   // Calculate the context path and make sure it is unique
  706                   String contextPath = "/" + files[i];
  707                   int period = contextPath.lastIndexOf(".");
  708                   if (period >= 0)
  709                       contextPath = contextPath.substring(0, period);
  710                   if (contextPath.equals("/ROOT"))
  711                       contextPath = "";
  712                   
  713                   if (isServiced(contextPath))
  714                       continue;
  715                   
  716                   String file = files[i];
  717                   
  718                   deployWAR(contextPath, dir, file);
  719                   
  720               }
  721               
  722           }
  723           
  724       }
  725   
  726   
  727       /**
  728        * @param contextPath
  729        * @param dir
  730        * @param file
  731        */
  732       protected void deployWAR(String contextPath, File dir, String file) {
  733           
  734           if (deploymentExists(contextPath))
  735               return;
  736           
  737           // Checking for a nested /META-INF/context.xml
  738           JarFile jar = null;
  739           JarEntry entry = null;
  740           InputStream istream = null;
  741           BufferedOutputStream ostream = null;
  742           File xml = new File
  743               (configBase, file.substring(0, file.lastIndexOf(".")) + ".xml");
  744           if (deployXML && !xml.exists()) {
  745               try {
  746                   jar = new JarFile(dir);
  747                   entry = jar.getJarEntry(Constants.ApplicationContextXml);
  748                   if (entry != null) {
  749                       istream = jar.getInputStream(entry);
  750                       
  751                       configBase.mkdirs();
  752                       
  753                       ostream =
  754                           new BufferedOutputStream
  755                           (new FileOutputStream(xml), 1024);
  756                       byte buffer[] = new byte[1024];
  757                       while (true) {
  758                           int n = istream.read(buffer);
  759                           if (n < 0) {
  760                               break;
  761                           }
  762                           ostream.write(buffer, 0, n);
  763                       }
  764                       ostream.flush();
  765                       ostream.close();
  766                       ostream = null;
  767                       istream.close();
  768                       istream = null;
  769                       entry = null;
  770                       jar.close();
  771                       jar = null;
  772                   }
  773               } catch (Exception e) {
  774                   // Ignore and continue
  775                   if (ostream != null) {
  776                       try {
  777                           ostream.close();
  778                       } catch (Throwable t) {
  779                           ;
  780                       }
  781                       ostream = null;
  782                   }
  783                   if (istream != null) {
  784                       try {
  785                           istream.close();
  786                       } catch (Throwable t) {
  787                           ;
  788                       }
  789                       istream = null;
  790                   }
  791               } finally {
  792                   entry = null;
  793                   if (jar != null) {
  794                       try {
  795                           jar.close();
  796                       } catch (Throwable t) {
  797                           ;
  798                       }
  799                       jar = null;
  800                   }
  801               }
  802           }
  803           
  804           DeployedApplication deployedApp = new DeployedApplication(contextPath);
  805           
  806           // Deploy the application in this WAR file
  807           if(log.isInfoEnabled()) 
  808               log.info(sm.getString("hostConfig.deployJar", file));
  809   
  810           // Populate redeploy resources with the WAR file
  811           deployedApp.redeployResources.put
  812               (dir.getAbsolutePath(), new Long(dir.lastModified()));
  813   
  814           try {
  815               Context context = (Context) Class.forName(contextClass).newInstance();
  816               if (context instanceof Lifecycle) {
  817                   Class clazz = Class.forName(host.getConfigClass());
  818                   LifecycleListener listener =
  819                       (LifecycleListener) clazz.newInstance();
  820                   ((Lifecycle) context).addLifecycleListener(listener);
  821               }
  822               context.setPath(contextPath);
  823               context.setDocBase(file);
  824               if (xml.exists()) {
  825                   context.setConfigFile(xml.getAbsolutePath());
  826                   deployedApp.redeployResources.put
  827                       (xml.getAbsolutePath(), new Long(xml.lastModified()));
  828               }
  829               host.addChild(context);
  830               // If we're unpacking WARs, the docBase will be mutated after
  831               // starting the context
  832               if (unpackWARs && (context.getDocBase() != null)) {
  833                   String name = null;
  834                   String path = context.getPath();
  835                   if (path.equals("")) {
  836                       name = "ROOT";
  837                   } else {
  838                       if (path.startsWith("/")) {
  839                           name = path.substring(1);
  840                       } else {
  841                           name = path;
  842                       }
  843                   }
  844                   File docBase = new File(name);
  845                   if (!docBase.isAbsolute()) {
  846                       docBase = new File(appBase(), name);
  847                   }
  848                   deployedApp.redeployResources.put(docBase.getAbsolutePath(),
  849                           new Long(docBase.lastModified()));
  850                   addWatchedResources(deployedApp, docBase.getAbsolutePath(), context);
  851               } else {
  852                   addWatchedResources(deployedApp, null, context);
  853               }
  854           } catch (Throwable t) {
  855               log.error(sm.getString("hostConfig.deployJar.error", file), t);
  856           }
  857           
  858           deployed.put(contextPath, deployedApp);
  859       }
  860   
  861   
  862       /**
  863        * Deploy directories.
  864        */
  865       protected void deployDirectories(File appBase, String[] files) {
  866   
  867           if (files == null)
  868               return;
  869           
  870           for (int i = 0; i < files.length; i++) {
  871   
  872               if (files[i].equalsIgnoreCase("META-INF"))
  873                   continue;
  874               if (files[i].equalsIgnoreCase("WEB-INF"))
  875                   continue;
  876               File dir = new File(appBase, files[i]);
  877               if (dir.isDirectory()) {
  878   
  879                   // Calculate the context path and make sure it is unique
  880                   String contextPath = "/" + files[i];
  881                   if (files[i].equals("ROOT"))
  882                       contextPath = "";
  883   
  884                   if (isServiced(contextPath))
  885                       continue;
  886   
  887                   deployDirectory(contextPath, dir, files[i]);
  888               
  889               }
  890   
  891           }
  892   
  893       }
  894   
  895       
  896       /**
  897        * @param contextPath
  898        * @param dir
  899        * @param file
  900        */
  901       protected void deployDirectory(String contextPath, File dir, String file) {
  902           DeployedApplication deployedApp = new DeployedApplication(contextPath);
  903           
  904           if (deploymentExists(contextPath))
  905               return;
  906   
  907           // Deploy the application in this directory
  908           if( log.isDebugEnabled() ) 
  909               log.debug(sm.getString("hostConfig.deployDir", file));
  910           try {
  911               Context context = (Context) Class.forName(contextClass).newInstance();
  912               if (context instanceof Lifecycle) {
  913                   Class clazz = Class.forName(host.getConfigClass());
  914                   LifecycleListener listener =
  915                       (LifecycleListener) clazz.newInstance();
  916                   ((Lifecycle) context).addLifecycleListener(listener);
  917               }
  918               context.setPath(contextPath);
  919               context.setDocBase(file);
  920               File configFile = new File(dir, Constants.ApplicationContextXml);
  921               if (deployXML) {
  922                   context.setConfigFile(configFile.getAbsolutePath());
  923               }
  924               host.addChild(context);
  925               deployedApp.redeployResources.put(dir.getAbsolutePath(),
  926                       new Long(dir.lastModified()));
  927               if (deployXML) {
  928                   deployedApp.redeployResources.put(configFile.getAbsolutePath(),
  929                           new Long(configFile.lastModified()));
  930               }
  931               addWatchedResources(deployedApp, dir.getAbsolutePath(), context);
  932           } catch (Throwable t) {
  933               log.error(sm.getString("hostConfig.deployDir.error", file), t);
  934           }
  935   
  936           deployed.put(contextPath, deployedApp);
  937       }
  938   
  939       
  940       /**
  941        * Check if a webapp is already deployed in this host.
  942        * 
  943        * @param contextPath of the context which will be checked
  944        */
  945       protected boolean deploymentExists(String contextPath) {
  946           return (deployed.containsKey(contextPath) || (host.findChild(contextPath) != null));
  947       }
  948       
  949   
  950       /**
  951        * Add watched resources to the specified Context.
  952        * @param app HostConfig deployed app
  953        * @param docBase web app docBase
  954        * @param context web application context
  955        */
  956       protected void addWatchedResources(DeployedApplication app, String docBase, Context context) {
  957           // FIXME: Feature idea. Add support for patterns (ex: WEB-INF/*, WEB-INF/*.xml), where
  958           //        we would only check if at least one resource is newer than app.timestamp
  959           File docBaseFile = null;
  960           if (docBase != null) {
  961               docBaseFile = new File(docBase);
  962               if (!docBaseFile.isAbsolute()) {
  963                   docBaseFile = new File(appBase(), docBase);
  964               }
  965           }
  966           String[] watchedResources = context.findWatchedResources();
  967           for (int i = 0; i < watchedResources.length; i++) {
  968               File resource = new File(watchedResources[i]);
  969               if (!resource.isAbsolute()) {
  970                   if (docBase != null) {
  971                       resource = new File(docBaseFile, watchedResources[i]);
  972                   } else {
  973                       if(log.isDebugEnabled())
  974                           log.debug("Ignoring non-existent WatchedResource '" 
  975                               + resource.getAbsolutePath() + "'");
  976                      continue;
  977                   }
  978               }
  979               if(log.isDebugEnabled())
  980                   log.debug("Watching WatchedResource '" + resource.getAbsolutePath() + "'");
  981               app.reloadResources.put(resource.getAbsolutePath(), 
  982                       new Long(resource.lastModified()));
  983           }
  984       }
  985       
  986   
  987       /**
  988        * Check resources for redeployment and reloading.
  989        */
  990       protected synchronized void checkResources(DeployedApplication app) {
  991           String[] resources = (String[]) app.redeployResources.keySet().toArray(new String[0]);
  992           for (int i = 0; i < resources.length; i++) {
  993               File resource = new File(resources[i]);
  994               if (log.isDebugEnabled())
  995                   log.debug("Checking context[" + app.name + "] redeploy resource " + resource);
  996               if (resource.exists()) {
  997                   long lastModified = ((Long) app.redeployResources.get(resources[i])).longValue();
  998                   if ((!resource.isDirectory()) && resource.lastModified() > lastModified) {
  999                       // Undeploy application
 1000                       if (log.isInfoEnabled())
 1001                           log.info(sm.getString("hostConfig.undeploy", app.name));
 1002                       ContainerBase context = (ContainerBase) host.findChild(app.name);
 1003                       try {
 1004                           host.removeChild(context);
 1005                       } catch (Throwable t) {
 1006                           log.warn(sm.getString
 1007                                    ("hostConfig.context.remove", app.name), t);
 1008                       }
 1009                       try {
 1010                           context.destroy();
 1011                       } catch (Throwable t) {
 1012                           log.warn(sm.getString
 1013                                    ("hostConfig.context.destroy", app.name), t);
 1014                       }
 1015                       // Delete other redeploy resources
 1016                       for (int j = i + 1; j < resources.length; j++) {
 1017                           try {
 1018                               File current = new File(resources[j]);
 1019                               current = current.getCanonicalFile();
 1020                               if ((current.getAbsolutePath().startsWith(appBase().getAbsolutePath()))
 1021                                       || (current.getAbsolutePath().startsWith(configBase().getAbsolutePath()))) {
 1022                                   if (log.isDebugEnabled())
 1023                                       log.debug("Delete " + current);
 1024                                   ExpandWar.delete(current);
 1025                               }
 1026                           } catch (IOException e) {
 1027                               log.warn(sm.getString
 1028                                       ("hostConfig.canonicalizing", app.name), e);
 1029                           }
 1030                       }
 1031                       deployed.remove(app.name);
 1032                       return;
 1033                   }
 1034               } else {
 1035                   long lastModified = ((Long) app.redeployResources.get(resources[i])).longValue();
 1036                   if (lastModified == 0L) {
 1037                       continue;
 1038                   }
 1039                   // Undeploy application
 1040                   if (log.isInfoEnabled())
 1041                       log.info(sm.getString("hostConfig.undeploy", app.name));
 1042                   ContainerBase context = (ContainerBase) host.findChild(app.name);
 1043                   try {
 1044                       host.removeChild(context);
 1045                   } catch (Throwable t) {
 1046                       log.warn(sm.getString
 1047                                ("hostConfig.context.remove", app.name), t);
 1048                   }
 1049                   try {
 1050                       context.destroy();
 1051                   } catch (Throwable t) {
 1052                       log.warn(sm.getString
 1053                                ("hostConfig.context.destroy", app.name), t);
 1054                   }
 1055                   // Delete all redeploy resources
 1056                   for (int j = i + 1; j < resources.length; j++) {
 1057                       try {
 1058                           File current = new File(resources[j]);
 1059                           current = current.getCanonicalFile();
 1060                           if ((current.getAbsolutePath().startsWith(appBase().getAbsolutePath()))
 1061                               || (current.getAbsolutePath().startsWith(configBase().getAbsolutePath()))) {
 1062                               if (log.isDebugEnabled())
 1063                                   log.debug("Delete " + current);
 1064                               ExpandWar.delete(current);
 1065                           }
 1066                       } catch (IOException e) {
 1067                           log.warn(sm.getString
 1068                                   ("hostConfig.canonicalizing", app.name), e);
 1069                       }
 1070                   }
 1071                   // Delete reload resources as well (to remove any remaining .xml descriptor)
 1072                   String[] resources2 = (String[]) app.reloadResources.keySet().toArray(new String[0]);
 1073                   for (int j = 0; j < resources2.length; j++) {
 1074                       try {
 1075                           File current = new File(resources2[j]);
 1076                           current = current.getCanonicalFile();
 1077                           if ((current.getAbsolutePath().startsWith(appBase().getAbsolutePath()))
 1078                               || ((current.getAbsolutePath().startsWith(configBase().getAbsolutePath())
 1079                                    && (current.getAbsolutePath().endsWith(".xml"))))) {
 1080                               if (log.isDebugEnabled())
 1081                                   log.debug("Delete " + current);
 1082                               ExpandWar.delete(current);
 1083                           }
 1084                       } catch (IOException e) {
 1085                           log.warn(sm.getString
 1086                                   ("hostConfig.canonicalizing", app.name), e);
 1087                       }
 1088                   }
 1089                   deployed.remove(app.name);
 1090                   return;
 1091               }
 1092           }
 1093           resources = (String[]) app.reloadResources.keySet().toArray(new String[0]);
 1094           for (int i = 0; i < resources.length; i++) {
 1095               File resource = new File(resources[i]);
 1096               if (log.isDebugEnabled())
 1097                   log.debug("Checking context[" + app.name + "] reload resource " + resource);
 1098               long lastModified = ((Long) app.reloadResources.get(resources[i])).longValue();
 1099               if ((!resource.exists() && lastModified != 0L) 
 1100                   || (resource.lastModified() != lastModified)) {
 1101                   // Reload application
 1102                   if(log.isInfoEnabled())
 1103                       log.info(sm.getString("hostConfig.reload", app.name));
 1104                   Container context = host.findChild(app.name);
 1105                   try {
 1106                       ((Lifecycle) context).stop();
 1107                   } catch (Exception e) {
 1108                       log.warn(sm.getString
 1109                                ("hostConfig.context.restart", app.name), e);
 1110                   }
 1111                   // If the context was not started (for example an error 
 1112                   // in web.xml) we'll still get to try to start
 1113                   try {
 1114                       ((Lifecycle) context).start();
 1115                   } catch (Exception e) {
 1116                       log.warn(sm.getString
 1117                                ("hostConfig.context.restart", app.name), e);
 1118                   }
 1119                   // Update times
 1120                   app.reloadResources.put(resources[i], new Long(resource.lastModified()));
 1121                   app.timestamp = System.currentTimeMillis();
 1122                   return;
 1123               }
 1124           }
 1125       }
 1126       
 1127       
 1128       /**
 1129        * Process a "start" event for this Host.
 1130        */
 1131       public void start() {
 1132   
 1133           if (log.isDebugEnabled())
 1134               log.debug(sm.getString("hostConfig.start"));
 1135   
 1136           try {
 1137               ObjectName hostON = new ObjectName(host.getObjectName());
 1138               oname = new ObjectName
 1139                   (hostON.getDomain() + ":type=Deployer,host=" + host.getName());
 1140               Registry.getRegistry(null, null).registerComponent
 1141                   (this, oname, this.getClass().getName());
 1142           } catch (Exception e) {
 1143               log.error(sm.getString("hostConfig.jmx.register", oname), e);
 1144           }
 1145   
 1146           if (host.getDeployOnStartup())
 1147               deployApps();
 1148           
 1149       }
 1150   
 1151   
 1152       /**
 1153        * Process a "stop" event for this Host.
 1154        */
 1155       public void stop() {
 1156   
 1157           if (log.isDebugEnabled())
 1158               log.debug(sm.getString("hostConfig.stop"));
 1159   
 1160           undeployApps();
 1161   
 1162           if (oname != null) {
 1163               try {
 1164                   Registry.getRegistry(null, null).unregisterComponent(oname);
 1165               } catch (Exception e) {
 1166                   log.error(sm.getString("hostConfig.jmx.unregister", oname), e);
 1167               }
 1168           }
 1169           oname = null;
 1170           appBase = null;
 1171           configBase = null;
 1172   
 1173       }
 1174   
 1175   
 1176       /**
 1177        * Undeploy all deployed applications.
 1178        */
 1179       protected void undeployApps() {
 1180   
 1181           if (log.isDebugEnabled())
 1182               log.debug(sm.getString("hostConfig.undeploying"));
 1183   
 1184           // Soft undeploy all contexts we have deployed
 1185           DeployedApplication[] apps = 
 1186               (DeployedApplication[]) deployed.values().toArray(new DeployedApplication[0]);
 1187           for (int i = 0; i < apps.length; i++) {
 1188               try {
 1189                   host.removeChild(host.findChild(apps[i].name));
 1190               } catch (Throwable t) {
 1191                   log.warn(sm.getString
 1192                           ("hostConfig.context.remove", apps[i].name), t);
 1193               }
 1194           }
 1195           
 1196           deployed.clear();
 1197   
 1198       }
 1199   
 1200   
 1201       /**
 1202        * Check status of all webapps.
 1203        */
 1204       protected void check() {
 1205   
 1206           if (host.getAutoDeploy()) {
 1207               // Check for resources modification to trigger redeployment
 1208               DeployedApplication[] apps = 
 1209                   (DeployedApplication[]) deployed.values().toArray(new DeployedApplication[0]);
 1210               for (int i = 0; i < apps.length; i++) {
 1211                   if (!isServiced(apps[i].name))
 1212                       checkResources(apps[i]);
 1213               }
 1214               // Hotdeploy applications
 1215               deployApps();
 1216           }
 1217   
 1218       }
 1219   
 1220       
 1221       /**
 1222        * Check status of a specific webapp, for use with stuff like management webapps.
 1223        */
 1224       public void check(String name) {
 1225           DeployedApplication app = (DeployedApplication) deployed.get(name);
 1226           if (app != null) {
 1227               checkResources(app);
 1228           } else {
 1229               deployApps(name);
 1230           }
 1231       }
 1232   
 1233       /**
 1234        * Add a new Context to be managed by us.
 1235        * Entry point for the admin webapp, and other JMX Context controlers.
 1236        */
 1237       public void manageApp(Context context)  {    
 1238   
 1239           String contextPath = context.getPath();
 1240           
 1241           if (deployed.containsKey(contextPath))
 1242               return;
 1243   
 1244           DeployedApplication deployedApp = new DeployedApplication(contextPath);
 1245           
 1246           // Add the associated docBase to the redeployed list if it's a WAR
 1247           boolean isWar = false;
 1248           if (context.getDocBase() != null) {
 1249               File docBase = new File(context.getDocBase());
 1250               if (!docBase.isAbsolute()) {
 1251                   docBase = new File(appBase(), context.getDocBase());
 1252               }
 1253               deployedApp.redeployResources.put(docBase.getAbsolutePath(),
 1254                                             new Long(docBase.lastModified()));
 1255               if (docBase.getAbsolutePath().toLowerCase().endsWith(".war")) {
 1256                   isWar = true;
 1257               }
 1258           }
 1259           host.addChild(context);
 1260           // Add the eventual unpacked WAR and all the resources which will be
 1261           // watched inside it
 1262           if (isWar && unpackWARs) {
 1263               String name = null;
 1264               String path = context.getPath();
 1265               if (path.equals("")) {
 1266                   name = "ROOT";
 1267               } else {
 1268                   if (path.startsWith("/")) {
 1269                       name = path.substring(1);
 1270                   } else {
 1271                       name = path;
 1272                   }
 1273               }
 1274               File docBase = new File(name);
 1275               if (!docBase.isAbsolute()) {
 1276                   docBase = new File(appBase(), name);
 1277               }
 1278               deployedApp.redeployResources.put(docBase.getAbsolutePath(),
 1279                           new Long(docBase.lastModified()));
 1280               addWatchedResources(deployedApp, docBase.getAbsolutePath(), context);
 1281           } else {
 1282               addWatchedResources(deployedApp, null, context);
 1283           }
 1284           deployed.put(contextPath, deployedApp);
 1285       }
 1286   
 1287       /**
 1288        * Remove a webapp from our control.
 1289        * Entry point for the admin webapp, and other JMX Context controlers.
 1290        */
 1291       public void unmanageApp(String contextPath) {
 1292           if(isServiced(contextPath)) {
 1293               deployed.remove(contextPath);
 1294               host.removeChild(host.findChild(contextPath));
 1295           }
 1296       }
 1297   
 1298       // ----------------------------------------------------- Instance Variables
 1299   
 1300   
 1301       /**
 1302        * This class represents the state of a deployed application, as well as 
 1303        * the monitored resources.
 1304        */
 1305       protected class DeployedApplication {
 1306       	public DeployedApplication(String name) {
 1307       		this.name = name;
 1308       	}
 1309       	
 1310       	/**
 1311       	 * Application context path. The assertion is that 
 1312       	 * (host.getChild(name) != null).
 1313       	 */
 1314       	public String name;
 1315       	
 1316       	/**
 1317       	 * Any modification of the specified (static) resources will cause a 
 1318       	 * redeployment of the application. If any of the specified resources is
 1319       	 * removed, the application will be undeployed. Typically, this will
 1320       	 * contain resources like the context.xml file, a compressed WAR path.
 1321            * The value is the last modification time.
 1322       	 */
 1323       	public LinkedHashMap redeployResources = new LinkedHashMap();
 1324   
 1325       	/**
 1326       	 * Any modification of the specified (static) resources will cause a 
 1327       	 * reload of the application. This will typically contain resources
 1328       	 * such as the web.xml of a webapp, but can be configured to contain
 1329       	 * additional descriptors.
 1330            * The value is the last modification time.
 1331       	 */
 1332       	public HashMap reloadResources = new HashMap();
 1333   
 1334       	/**
 1335       	 * Instant where the application was last put in service.
 1336       	 */
 1337       	public long timestamp = System.currentTimeMillis();
 1338       }
 1339   
 1340   }

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