You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by re...@apache.org on 2004/07/24 00:57:35 UTC

cvs commit: jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina Lifecycle.java Context.java Host.java

remm        2004/07/23 15:57:35

  Modified:    catalina/src/share/org/apache/catalina/startup
                        HostConfig.java ContextConfig.java
                        ContextRuleSet.java
               catalina/src/share/org/apache/catalina/core
                        StandardContext.java
               catalina/src/share/org/apache/catalina Lifecycle.java
                        Context.java Host.java
  Removed:     catalina/src/share/org/apache/catalina/startup
                        SetDocBaseRule.java
  Log:
  - First part of the refactoring of the deployer, where HostConfig will be the only entry point for automated deployments
    (= deployments where Container.addChild isn't directly used, as in the embedded world) and redeployments.
  - This will allow removing a lot of the complexity involved with the fact that there was 3 components which had
    deployment logic inside (HostConfig, StdHostDeployer, and the manager webapp). Now the manager webapp will "talk"
    to the HostConfig.
  - Some hooks on HostConfig are not there yet.
  - Auto redeploy checks aren't done in HostConfig yet.
  - ContextConfig is now (logically) in charge of parsing the context config files.
  - New default web.xml and context config files have been added. This is not tested (actually, little of this is testedright now: the default
    setup deploys ok, but more complex setups might not). Having default context config files should allow removing DefaultContext and its
    complexity.
  - The new default context config files are:
    - conf/context.xml (so this is for all contexts)
    - conf/<engine>/<host>/context.xml.default (per host)
  - The new default web.xml is:
    - conf/<engine>/<host>/web.xml.default (per host)
  - Some of the paths to files are hardcoded, but I think I'll add configurability for most of them.
  
  Revision  Changes    Path
  1.34      +327 -446  jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/HostConfig.java
  
  Index: HostConfig.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/HostConfig.java,v
  retrieving revision 1.33
  retrieving revision 1.34
  diff -u -r1.33 -r1.34
  --- HostConfig.java	23 Jun 2004 16:59:41 -0000	1.33
  +++ HostConfig.java	23 Jul 2004 22:57:34 -0000	1.34
  @@ -26,26 +26,19 @@
   import java.net.URL;
   import java.util.ArrayList;
   import java.util.HashMap;
  -import java.util.Iterator;
  -import java.util.List;
   import java.util.jar.JarEntry;
   import java.util.jar.JarFile;
   
  -import javax.naming.NamingException;
  -import javax.naming.directory.DirContext;
  -
   import org.apache.catalina.Container;
   import org.apache.catalina.Context;
  -import org.apache.catalina.Deployer;
   import org.apache.catalina.Engine;
   import org.apache.catalina.Host;
   import org.apache.catalina.Lifecycle;
   import org.apache.catalina.LifecycleEvent;
   import org.apache.catalina.LifecycleListener;
  -import org.apache.catalina.core.StandardContext;
   import org.apache.catalina.core.StandardHost;
   import org.apache.catalina.util.StringManager;
  -import org.apache.naming.resources.ResourceAttributes;
  +import org.apache.tomcat.util.digester.Digester;
   
   
   /**
  @@ -60,7 +53,7 @@
   public class HostConfig
       implements LifecycleListener {
       
  -    private static org.apache.commons.logging.Log log=
  +    protected static org.apache.commons.logging.Log log=
            org.apache.commons.logging.LogFactory.getLog( HostConfig.class );
   
       // ----------------------------------------------------- Instance Variables
  @@ -69,13 +62,13 @@
       /**
        * App base.
        */
  -    private File appBase = null;
  +    protected File appBase = null;
   
   
       /**
        * Config base.
        */
  -    private File configBase = null;
  +    protected File configBase = null;
   
   
       /**
  @@ -91,13 +84,6 @@
   
   
       /**
  -     * The names of applications that we have auto-deployed (to avoid
  -     * double deployment attempts).
  -     */
  -    protected ArrayList deployed = new ArrayList();
  -
  -
  -    /**
        * The Host we are associated with.
        */
       protected Host host = null;
  @@ -113,46 +99,45 @@
       /**
        * Should we deploy XML Context config files?
        */
  -    private boolean deployXML = false;
  +    protected boolean deployXML = false;
   
   
       /**
        * Should we unpack WAR files when auto-deploying applications in the
        * <code>appBase</code> directory?
        */
  -    private boolean unpackWARs = false;
  +    protected boolean unpackWARs = false;
   
   
       /**
  -     * Last modified dates of the web.xml files of the contexts, keyed by
  -     * context name.
  +     * Map of deployed applications.
        */
  -    private HashMap webXmlLastModified = new HashMap();
  -
  +    protected HashMap deployed = new HashMap();
   
  +    
       /**
  -     * Last modified dates of the Context xml files of the contexts, keyed by
  -     * context name.
  +     * List of applications which are being serviced, and shouldn't be 
  +     * deployed/undeployed/redeployed at the moment.
        */
  -    private HashMap contextXmlLastModified = new HashMap();
  -
  +    protected ArrayList serviced = new ArrayList();
  +    
   
       /**
  -     * Last modified dates of the source WAR files, keyed by WAR name.
  +     * Attribute value used to turn on/off XML validation
        */
  -    private HashMap warLastModified = new HashMap();
  +    protected boolean xmlValidation = false;
   
   
       /**
  -     * Attribute value used to turn on/off XML validation
  +     * Attribute value used to turn on/off XML namespace awarenes.
        */
  -    private boolean xmlValidation = false;
  +    protected boolean xmlNamespaceAware = false;
   
   
       /**
  -     * Attribute value used to turn on/off XML namespace awarenes.
  +     * The <code>Digester</code> instance used to parse context descriptors.
        */
  -    private boolean xmlNamespaceAware = false;
  +    protected static Digester digester = createDigester();
   
   
       // ------------------------------------------------------------- Properties
  @@ -319,9 +304,65 @@
   
       }
   
  +    
  +    /**
  +     * Add a serviced application to the list.
  +     */
  +    public synchronized void addServiced(String name) {
  +        serviced.add(name);
  +    }
  +    
  +    
  +    /**
  +     * Is application serviced ?
  +     * @return state of the application
  +     */
  +    public synchronized boolean isServiced(String name) {
  +        return (serviced.contains(name));
  +    }
  +    
   
  +    /**
  +     * Removed a serviced application from the list.
  +     */
  +    public synchronized void removeServiced(String name) {
  +        serviced.remove(name);
  +    }
  +
  +    
  +    /**
  +     * Get the instant where an application was deployed.
  +     * @return 0L if no application with that name is deployed, or the instant
  +     * on which the application was deployed
  +     */
  +    public long getDeploymentTime(String name) {
  +    	DeployedApplication app = (DeployedApplication) deployed.get(name);
  +    	if (app == null) {
  +    		return 0L;
  +    	} else {
  +    		return app.timestamp;
  +    	}
  +    }
  +    
  +    
       // ------------------------------------------------------ Protected Methods
   
  +    
  +    /**
  +     * Create the digester which will be used to parse context config files.
  +     */
  +    protected static Digester createDigester() {
  +        Digester digester = new Digester();
  +        digester.setValidating(false);
  +        // Add object creation rule
  +        digester.addObjectCreate("Context", "org.apache.catalina.core.StandardContext",
  +            "className");
  +        // Set the properties on that object (it doesn't matter if extra 
  +        // properties are set)
  +        digester.addSetProperties("Context");
  +        return (digester);
  +    }
  +    
   
       /**
        * Return a File object representing the "application root" directory
  @@ -379,24 +420,15 @@
        */
       protected void deployApps() {
   
  -        if (!(host instanceof Deployer))
  -            return;
  -
  -        // Initialize the deployer
  -        ((Deployer) host).findDeployedApps();
  -
           File appBase = appBase();
  -        if (!appBase.exists() || !appBase.isDirectory())
  -            return;
           File configBase = configBase();
  -        if (configBase.exists() && configBase.isDirectory()) {
  -            String configFiles[] = configBase.list();
  -            deployDescriptors(configBase, configFiles);
  -        }
  -
  -        String files[] = appBase.list();
  -        deployWARs(appBase, files);
  -        deployDirectories(appBase, files);
  +        do {
  +            // Deploy XML descriptors from configBase
  +            deployDescriptors(configBase, configBase.list());
  +            // Deploy expanded folders
  +            deployDirectories(appBase, appBase.list());
  +            // Deploy WARs, and loop if additional descriptors are found
  +        } while (deployWARs(appBase, appBase.list()));
   
       }
   
  @@ -406,22 +438,18 @@
        */
       protected void deployDescriptors(File configBase, String[] files) {
   
  -        if (!deployXML)
  -           return;
  -
  +        if (files == null)
  +            return;
  +        
           for (int i = 0; i < files.length; i++) {
   
               if (files[i].equalsIgnoreCase("META-INF"))
                   continue;
               if (files[i].equalsIgnoreCase("WEB-INF"))
                   continue;
  -            if (deployed.contains(files[i]))
  -                continue;
  -            File dir = new File(configBase, files[i]);
  +            File contextXml = new File(configBase, files[i]);
               if (files[i].toLowerCase().endsWith(".xml")) {
   
  -                deployed.add(files[i]);
  -
                   // Calculate the context path and make sure it is unique
                   String file = files[i].substring(0, files[i].length() - 4);
                   String contextPath = "/" + file.replace('#', '/');
  @@ -429,29 +457,34 @@
                       contextPath = "";
                   }
   
  +                if (deployed.containsKey(contextPath))
  +                    continue;
  +
                   // Assume this is a configuration descriptor and deploy it
                   log.debug(sm.getString("hostConfig.deployDescriptor", files[i]));
                   try {
  -                    if (host.findChild(contextPath) != null) {
  -                        if ((deployed.contains(file))
  -                            || (deployed.contains(file + ".war"))) {
  -                            // If this is a newly added context file and 
  -                            // it overrides a context with a simple path, 
  -                            // that was previously deployed by the auto
  -                            // deployer, undeploy the context
  -                            ((Deployer) host).remove(contextPath);
  -                        } else {
  -                            continue;
  -                        }
  +                    Context newContext = null;
  +                    synchronized (digester) {
  +                        newContext = (Context) digester.parse(contextXml);
  +                    }
  +                    if (newContext instanceof Lifecycle) {
  +                        Class clazz = Class.forName(host.getConfigClass());
  +                        LifecycleListener listener =
  +                            (LifecycleListener) clazz.newInstance();
  +                        ((Lifecycle) newContext).addLifecycleListener(listener);
                       }
  -                    URL config =
  -                        new URL("file", null, dir.getCanonicalPath());
  -                    ((Deployer) host).install(config, null);
  +                    newContext.setConfigFile(contextXml.getAbsolutePath());
  +                    newContext.setPath(contextPath);
  +                    host.addChild(newContext);
                   } catch (Throwable t) {
                       log.error(sm.getString("hostConfig.deployDescriptor.error",
                                              files[i]), t);
                   }
   
  +                deployed.put(contextPath, new DeployedApplication(contextPath));
  +                // FIXME: populate needed resources, such as the docBase (WAR, expanded)
  +                // FIXME: populate watched resources
  +                
               }
   
           }
  @@ -462,21 +495,22 @@
       /**
        * Deploy WAR files.
        */
  -    protected void deployWARs(File appBase, String[] files) {
  +    protected boolean deployWARs(File appBase, String[] files) {
   
  +        if (files == null)
  +            return false;
  +        
  +        boolean checkAdditionalDeployments = false;
  +        
           for (int i = 0; i < files.length; i++) {
   
               if (files[i].equalsIgnoreCase("META-INF"))
                   continue;
               if (files[i].equalsIgnoreCase("WEB-INF"))
                   continue;
  -            if (deployed.contains(files[i]))
  -                continue;
               File dir = new File(appBase, files[i]);
               if (files[i].toLowerCase().endsWith(".war")) {
   
  -                deployed.add(files[i]);
  -
                   // Calculate the context path and make sure it is unique
                   String contextPath = "/" + files[i];
                   int period = contextPath.lastIndexOf(".");
  @@ -484,79 +518,9 @@
                       contextPath = contextPath.substring(0, period);
                   if (contextPath.equals("/ROOT"))
                       contextPath = "";
  -                if (host.findChild(contextPath) != null)
  -                    continue;
   
  -                // Checking for a nested /META-INF/context.xml
  -                JarFile jar = null;
  -                JarEntry entry = null;
  -                InputStream istream = null;
  -                BufferedOutputStream ostream = null;
  -                File xml = new File
  -                    (configBase, files[i].substring
  -                     (0, files[i].lastIndexOf(".")) + ".xml");
  -                if (!xml.exists()) {
  -                    try {
  -                        jar = new JarFile(dir);
  -                        entry = jar.getJarEntry("META-INF/context.xml");
  -                        if (entry != null) {
  -                            istream = jar.getInputStream(entry);
  -                         
  -                            // Added for Bugzilla 29038
  -                            xml.mkdirs();
  -
  -                            ostream =
  -                                new BufferedOutputStream
  -                                (new FileOutputStream(xml), 1024);
  -                            byte buffer[] = new byte[1024];
  -                            while (true) {
  -                                int n = istream.read(buffer);
  -                                if (n < 0) {
  -                                    break;
  -                                }
  -                                ostream.write(buffer, 0, n);
  -                            }
  -                            ostream.flush();
  -                            ostream.close();
  -                            ostream = null;
  -                            istream.close();
  -                            istream = null;
  -                            entry = null;
  -                            jar.close();
  -                            jar = null;
  -                            deployDescriptors(configBase(), configBase.list());
  -                            return;
  -                        }
  -                    } catch (Exception e) {
  -                        // Ignore and continue
  -                        if (ostream != null) {
  -                            try {
  -                                ostream.close();
  -                            } catch (Throwable t) {
  -                                ;
  -                            }
  -                            ostream = null;
  -                        }
  -                        if (istream != null) {
  -                            try {
  -                                istream.close();
  -                            } catch (Throwable t) {
  -                                ;
  -                            }
  -                            istream = null;
  -                        }
  -                    } finally {
  -                        entry = null;
  -                        if (jar != null) {
  -                            try {
  -                                jar.close();
  -                            } catch (Throwable t) {
  -                                ;
  -                            }
  -                            jar = null;
  -                        }
  -                    }
  -                }
  +                if (deployed.containsKey(contextPath))
  +                    continue;
   
                   if (isUnpackWARs()) {
   
  @@ -568,45 +532,122 @@
                           url = new URL("jar:file:" +
                                         dir.getCanonicalPath() + "!/");
                           path = ExpandWar.expand(host, url);
  +                        checkAdditionalDeployments = true;
                       } catch (IOException e) {
                           // JAR decompression failure
                           log.warn(sm.getString
                                    ("hostConfig.expand.error", files[i]));
  -                        continue;
                       } catch (Throwable t) {
                           log.error(sm.getString
                                     ("hostConfig.expand.error", files[i]), t);
  -                        continue;
                       }
  -                    try {
  -                        if (path != null) {
  -                            url = new URL("file:" + path);
  -                            ((Deployer) host).install(contextPath, url);
  +                    
  +                    // The webapp will actually be deployed when checking the directories
  +                    
  +                } else {
  +
  +                    // FIXME: Don't do that if disabled on Host (sort of "security" feature)
  +
  +                    // Checking for a nested /META-INF/context.xml
  +                    JarFile jar = null;
  +                    JarEntry entry = null;
  +                    InputStream istream = null;
  +                    BufferedOutputStream ostream = null;
  +                    File xml = new File
  +                        (configBase, files[i].substring
  +                         (0, files[i].lastIndexOf(".")) + ".xml");
  +                    if (!xml.exists()) {
  +                        try {
  +                            jar = new JarFile(dir);
  +                            entry = jar.getJarEntry("META-INF/context.xml");
  +                            if (entry != null) {
  +                                istream = jar.getInputStream(entry);
  +                                
  +                                configBase.mkdirs();
  +                                
  +                                ostream =
  +                                    new BufferedOutputStream
  +                                    (new FileOutputStream(xml), 1024);
  +                                byte buffer[] = new byte[1024];
  +                                while (true) {
  +                                    int n = istream.read(buffer);
  +                                    if (n < 0) {
  +                                        break;
  +                                    }
  +                                    ostream.write(buffer, 0, n);
  +                                }
  +                                ostream.flush();
  +                                ostream.close();
  +                                ostream = null;
  +                                istream.close();
  +                                istream = null;
  +                                entry = null;
  +                                jar.close();
  +                                jar = null;
  +                                //deployDescriptors(configBase(), configBase.list());
  +                                checkAdditionalDeployments = true;
  +                                continue;
  +                            }
  +                        } catch (Exception e) {
  +                            // Ignore and continue
  +                            if (ostream != null) {
  +                                try {
  +                                    ostream.close();
  +                                } catch (Throwable t) {
  +                                    ;
  +                                }
  +                                ostream = null;
  +                            }
  +                            if (istream != null) {
  +                                try {
  +                                    istream.close();
  +                                } catch (Throwable t) {
  +                                    ;
  +                                }
  +                                istream = null;
  +                            }
  +                        } finally {
  +                            entry = null;
  +                            if (jar != null) {
  +                                try {
  +                                    jar.close();
  +                                } catch (Throwable t) {
  +                                    ;
  +                                }
  +                                jar = null;
  +                            }
                           }
  -                    } catch (Throwable t) {
  -                        log.error(sm.getString
  -                                  ("hostConfig.expand.error", files[i]), t);
                       }
   
  -                } else {
  -
                       // Deploy the application in this WAR file
                       log.info(sm.getString("hostConfig.deployJar", files[i]));
                       try {
  -                        URL url = new URL("file", null,
  -                                          dir.getCanonicalPath());
  -                        url = new URL("jar:" + url.toString() + "!/");
  -                        ((Deployer) host).install(contextPath, url);
  +                        Context context = (Context) Class.forName(contextClass).newInstance();
  +                        if (context instanceof Lifecycle) {
  +                            Class clazz = Class.forName(host.getConfigClass());
  +                            LifecycleListener listener =
  +                                (LifecycleListener) clazz.newInstance();
  +                            ((Lifecycle) context).addLifecycleListener(listener);
  +                        }
  +                        context.setPath(contextPath);
  +                        context.setDocBase(files[i]);
  +                        host.addChild(context);
                       } catch (Throwable t) {
                           log.error(sm.getString("hostConfig.deployJar.error",
                                            files[i]), t);
                       }
   
  +                    deployed.put(contextPath, new DeployedApplication(contextPath));
  +                    // FIXME: populate needed resources, such as the docBase (context file, WAR, expanded)
  +                    // FIXME: populate watched resources
  +
                   }
   
               }
   
           }
  +        
  +        return checkAdditionalDeployments;
   
       }
   
  @@ -616,19 +657,18 @@
        */
       protected void deployDirectories(File appBase, String[] files) {
   
  +        if (files == null)
  +            return;
  +        
           for (int i = 0; i < files.length; i++) {
   
               if (files[i].equalsIgnoreCase("META-INF"))
                   continue;
               if (files[i].equalsIgnoreCase("WEB-INF"))
                   continue;
  -            if (deployed.contains(files[i]))
  -                continue;
               File dir = new File(appBase, files[i]);
               if (dir.isDirectory()) {
   
  -                deployed.add(files[i]);
  -
                   // Make sure there is an application configuration directory
                   // This is needed if the Context appBase is the same as the
                   // web server document root to make sure only web applications
  @@ -642,7 +682,8 @@
                   String contextPath = "/" + files[i];
                   if (files[i].equals("ROOT"))
                       contextPath = "";
  -                if (host.findChild(contextPath) != null)
  +
  +                if (deployed.containsKey(contextPath))
                       continue;
   
                   // Deploy the application in this directory
  @@ -650,15 +691,30 @@
                       log.debug(sm.getString("hostConfig.deployDir", files[i]));
                   long t1=System.currentTimeMillis();
                   try {
  -                    URL url = new URL("file", null, dir.getCanonicalPath());
  -                    ((Deployer) host).install(contextPath, url);
  +                    Context context = (Context) Class.forName(contextClass).newInstance();
  +                    if (context instanceof Lifecycle) {
  +                        Class clazz = Class.forName(host.getConfigClass());
  +                        LifecycleListener listener =
  +                            (LifecycleListener) clazz.newInstance();
  +                        ((Lifecycle) context).addLifecycleListener(listener);
  +                    }
  +                    context.setPath(contextPath);
  +                    context.setDocBase(files[i]);
  +                    // FIXME: Don't set that if disabled on Host (sort of "security" feature)
  +                    context.setConfigFile((new File(dir, "META-INF/context.xml")).getAbsolutePath());
  +                    host.addChild(context);
                   } catch (Throwable t) {
                       log.error(sm.getString("hostConfig.deployDir.error", files[i]),
                           t);
                   }
                   long t2=System.currentTimeMillis();
  -                if( (t2-t1) > 200 )
  +                if( log.isDebugEnabled() && (t2-t1) > 200 )
                       log.debug("Deployed " + files[i] + " " + (t2-t1));
  +
  +                deployed.put(contextPath, new DeployedApplication(contextPath));
  +                // FIXME: populate needed resources, such as the docBase (context file, WAR, expanded)
  +                // FIXME: populate watched resources
  +            
               }
   
           }
  @@ -667,224 +723,58 @@
   
   
       /**
  -     * Check deployment descriptors last modified date.
  +     * Check resources for redeployment and reloading.
        */
  -    protected void checkContextLastModified() {
  -
  -        if (!(host instanceof Deployer))
  +    protected synchronized void checkResources(DeployedApplication app) {
  +        if (isServiced(app.name))
               return;
  -
  -        Deployer deployer = (Deployer) host;
  -
  -        String[] contextNames = deployer.findDeployedApps();
  -
  -        for (int i = 0; i < contextNames.length; i++) {
  -
  -            String contextName = contextNames[i];
  -            Context context = deployer.findDeployedApp(contextName);
  -
  -            if (!(context instanceof Lifecycle))
  -                continue;
  -
  -            try {
  -                DirContext resources = context.getResources();
  -                if (resources == null) {
  -                    // This can happen if there was an error initializing
  -                    // the context
  -                    continue;
  -                }
  -                ResourceAttributes webXmlAttributes = 
  -                    (ResourceAttributes) 
  -                    resources.getAttributes("/WEB-INF/web.xml");
  -                ResourceAttributes webInfAttributes = 
  -                    (ResourceAttributes) 
  -                    resources.getAttributes("/WEB-INF");
  -                long newLastModified = webXmlAttributes.getLastModified();
  -                long webInfLastModified = webInfAttributes.getLastModified();
  -                Long lastModified = (Long) webXmlLastModified.get(contextName);
  -                if (lastModified == null) {
  -                    webXmlLastModified.put
  -                        (contextName, new Long(newLastModified));
  -                } else {
  -                    if (lastModified.longValue() != newLastModified) {
  -                        if (newLastModified > (webInfLastModified + 5000)) {
  -                            webXmlLastModified.remove(contextName);
  -                            restartContext(context);
  -                        } else {
  -                            webXmlLastModified.put
  -                                (contextName, new Long(newLastModified));
  -                        }
  -                    }
  -                }
  -            } catch (NamingException e) {
  -                ; // Ignore
  +        String[] resources = (String[]) app.redeployResources.toArray(new String[0]);
  +        for (int i = 0; i < resources.length; i++) {
  +            File resource = new File(resources[i]);
  +            if (resource.exists()) { 
  +                if (resource.lastModified() > app.timestamp) {
  +                    // Redeploy application
  +                    host.removeChild(host.findChild(app.name));
  +                    // FIXME: Remove other redeploy resources; need hack here to remove expanded
  +                    // folder if updated resource is a WAR
  +                    deployed.remove(app);
  +                    return;
  +                }
  +            } else {
  +                // Undeploy application
  +                host.removeChild(host.findChild(app.name));
  +                // FIXME: Delete all redeploy resources
  +                deployed.remove(app);
  +                return;
               }
  -
  -            Long lastModified = (Long) contextXmlLastModified.get(contextName);
  -            String configBase = configBase().getPath();
  -            String configFileName = context.getConfigFile();
  -            if (configFileName != null) {
  -                File configFile = new File(configFileName);
  -                if (!configFile.isAbsolute()) {
  -                    configFile = new File(System.getProperty("catalina.base"),
  -                                          configFile.getPath());
  +        }
  +        resources = (String[]) app.reloadResources.toArray(new String[0]);
  +        for (int i = 0; i < resources.length; i++) {
  +            File resource = new File(resources[i]);
  +            if ((!resource.exists()) || (resource.lastModified() > app.timestamp)) {
  +                // Reload application
  +                Container context = host.findChild(app.name);
  +                try {
  +                    ((Lifecycle) context).stop();
  +                } catch (Exception e) {
  +                    log.warn(sm.getString
  +                             ("hostConfig.context.restart", app.name), e);
                   }
  -                long newLastModified = configFile.lastModified();
  -                if (lastModified == null) {
  -                    contextXmlLastModified.put
  -                        (contextName, new Long(newLastModified));
  -                } else {
  -                    if (lastModified.longValue() != newLastModified) {
  -                        contextXmlLastModified.remove(contextName);
  -                        String fileName = configFileName;
  -                        if (fileName.startsWith(configBase)) {
  -                            fileName = 
  -                                fileName.substring(configBase.length() + 1);
  -                            try {
  -                                deployed.remove(fileName);
  -                                if (host.findChild(contextName) != null) {
  -                                    ((Deployer) host).remove(contextName);
  -                                }
  -                            } catch (Throwable t) {
  -                                log.error(sm.getString
  -                                          ("hostConfig.undeployJar.error",
  -                                           fileName), t);
  -                            }
  -                            deployApps();
  -                        }
  -                    }
  +                // If the context was not started (for example an error 
  +                // in web.xml) we'll still get to try to start
  +                try {
  +                    ((Lifecycle) context).start();
  +                } catch (Exception e) {
  +                    log.warn(sm.getString
  +                             ("hostConfig.context.restart", app.name), e);
                   }
  -            }
  -
  -        }
  -
  -        // Check for WAR modification
  -        if (isUnpackWARs()) {
  -            File appBase = appBase();
  -            if (!appBase.exists() || !appBase.isDirectory())
  +                app.timestamp = System.currentTimeMillis();
                   return;
  -            String files[] = appBase.list();
  -
  -            for (int i = 0; i < files.length; i++) {
  -                if (files[i].endsWith(".war")) {
  -                    File dir = new File(appBase, files[i]);
  -                    Long lastModified = (Long) warLastModified.get(files[i]);
  -                    long dirLastModified = dir.lastModified();
  -                    if (lastModified == null) {
  -                        warLastModified.put
  -                            (files[i], new Long(dir.lastModified()));
  -                    } else if (dirLastModified > lastModified.longValue()) {
  -                        // The WAR has been modified: redeploy
  -                        String expandedDir = files[i];
  -                        int period = expandedDir.lastIndexOf(".");
  -                        if (period >= 0)
  -                            expandedDir = expandedDir.substring(0, period);
  -                        File expanded = new File(appBase, expandedDir);
  -                        String contextPath = "/" + expandedDir;
  -                        if (contextPath.equals("/ROOT"))
  -                            contextPath = "";
  -                        if (dirLastModified > expanded.lastModified()) {
  -                            try {
  -                                // Undeploy current application
  -                                deployed.remove(files[i]);
  -                                deployed.remove(expandedDir + ".xml");
  -                                if (host.findChild(contextPath) != null) {
  -                                    ((Deployer) host).remove(contextPath, 
  -                                                             false);
  -                                    ExpandWar.deleteDir(expanded);
  -                                }
  -                            } catch (Throwable t) {
  -                                log.error(sm.getString
  -                                          ("hostConfig.undeployJar.error",
  -                                           files[i]), t);
  -                            }
  -                            deployApps();
  -                        }
  -                        // If deployment was successful, reset 
  -                        // the last modified values
  -                        if (host.findChild(contextPath) != null) {
  -                            webXmlLastModified.remove(contextPath);
  -                            warLastModified.put
  -                                (files[i], new Long(dir.lastModified()));
  -                        }
  -                    }
  -                }
  -            }
  -        }
  -
  -    }
  -
  -
  -    protected boolean restartContext(Context context) {
  -        boolean result = true;
  -        log.info("restartContext(" + context.getName() + ")");
  -
  -        if (context instanceof StandardContext) {
  -            try {
  -                StandardContext sctx = (StandardContext)context;
  -                sctx.reload();
  -            } catch (Exception e) {
  -                log.warn(sm.getString
  -                         ("hostConfig.context.restart", context.getName()), e);
  -                result = false;
  -            }
  -        } else {
  -            try {
  -                ((Lifecycle) context).stop();
  -            } catch (Exception e) {
  -                log.warn(sm.getString
  -                         ("hostConfig.context.restart", context.getName()), e);
  -            }
  -            // If the context was not started (for example an error 
  -            // in web.xml) we'll still get to try to start
  -            try {
  -                ((Lifecycle) context).start();
  -            } catch (Exception e) {
  -                log.warn(sm.getString
  -                         ("hostConfig.context.restart", context.getName()), e);
  -                result = false;
               }
           }
  -
  -        return result;
       }
  -
  -
  -    /**
  -     * Expand the WAR file found at the specified URL into an unpacked
  -     * directory structure, and return the absolute pathname to the expanded
  -     * directory.
  -     *
  -     * @param war URL of the web application archive to be expanded
  -     *  (must start with "jar:")
  -     *
  -     * @exception IllegalArgumentException if this is not a "jar:" URL
  -     * @exception IOException if an input/output error was encountered
  -     *  during expansion
  -     */
  -    protected String expand(URL war) throws IOException {
  -
  -        return ExpandWar.expand(host,war);
  -    }
  -
  -
  -    /**
  -     * Expand the specified input stream into the specified directory, creating
  -     * a file named from the specified relative path.
  -     *
  -     * @param input InputStream to be copied
  -     * @param docBase Document base directory into which we are expanding
  -     * @param name Relative pathname of the file to be created
  -     *
  -     * @exception IOException if an input/output error occurs
  -     */
  -    protected void expand(InputStream input, File docBase, String name)
  -        throws IOException {
  -
  -        ExpandWar.expand(input,docBase,name);
  -    }
  -
  -
  +    
  +    
       /**
        * Process a "start" event for this Host.
        */
  @@ -893,18 +783,8 @@
           if (log.isDebugEnabled())
               log.debug(sm.getString("hostConfig.start"));
   
  -        if (host.getDeployOnStartup()) {
  -            deployApps();
  -        } else {
  -            // Deploy descriptors anyway (it should be equivalent to being
  -            // part of server.xml)
  -            File configBase = configBase();
  -            if (configBase.exists() && configBase.isDirectory()) {
  -                String configFiles[] = configBase.list();
  -                deployDescriptors(configBase, configFiles);
  -            }
  -        }
  -
  +        deployApps();
  +        
       }
   
   
  @@ -929,77 +809,78 @@
        */
       protected void undeployApps() {
   
  -        if (!(host instanceof Deployer))
  -            return;
           if (log.isDebugEnabled())
               log.debug(sm.getString("hostConfig.undeploying"));
   
  -        String contextPaths[] = ((Deployer) host).findDeployedApps();
  -        for (int i = 0; i < contextPaths.length; i++) {
  -            if (log.isDebugEnabled())
  -                log.debug(sm.getString("hostConfig.undeploy", contextPaths[i]));
  -            try {
  -                ((Deployer) host).remove(contextPaths[i]);
  -            } catch (Throwable t) {
  -                log.error(sm.getString("hostConfig.undeploy.error",
  -                                 contextPaths[i]), t);
  -            }
  +        // Soft undeploy all contexts we have deployed
  +        DeployedApplication[] apps = 
  +            (DeployedApplication[]) deployed.values().toArray(new DeployedApplication[0]);
  +        for (int i = 0; i < apps.length; i++) {
  +            host.removeChild(host.findChild(apps[i].name));
           }
  -
  -        webXmlLastModified.clear();
  +        
           deployed.clear();
   
       }
   
   
       /**
  -     *  Check remove wars, dir and context xml's
  -     */
  -    protected void checkRemoveApps() {
  -        File appBase = appBase();
  -        File configBase = configBase();
  -        List currentDeployed = (List) deployed.clone();
  -        Iterator iter = currentDeployed.iterator();
  -        while (iter.hasNext()) {
  -            String filename = (String) iter.next();
  -            if (filename.toLowerCase().endsWith(".xml")) {
  -                File file = new File(configBase, filename);
  -                if (!file.exists()) {
  -                    log.debug(sm.getString("hostConfig.removeXML", filename));
  -                    deployed.remove(filename);
  -                }
  -                continue;
  -            }
  -            File file = new File(appBase, filename);
  -            if (!file.exists()) {
  -                if (log.isDebugEnabled() ) {
  -                    if (filename.toLowerCase().endsWith(".war"))
  -                        log.debug(sm.getString("hostConfig.removeDIR", 
  -                                               filename));
  -                    else
  -                        log.debug(sm.getString("hostConfig.removeWAR",
  -                                               filename));
  -                }
  -                deployed.remove(filename);
  -            }
  -        }
  -    }
  -
  -
  -    /**
        * Deploy webapps.
        */
       protected void check() {
   
           if (host.getAutoDeploy()) {
  -            checkRemoveApps();
  -            // Deploy apps if the Host allows auto deploying
  +            // Check for resources modification to trigger redeployment
  +            DeployedApplication[] apps = 
  +                (DeployedApplication[]) deployed.values().toArray(new DeployedApplication[0]);
  +            for (int i = 0; i < apps.length; i++) {
  +                checkResources(apps[i]);
  +            }
  +            // Hotdeploy applications
               deployApps();
  -            // Check for web.xml modification
  -            checkContextLastModified();
           }
   
       }
   
  +    
  +    // ----------------------------------------------------- Instance Variables
  +
  +
  +    /**
  +     * This class represents the state of a deployed application, as well as 
  +     * the monitored resources.
  +     */
  +    protected class DeployedApplication {
  +    	public DeployedApplication(String name) {
  +    		this.name = name;
  +    	}
  +    	
  +    	/**
  +    	 * Application context path. The assertion is that 
  +    	 * (host.getChild(name) != null).
  +    	 */
  +    	public String name;
  +    	
  +    	/**
  +    	 * Any modification of the specified (static) resources will cause a 
  +    	 * redeployment of the application. If any of the specified resources is
  +    	 * removed, the application will be undeployed. Typically, this will
  +    	 * contain resources like the context.xml file, a compressed WAR path.
  +    	 */
  +    	public ArrayList redeployResources = new ArrayList();
  +
  +    	/**
  +    	 * Any modification of the specified (static) resources will cause a 
  +    	 * reload of the application. This will typically contain resources
  +    	 * such as the web.xml of a webapp, but can be configured to contain
  +    	 * additional descriptors.
  +    	 */
  +    	public ArrayList reloadResources = new ArrayList();
  +
  +    	/**
  +    	 * Instant where the application was last put in service.
  +    	 */
  +    	public long timestamp = System.currentTimeMillis();
  +    }
   
   }
  
  
  
  1.47      +348 -54   jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/ContextConfig.java
  
  Index: ContextConfig.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/ContextConfig.java,v
  retrieving revision 1.46
  retrieving revision 1.47
  diff -u -r1.46 -r1.47
  --- ContextConfig.java	26 Jun 2004 17:41:31 -0000	1.46
  +++ ContextConfig.java	23 Jul 2004 22:57:34 -0000	1.47
  @@ -41,6 +41,7 @@
   import org.apache.catalina.core.ContainerBase;
   import org.apache.catalina.core.StandardContext;
   import org.apache.catalina.core.StandardEngine;
  +import org.apache.catalina.core.StandardHost;
   import org.apache.catalina.deploy.ErrorPage;
   import org.apache.catalina.deploy.FilterDef;
   import org.apache.catalina.deploy.FilterMap;
  @@ -49,6 +50,7 @@
   import org.apache.catalina.session.StandardManager;
   import org.apache.catalina.util.StringManager;
   import org.apache.tomcat.util.digester.Digester;
  +import org.apache.tomcat.util.digester.RuleSet;
   import org.xml.sax.ErrorHandler;
   import org.xml.sax.InputSource;
   import org.xml.sax.SAXParseException;
  @@ -65,7 +67,7 @@
   public final class ContextConfig
       implements LifecycleListener {
   
  -    private static org.apache.commons.logging.Log log=
  +    protected static org.apache.commons.logging.Log log=
           org.apache.commons.logging.LogFactory.getLog( ContextConfig.class );
   
       // ----------------------------------------------------- Instance Variables
  @@ -75,62 +77,69 @@
        * the name of the implemented authentication method, and the value is
        * the fully qualified Java class name of the corresponding Valve.
        */
  -    private static Properties authenticators = null;
  +    protected static Properties authenticators = null;
   
   
       /**
        * The Context we are associated with.
        */
  -    private Context context = null;
  +    protected Context context = null;
   
   
       /**
        * The default web application's deployment descriptor location.
        */
  -    private String defaultWebXml = null;
  +    protected String defaultWebXml = null;
       
       
       /**
        * Track any fatal errors during startup configuration processing.
        */
  -    private boolean ok = false;
  +    protected boolean ok = false;
   
   
       /**
        * Any parse error which occurred while parsing XML descriptors.
        */
  -    private SAXParseException parseException = null;
  +    protected SAXParseException parseException = null;
   
   
       /**
        * The string resources for this package.
        */
  -    private static final StringManager sm =
  +    protected static final StringManager sm =
           StringManager.getManager(Constants.Package);
   
   
       /**
        * The <code>Digester</code> we will use to process web application
  +     * context files.
  +     */
  +    protected static Digester contextDigester = null;
  +    
  +    
  +    /**
  +     * The <code>Digester</code> we will use to process web application
        * deployment descriptor files.
        */
  -    private static Digester webDigester = null;
  +    protected static Digester webDigester = null;
       
       
       /**
        * The <code>Rule</code> used to parse the web.xml
        */
  -    private static WebRuleSet webRuleSet = new WebRuleSet();
  +    protected static WebRuleSet webRuleSet = new WebRuleSet();
   
       /**
        * Attribute value used to turn on/off XML validation
        */
  -     private static boolean xmlValidation = false;
  +     protected static boolean xmlValidation = false;
   
   
       /**
        * Attribute value used to turn on/off XML namespace awarenes.
        */
  -    private static boolean xmlNamespaceAware = false;
  +    protected static boolean xmlNamespaceAware = false;
   
           
       // ------------------------------------------------------------- Properties
  @@ -162,7 +171,7 @@
   
   
       /**
  -     * Process the START event for an associated Context.
  +     * Process events for an associated Context.
        *
        * @param event The lifecycle event that has occurred
        */
  @@ -171,33 +180,32 @@
           // Identify the context we are associated with
           try {
               context = (Context) event.getLifecycle();
  -//             if (context instanceof StandardContext) {
  -//                 int contextDebug = ((StandardContext) context).getDebug();
  -//                 if (contextDebug > this.debug)
  -//                     this.debug = contextDebug;
  -//             }
           } catch (ClassCastException e) {
               log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
               return;
           }
   
  -        // Called from ContainerBase.addChild() -> StandardContext.start()
           // Process the event that has occurred
  -        if (event.getType().equals(Lifecycle.START_EVENT))
  +        if (event.getType().equals(Lifecycle.START_EVENT)) {
               start();
  -        else if (event.getType().equals(Lifecycle.STOP_EVENT))
  +        } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
               stop();
  +        } else if (event.getType().equals(Lifecycle.INIT_EVENT)) {
  +            init();
  +        } else if (event.getType().equals(Lifecycle.DESTROY_EVENT)) {
  +            destroy();
  +        }
   
       }
   
   
  -    // -------------------------------------------------------- Private Methods
  +    // -------------------------------------------------------- protected Methods
   
   
       /**
        * Process the application configuration file, if it exists.
        */
  -    private void applicationConfig() {
  +    protected void applicationWebConfig() {
   
           // Open the application web.xml file, if it exists
           InputStream stream = null;
  @@ -271,7 +279,7 @@
       /**
        * Set up a manager.
        */
  -    private synchronized void managerConfig() {
  +    protected synchronized void managerConfig() {
   
           if (context.getManager() == null) {
               if ((context.getCluster() != null) && context.getDistributable()) {
  @@ -294,7 +302,7 @@
        * Set up an Authenticator automatically if required, and one has not
        * already been configured.
        */
  -    private synchronized void authenticatorConfig() {
  +    protected synchronized void authenticatorConfig() {
   
           // Does this Context require an Authenticator?
           SecurityConstraint constraints[] = context.findConstraints();
  @@ -371,7 +379,7 @@
                   Pipeline pipeline = ((ContainerBase) context).getPipeline();
                   if (pipeline != null) {
                       ((ContainerBase) context).addValve(authenticator);
  -                    if( log.isDebugEnabled() )
  +                    if (log.isDebugEnabled())
                           log.debug(sm.getString("contextConfig.authenticatorConfigured",
                                        loginConfig.getAuthMethod()));
                   }
  @@ -389,14 +397,14 @@
        * Create (if necessary) and return a Digester configured to process the
        * web application deployment descriptor (web.xml).
        */
  -    private static Digester createWebDigester() {
  +    protected static Digester createWebDigester() {
           Digester webDigester =
               createWebXmlDigester(xmlNamespaceAware, xmlValidation);
           return webDigester;
       }
   
   
  -    /*
  +    /**
        * Create (if necessary) and return a Digester configured to process the
        * web application deployment descriptor (web.xml).
        */
  @@ -409,7 +417,23 @@
           return webDigester;
       }
   
  -    private String getBaseDir() {
  +    
  +    /**
  +     * Create (if necessary) and return a Digester configured to process the
  +     * context configuration descriptor for an application.
  +     */
  +    protected Digester createContextDigester() {
  +        Digester digester = new Digester();
  +        digester.setValidating(false);
  +        RuleSet contextRuleSet = new ContextRuleSet("", false);
  +        digester.addRuleSet(contextRuleSet);
  +        RuleSet namingRuleSet = new NamingRuleSet("Context/");
  +        digester.addRuleSet(namingRuleSet);
  +        return digester;
  +    }
  +
  +
  +    protected String getBaseDir() {
           Container engineC=context.getParent().getParent();
           if( engineC instanceof StandardEngine ) {
               return ((StandardEngine)engineC).getBaseDir();
  @@ -422,7 +446,7 @@
        * The default config must be read with the container loader - so
        * container servlets can be loaded
        */
  -    private void defaultConfig() {
  +    protected void defaultWebConfig() {
           long t1=System.currentTimeMillis();
   
           // Open the default web.xml file, if it exists
  @@ -452,8 +476,6 @@
                               .getResource(defaultWebXml).toString());
                   } else {
                       log.info("No default web.xml");
  -                    // no default web.xml
  -                    return;
                   }
               } else {
                   source =
  @@ -463,27 +485,64 @@
           } catch (Exception e) {
               log.error(sm.getString("contextConfig.defaultMissing") 
                         + " " + defaultWebXml + " " + file , e);
  -            return;
           }
   
           if (webDigester == null){
               webDigester = createWebDigester();
           }
           
  +        if (stream != null) {
  +            processDefaultWebConfig(webDigester, stream, source);
  +            webRuleSet.recycle();
  +        }
  +
  +        long t2=System.currentTimeMillis();
  +        if( (t2-t1) > 200 )
  +            log.debug("Processed default web.xml " + file + " "  + ( t2-t1));
  +
  +        stream = null;
  +        source = null;
  +
  +        file = new File(getConfigBase(), "web.xml.default");
  +        
  +        try {
  +           if (file.exists()) {
  +                source =
  +                    new InputSource("file://" + file.getAbsolutePath());
  +                stream = new FileInputStream(file);
  +            }
  +        } catch (Exception e) {
  +            log.error(sm.getString("contextConfig.defaultMissing") 
  +                      + " " + file , e);
  +        }
  +        
  +        if (stream != null) {
  +            processDefaultWebConfig(webDigester, stream, source);
  +            webRuleSet.recycle();
  +        }
  +
  +    }
  +
  +
  +    /**
  +     * Process a default web.xml.
  +     */
  +    protected void processDefaultWebConfig(Digester digester, InputStream stream, 
  +            InputSource source) {
           // Process the default web.xml file
  -        synchronized (webDigester) {
  +        synchronized (digester) {
               try {
                   source.setByteStream(stream);
                   
                   if (context instanceof StandardContext)
                       ((StandardContext) context).setReplaceWelcomeFiles(true);
  -                webDigester.clear();
  -                webDigester.setClassLoader(this.getClass().getClassLoader());
  +                digester.clear();
  +                digester.setClassLoader(this.getClass().getClassLoader());
                   //log.info( "Using cl: " + webDigester.getClassLoader());
  -                webDigester.setUseContextClassLoader(false);
  -                webDigester.push(context);
  -                webDigester.setErrorHandler(new ContextErrorHandler());
  -                webDigester.parse(source);
  +                digester.setUseContextClassLoader(false);
  +                digester.push(context);
  +                digester.setErrorHandler(new ContextErrorHandler());
  +                digester.parse(source);
                   if (parseException != null) {
                       ok = false;
                   }
  @@ -507,24 +566,217 @@
                   }
               }
           }
  -        webRuleSet.recycle();
  +    }
  +
  +
  +    /**
  +     * Process the default configuration file, if it exists.
  +     */
  +    protected void contextConfig() {
           
  -        long t2=System.currentTimeMillis();
  -        if( (t2-t1) > 200 )
  -            log.debug("Processed default web.xml " + file + " "  + ( t2-t1));
  +        // FIXME: Externalize default context.xml path the same way as web.xml
  +        processContextConfig(new File(getBaseDir(), "conf/context.xml"));
  +        processContextConfig(new File(getConfigBase(), "context.xml.default"));
  +        if (context.getConfigFile() != null)
  +            processContextConfig(new File(context.getConfigFile()));
  +        
  +    }
  +
  +    
  +    /**
  +     * Process a context.xml.
  +     */
  +    protected void processContextConfig(File file) {
  +        if (log.isDebugEnabled())
  +            log.debug("Processing context [" + context.getName() + "] configuration file " + file);
  +        InputSource source = null;
  +        InputStream stream = null;
  +        try {
  +            if (file.exists()) {
  +                stream = new FileInputStream(file);
  +                source =
  +                    new InputSource("file://" + file.getAbsolutePath());
  +            } else if (log.isDebugEnabled()) {
  +                log.debug("Context [" + context.getName() + "] configuration file " + file + " not found");
  +            }
  +        } catch (Exception e) {
  +            log.error(sm.getString("contextConfig.defaultMissing") + file);
  +        }
  +        if (source == null)
  +            return;
  +        if (contextDigester == null){
  +            contextDigester = createContextDigester();
  +        }
  +        synchronized (contextDigester) {
  +            try {
  +                source.setByteStream(stream);
  +                contextDigester.clear();
  +                contextDigester.setClassLoader(this.getClass().getClassLoader());
  +                //log.info( "Using cl: " + webDigester.getClassLoader());
  +                contextDigester.setUseContextClassLoader(false);
  +                contextDigester.push(context.getParent());
  +                contextDigester.push(context);
  +                contextDigester.setErrorHandler(new ContextErrorHandler());
  +                contextDigester.parse(source);
  +                if (parseException != null) {
  +                    ok = false;
  +                }
  +            } catch (SAXParseException e) {
  +                log.error(sm.getString("contextConfig.defaultParse"), e);
  +                log.error(sm.getString("contextConfig.defaultPosition",
  +                                 "" + e.getLineNumber(),
  +                                 "" + e.getColumnNumber()));
  +                ok = false;
  +            } catch (Exception e) {
  +                log.error(sm.getString("contextConfig.defaultParse"), e);
  +                ok = false;
  +            } finally {
  +                parseException = null;
  +                try {
  +                    if (stream != null) {
  +                        stream.close();
  +                    }
  +                } catch (IOException e) {
  +                    log.error(sm.getString("contextConfig.defaultClose"), e);
  +                }
  +            }
  +        }
       }
   
  +    
  +    /**
  +     * Adjust docBase.
  +     */
  +    protected void fixDocBase()
  +        throws IOException {
  +        
  +        Host host = (Host) context.getParent();
  +        String appBase = host.getAppBase();
  +
  +        boolean unpackWARs = true;
  +        if (host instanceof StandardHost) {
  +            unpackWARs = ((StandardHost) host).isUnpackWARs() 
  +                && ((StandardContext) context).getUnpackWAR();
  +        }
  +
  +        File canonicalAppBase = new File(appBase);
  +        if (canonicalAppBase.isAbsolute()) {
  +            canonicalAppBase = canonicalAppBase.getCanonicalFile();
  +        } else {
  +            canonicalAppBase = 
  +                new File(System.getProperty("catalina.base"), appBase)
  +                .getCanonicalFile();
  +        }
  +
  +        String docBase = context.getDocBase();
  +        if (docBase == null) {
  +            // Trying to guess the docBase according to the path
  +            String path = context.getPath();
  +            if (path == null) {
  +                return;
  +            }
  +            if (path.equals("")) {
  +                docBase = "ROOT";
  +            } else {
  +                if (path.startsWith("/")) {
  +                    docBase = path.substring(1);
  +                } else {
  +                    docBase = path;
  +                }
  +            }
  +        }
  +
  +        File file = new File(docBase);
  +        if (!file.isAbsolute()) {
  +            docBase = (new File(canonicalAppBase, docBase)).getPath();
  +        } else {
  +            docBase = file.getCanonicalPath();
  +        }
  +
  +        if (docBase.toLowerCase().endsWith(".war") && unpackWARs) {
  +            URL war = new URL("jar:" + (new File(docBase)).toURL() + "!/");
  +            String contextPath = context.getPath();
  +            if (contextPath.equals("")) {
  +                contextPath = "ROOT";
  +            }
  +            docBase = ExpandWar.expand(host, war, contextPath);
  +            file = new File(docBase);
  +            docBase = file.getCanonicalPath();
  +        } else {
  +            File docDir = new File(docBase);
  +            if (!docDir.exists()) {
  +                File warFile = new File(docBase + ".war");
  +                if (warFile.exists()) {
  +                    if (unpackWARs) {
  +                        URL war = new URL("jar:" + warFile.toURL() + "!/");
  +                        docBase = ExpandWar.expand(host, war, context.getPath());
  +                        file = new File(docBase);
  +                        docBase = file.getCanonicalPath();
  +                    } else {
  +                        docBase = warFile.getCanonicalPath();
  +                    }
  +                }
  +            }
  +        }
  +
  +        if (docBase.startsWith(canonicalAppBase.getPath())) {
  +            docBase = docBase.substring(canonicalAppBase.getPath().length());
  +            docBase = docBase.replace(File.separatorChar, '/');
  +            if (docBase.startsWith("/")) {
  +                docBase = docBase.substring(1);
  +            }
  +        } else {
  +            docBase = docBase.replace(File.separatorChar, '/');
  +        }
  +
  +        context.setDocBase(docBase);
  +
  +    }
  +    
  +    
  +    protected void antiLocking()
  +        throws IOException {
  +        // FIXME: Implement anti locking, if it is enabled, by copying the whole
  +        // contents of the docBase to a unique folder in temp, and setting the docBase
  +        // to point to that
  +    }
  +    
   
       /**
  -     * Process a "start" event for this Context - in background
  +     * Process a "init" event for this Context.
        */
  -    private synchronized void start() {
  -        // Called from StandardContext.start()
  +    protected void init() {
  +        // Called from StandardContext.init()
   
           if (log.isDebugEnabled())
  -            log.debug(sm.getString("contextConfig.start"));
  +            log.debug(sm.getString("contextConfig.init"));
           context.setConfigured(false);
           ok = true;
  +        
  +        contextConfig();
  +        
  +        try {
  +            fixDocBase();
  +        } catch (IOException e) {
  +            log.error(sm.getString("contextConfig.fixDocBase"), e);
  +        }
  +        try {
  +            antiLocking();
  +        } catch (IOException e) {
  +            log.error(sm.getString("contextConfig.antiLocking"), e);
  +        }
  +        
  +    }
  +    
  +    
  +    /**
  +     * Process a "start" event for this Context.
  +     */
  +    protected synchronized void start() {
  +        // Called from StandardContext.start()
  +
  +        if (log.isDebugEnabled())
  +            log.debug(sm.getString("contextConfig.start"));
   
           // Set properties based on DefaultContext
           Container container = context.getParent();
  @@ -553,8 +805,8 @@
           }
   
           // Process the default and application web.xml files
  -        defaultConfig();
  -        applicationConfig();
  +        defaultWebConfig();
  +        applicationWebConfig();
           if (ok) {
               validateSecurityRoles();
           }
  @@ -596,7 +848,7 @@
       /**
        * Process a "stop" event for this Context.
        */
  -    private synchronized void stop() {
  +    protected synchronized void stop() {
   
           if (log.isDebugEnabled())
               log.debug(sm.getString("contextConfig.stop"));
  @@ -745,7 +997,19 @@
           ok = true;
   
       }
  -
  +    
  +    
  +    /**
  +     * Process a "destroy" event for this Context.
  +     */
  +    protected synchronized void destroy() {
  +        // Called from StandardContext.destroy()
  +        if (log.isDebugEnabled())
  +            log.debug(sm.getString("contextConfig.destroy"));
  +        
  +    }
  +    
  +    
       /**
        * Validate the usage of security role names in the web application
        * deployment descriptor.  If any problems are found, issue warning
  @@ -753,7 +1017,7 @@
        * (To make these problems fatal instead, simply set the <code>ok</code>
        * instance variable to <code>false</code> as well).
        */
  -    private void validateSecurityRoles() {
  +    protected void validateSecurityRoles() {
   
           // Check role names used in <security-constraint> elements
           SecurityConstraint constraints[] = context.findConstraints();
  @@ -790,7 +1054,37 @@
       }
   
   
  -    private class ContextErrorHandler
  +    /**
  +     * Get config base.
  +     */
  +    protected File getConfigBase() {
  +        File configBase = 
  +            new File(System.getProperty("catalina.base"), "conf");
  +        if (!configBase.exists()) {
  +            return null;
  +        }
  +        Container container = context;
  +        Container host = null;
  +        Container engine = null;
  +        while (container != null) {
  +            if (container instanceof Host)
  +                host = container;
  +            if (container instanceof Engine)
  +                engine = container;
  +            container = container.getParent();
  +        }
  +        if (engine != null) {
  +            configBase = new File(configBase, engine.getName());
  +        }
  +        if (host != null) {
  +            configBase = new File(configBase, host.getName());
  +        }
  +        configBase.mkdirs();
  +        return configBase;
  +    }
  +
  +
  +    protected class ContextErrorHandler
           implements ErrorHandler {
   
           public void error(SAXParseException exception) {
  
  
  
  1.10      +36 -10    jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/ContextRuleSet.java
  
  Index: ContextRuleSet.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/ContextRuleSet.java,v
  retrieving revision 1.9
  retrieving revision 1.10
  diff -u -r1.9 -r1.10
  --- ContextRuleSet.java	26 Jun 2004 17:41:31 -0000	1.9
  +++ ContextRuleSet.java	23 Jul 2004 22:57:34 -0000	1.10
  @@ -49,6 +49,12 @@
       protected String prefix = null;
   
   
  +    /**
  +     * Should the context be created.
  +     */
  +    protected boolean create = true;
  +
  +
       // ------------------------------------------------------------ Constructor
   
   
  @@ -79,6 +85,23 @@
       }
   
   
  +    /**
  +     * Construct an instance of this <code>RuleSet</code> with the specified
  +     * matching pattern prefix.
  +     *
  +     * @param prefix Prefix for matching pattern rules (including the
  +     *  trailing slash character)
  +     */
  +    public ContextRuleSet(String prefix, boolean create) {
  +
  +        super();
  +        this.namespaceURI = null;
  +        this.prefix = prefix;
  +        this.create = create;
  +
  +    }
  +
  +
       // --------------------------------------------------------- Public Methods
   
   
  @@ -93,27 +116,30 @@
        */
       public void addRuleInstances(Digester digester) {
   
  -        if (!isDefaultContext()) {
  -            digester.addObjectCreate(prefix + "Context",
  -                                     "org.apache.catalina.core.StandardContext",
  -                                     "className");
  -        } else {
  -            digester.addObjectCreate(prefix + "Context",
  -                                     "org.apache.catalina.core.StandardDefaultContext",
  -                                     "className");
  +        if (create) {
  +            if (!isDefaultContext()) {
  +                digester.addObjectCreate(prefix + "Context",
  +                        "org.apache.catalina.core.StandardContext",
  +                "className");
  +            } else {
  +                digester.addObjectCreate(prefix + "Context",
  +                        "org.apache.catalina.core.StandardDefaultContext",
  +                "className");
  +            }
           }
           digester.addSetProperties(prefix + "Context");
           if (!isDefaultContext()) {
               digester.addRule(prefix + "Context",
                                new CopyParentClassLoaderRule());
  +            if (create) {
               digester.addRule(prefix + "Context",
                                new LifecycleListenerRule
                                    ("org.apache.catalina.startup.ContextConfig",
                                     "configClass"));
  -            digester.addRule(prefix + "Context", new SetDocBaseRule());
               digester.addSetNext(prefix + "Context",
                                   "addChild",
                                   "org.apache.catalina.Container");
  +            }
           } else {
               digester.addSetNext(prefix + "Context",
                                   "addDefaultContext",
  
  
  
  1.134     +10 -2     jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/core/StandardContext.java
  
  Index: StandardContext.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/core/StandardContext.java,v
  retrieving revision 1.133
  retrieving revision 1.134
  diff -u -r1.133 -r1.134
  --- StandardContext.java	24 Jun 2004 22:09:52 -0000	1.133
  +++ StandardContext.java	23 Jul 2004 22:57:35 -0000	1.134
  @@ -4064,6 +4064,7 @@
           boolean ok = true;
   
           // Set config file name
  +        /*
           File configBase = getConfigBase();
           if ((configBase != null) && saveConfig) {
               if (getConfigFile() == null) {
  @@ -4103,6 +4104,7 @@
                   }
               }
           }
  +        */
   
           // Install DefaultContext configuration
           if (!getOverride()) {
  @@ -4589,6 +4591,9 @@
           } 
           super.destroy();
   
  +        // Notify our interested LifecycleListeners
  +        lifecycle.fireLifecycleEvent(DESTROY_EVENT, null);
  +
           instanceListeners = new String[0];
           applicationListeners = new String[0];
       }
  @@ -4843,7 +4848,7 @@
       /**
        * Get config base.
        */
  -    private File getConfigBase() {
  +    public File getConfigBase() {
           File configBase = 
               new File(System.getProperty("catalina.base"), "conf");
           if (!configBase.exists()) {
  @@ -5457,6 +5462,9 @@
           }
           super.init();
           
  +        // Notify our interested LifecycleListeners
  +        lifecycle.fireLifecycleEvent(INIT_EVENT, null);
  +
           // Send j2ee.state.starting notification 
           if (this.getObjectName() != null) {
               Notification notification = new Notification("j2ee.state.starting", 
  
  
  
  1.3       +13 -1     jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/Lifecycle.java
  
  Index: Lifecycle.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/Lifecycle.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Lifecycle.java	27 Feb 2004 14:58:38 -0000	1.2
  +++ Lifecycle.java	23 Jul 2004 22:57:35 -0000	1.3
  @@ -35,6 +35,12 @@
   
   
       /**
  +     * The LifecycleEvent type for the "component init" event.
  +     */
  +    public static final String INIT_EVENT = "init";
  +
  +
  +    /**
        * The LifecycleEvent type for the "component start" event.
        */
       public static final String START_EVENT = "start";
  @@ -68,6 +74,12 @@
        * The LifecycleEvent type for the "component after stop" event.
        */
       public static final String AFTER_STOP_EVENT = "after_stop";
  +
  +
  +    /**
  +     * The LifecycleEvent type for the "component destroy" event.
  +     */
  +    public static final String DESTROY_EVENT = "destroy";
   
   
       // --------------------------------------------------------- Public Methods
  
  
  
  1.14      +3 -1      jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/Context.java
  
  Index: Context.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/Context.java,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- Context.java	23 Jun 2004 16:59:42 -0000	1.13
  +++ Context.java	23 Jul 2004 22:57:35 -0000	1.14
  @@ -18,6 +18,8 @@
   package org.apache.catalina;
   
   
  +import java.io.File;
  +
   import javax.servlet.ServletContext;
   
   import org.apache.tomcat.util.http.mapper.Mapper;
  
  
  
  1.9       +17 -1     jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/Host.java
  
  Index: Host.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/Host.java,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- Host.java	27 Feb 2004 14:58:38 -0000	1.8
  +++ Host.java	23 Jul 2004 22:57:35 -0000	1.9
  @@ -99,6 +99,22 @@
   
   
       /**
  +     * Return the Java class name of the context configuration class
  +     * for new web applications.
  +     */
  +    public String getConfigClass();
  +
  +    
  +    /**
  +     * Set the Java class name of the context configuration class
  +     * for new web applications.
  +     *
  +     * @param configClass The new context configuration class
  +     */
  +    public void setConfigClass(String configClass);
  +
  +        
  +        /**
        * Set the DefaultContext
        * for new web applications.
        *
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: tomcat-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tomcat-dev-help@jakarta.apache.org