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

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