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/26 01:35:38 UTC

cvs commit: jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup HostConfig.java ContextConfig.java ExpandWar.java

remm        2004/07/25 16:35:37

  Modified:    catalina/src/share/org/apache/catalina/startup
                        HostConfig.java ContextConfig.java ExpandWar.java
  Log:
  - Implement a significant amount of reload/redeployment.
  - Since I'm a happy (?) user of MickeyMouse(TM) OS (aka Windows), I can't really test stuff without the anti-locking code
    which I haven't implemented yet. I'll do that tomorrow (this seems simple).
  - I used some ideas from Peter, and managed to notice code I could remove. Thanks.
  - Please don't file bugs on this until I've at least tested a bit :)
  - I need to add one extra method and refactor a bit to allow usage through the manager webapp (the manager webapp
    will want synced operation, I think, so the background thread won't do it in that case - although it would do the job
    eventually).
  - I'll also add the new extra configuration items tomorrow, such as the "watchedfile" feature (that's simple).
  
  Revision  Changes    Path
  1.35      +230 -159  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.34
  retrieving revision 1.35
  diff -u -r1.34 -r1.35
  --- HostConfig.java	23 Jul 2004 22:57:34 -0000	1.34
  +++ HostConfig.java	25 Jul 2004 23:35:37 -0000	1.35
  @@ -23,7 +23,6 @@
   import java.io.FileOutputStream;
   import java.io.IOException;
   import java.io.InputStream;
  -import java.net.URL;
   import java.util.ArrayList;
   import java.util.HashMap;
   import java.util.jar.JarEntry;
  @@ -36,6 +35,7 @@
   import org.apache.catalina.Lifecycle;
   import org.apache.catalina.LifecycleEvent;
   import org.apache.catalina.LifecycleListener;
  +import org.apache.catalina.core.ContainerBase;
   import org.apache.catalina.core.StandardHost;
   import org.apache.catalina.util.StringManager;
   import org.apache.tomcat.util.digester.Digester;
  @@ -422,14 +422,13 @@
   
           File appBase = appBase();
           File configBase = configBase();
  -        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()));
  -
  +        // Deploy XML descriptors from configBase
  +        deployDescriptors(configBase, configBase.list());
  +        // Deploy WARs, and loop if additional descriptors are found
  +        deployWARs(appBase, appBase.list());
  +        // Deploy expanded folders
  +        deployDirectories(appBase, appBase.list());
  +        
       }
   
   
  @@ -460,6 +459,8 @@
                   if (deployed.containsKey(contextPath))
                       continue;
   
  +                DeployedApplication deployedApp = new DeployedApplication(contextPath);
  +                
                   // Assume this is a configuration descriptor and deploy it
                   log.debug(sm.getString("hostConfig.deployDescriptor", files[i]));
                   try {
  @@ -475,15 +476,45 @@
                       }
                       newContext.setConfigFile(contextXml.getAbsolutePath());
                       newContext.setPath(contextPath);
  +                    // Add the context XML to the list of watched files
  +                    deployedApp.reloadResources.put
  +                        (contextXml.getAbsolutePath(), new Long(contextXml.lastModified()));
  +                    // Add the associated docBase to the redeployed list if it's a WAR
  +                    boolean isWar = false;
  +                    if (newContext.getDocBase() != null) {
  +                        File docBase = new File(newContext.getDocBase());
  +                        if (!docBase.isAbsolute()) {
  +                            docBase = new File(new File(host.getAppBase()), 
  +                                    newContext.getDocBase());
  +                        }
  +                        deployedApp.redeployResources.put(docBase.getAbsolutePath(),
  +                                new Long(docBase.lastModified()));
  +                        if (docBase.getAbsolutePath().toLowerCase().endsWith(".war")) {
  +                            isWar = true;
  +                        }
  +                    }
                       host.addChild(newContext);
  +                    // Add the eventual unpacked WAR and all the resources which will be
  +                    // watched inside it
  +                    if (isWar && unpackWARs && (newContext.getDocBase() != null)) {
  +                        File docBase = new File(newContext.getDocBase());
  +                        if (!docBase.isAbsolute()) {
  +                            docBase = new File(new File(host.getAppBase()), 
  +                                    newContext.getDocBase());
  +                        }
  +                        deployedApp.redeployResources.put(docBase.getAbsolutePath(),
  +                                new Long(docBase.lastModified()));
  +                        // FIXME: Add the list of reload resources as given by the context
  +                        //        This list would by default contain /WEB-INF/web.xml and
  +                        //        /META-INF/context.xml
  +                        //        Add new element in Context to configure this
  +                    }
                   } 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
  +                deployed.put(contextPath, deployedApp);
                   
               }
   
  @@ -495,22 +526,22 @@
       /**
        * Deploy WAR files.
        */
  -    protected boolean deployWARs(File appBase, String[] files) {
  -
  +    protected void deployWARs(File appBase, String[] files) {
  +        
           if (files == null)
  -            return false;
  +            return;
           
           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;
               File dir = new File(appBase, files[i]);
               if (files[i].toLowerCase().endsWith(".war")) {
  -
  +                
                   // Calculate the context path and make sure it is unique
                   String contextPath = "/" + files[i];
                   int period = contextPath.lastIndexOf(".");
  @@ -518,137 +549,128 @@
                       contextPath = contextPath.substring(0, period);
                   if (contextPath.equals("/ROOT"))
                       contextPath = "";
  -
  +                
                   if (deployed.containsKey(contextPath))
                       continue;
  -
  -                if (isUnpackWARs()) {
  -
  -                    // Expand and deploy this application as a directory
  -                    log.debug(sm.getString("hostConfig.expand", files[i]));
  -                    URL url = null;
  -                    String path = null;
  +                
  +                // 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 {
  -                        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]));
  -                    } catch (Throwable t) {
  -                        log.error(sm.getString
  -                                  ("hostConfig.expand.error", files[i]), t);
  -                    }
  -                    
  -                    // 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);
  +                        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.flush();
  -                                ostream.close();
  -                                ostream = null;
  -                                istream.close();
  -                                istream = null;
  -                                entry = null;
  -                                jar.close();
  -                                jar = null;
  -                                //deployDescriptors(configBase(), configBase.list());
  -                                checkAdditionalDeployments = true;
  -                                continue;
  +                                ostream.write(buffer, 0, n);
                               }
  -                        } catch (Exception e) {
  -                            // Ignore and continue
  -                            if (ostream != null) {
  -                                try {
  -                                    ostream.close();
  -                                } catch (Throwable t) {
  -                                    ;
  -                                }
  -                                ostream = null;
  +                            ostream.flush();
  +                            ostream.close();
  +                            ostream = null;
  +                            istream.close();
  +                            istream = null;
  +                            entry = null;
  +                            jar.close();
  +                            jar = null;
  +                        }
  +                    } catch (Exception e) {
  +                        // Ignore and continue
  +                        if (ostream != null) {
  +                            try {
  +                                ostream.close();
  +                            } catch (Throwable t) {
  +                                ;
                               }
  -                            if (istream != null) {
  -                                try {
  -                                    istream.close();
  -                                } catch (Throwable t) {
  -                                    ;
  -                                }
  -                                istream = null;
  +                            ostream = null;
  +                        }
  +                        if (istream != null) {
  +                            try {
  +                                istream.close();
  +                            } catch (Throwable t) {
  +                                ;
                               }
  -                        } finally {
  -                            entry = null;
  -                            if (jar != null) {
  -                                try {
  -                                    jar.close();
  -                                } catch (Throwable t) {
  -                                    ;
  -                                }
  -                                jar = null;
  +                            istream = null;
  +                        }
  +                    } finally {
  +                        entry = null;
  +                        if (jar != null) {
  +                            try {
  +                                jar.close();
  +                            } catch (Throwable t) {
  +                                ;
                               }
  +                            jar = null;
                           }
                       }
  -
  -                    // Deploy the application in this WAR file
  -                    log.info(sm.getString("hostConfig.deployJar", files[i]));
  -                    try {
  -                        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);
  +                }
  +                
  +                DeployedApplication deployedApp = new DeployedApplication(contextPath);
  +                
  +                // Deploy the application in this WAR file
  +                log.info(sm.getString("hostConfig.deployJar", files[i]));
  +                try {
  +                    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]);
  +                    if (xml.exists()) {
  +                        context.setConfigFile(xml.getAbsolutePath());
  +                        deployedApp.reloadResources.put
  +                            (xml.getAbsolutePath(), new Long(xml.lastModified()));
  +                    }
  +                    host.addChild(context);
  +                    // If we're unpacking WARs, the docBase will be mutated after
  +                    // starting the context
  +                    if (unpackWARs && (context.getDocBase() != null)) {
  +                        File docBase = new File(context.getDocBase());
  +                        if (!docBase.isAbsolute()) {
  +                            docBase = new File(new File(host.getAppBase()), 
  +                                    context.getDocBase());
                           }
  -                        context.setPath(contextPath);
  -                        context.setDocBase(files[i]);
  -                        host.addChild(context);
  -                    } catch (Throwable t) {
  -                        log.error(sm.getString("hostConfig.deployJar.error",
  -                                         files[i]), t);
  +                        deployedApp.redeployResources.put(docBase.getAbsolutePath(),
  +                                new Long(docBase.lastModified()));
  +                        // FIXME: Add the list of reload resources as given by the context
  +                        //        This list would by default contain /WEB-INF/web.xml and
  +                        //        /META-INF/context.xml
  +                        //        Add new element in Context to configure this
                       }
  -
  -                    deployed.put(contextPath, new DeployedApplication(contextPath));
  -                    // FIXME: populate needed resources, such as the docBase (context file, WAR, expanded)
  -                    // FIXME: populate watched resources
  -
  +                } catch (Throwable t) {
  +                    log.error(sm.getString("hostConfig.deployJar.error",
  +                            files[i]), t);
                   }
  -
  +                
  +                // Populate redeploy resources with the WAR file
  +                deployedApp.redeployResources.put
  +                    (dir.getAbsolutePath(), new Long(dir.lastModified()));
  +                deployed.put(contextPath, deployedApp);
  +                
               }
  -
  +            
           }
           
  -        return checkAdditionalDeployments;
  -
       }
   
   
  @@ -686,10 +708,11 @@
                   if (deployed.containsKey(contextPath))
                       continue;
   
  +                DeployedApplication deployedApp = new DeployedApplication(contextPath);
  +                
                   // Deploy the application in this directory
                   if( log.isDebugEnabled() ) 
                       log.debug(sm.getString("hostConfig.deployDir", files[i]));
  -                long t1=System.currentTimeMillis();
                   try {
                       Context context = (Context) Class.forName(contextClass).newInstance();
                       if (context instanceof Lifecycle) {
  @@ -703,17 +726,18 @@
                       // 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);
  +                    deployedApp.redeployResources.put(dir.getAbsolutePath(),
  +                            new Long(dir.lastModified()));
  +                    // FIXME: Add the list of reload resources as given by the context
  +                    //        This list would by default contain /WEB-INF/web.xml and
  +                    //        /META-INF/context.xml
  +                    //        Add new element in Context to configure this
                   } catch (Throwable t) {
                       log.error(sm.getString("hostConfig.deployDir.error", files[i]),
                           t);
                   }
  -                long t2=System.currentTimeMillis();
  -                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
  +
  +                deployed.put(contextPath, deployedApp);
               
               }
   
  @@ -726,32 +750,62 @@
        * Check resources for redeployment and reloading.
        */
       protected synchronized void checkResources(DeployedApplication app) {
  -        if (isServiced(app.name))
  -            return;
  -        String[] resources = (String[]) app.redeployResources.toArray(new String[0]);
  +        String[] resources = (String[]) app.redeployResources.keySet().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) {
  +            if (log.isDebugEnabled())
  +                log.debug("Checking context[" + app.name + "] redeploy resource " + resource);
  +            if (resource.exists()) {
  +                long lastModified = ((Long) app.redeployResources.get(resources[i])).longValue();
  +                if ((!resource.isDirectory()) && resource.lastModified() > lastModified) {
                       // 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);
  +                    ContainerBase context = (ContainerBase) host.findChild(app.name);
  +                    host.removeChild(context);
  +                    try {
  +                        context.destroy();
  +                    } catch (Exception e) {
  +                        log.warn(sm.getString
  +                                 ("hostConfig.context.destroy", app.name), e);
  +                    }
  +                    // Delete other redeploy resources
  +                    for (int j = 0; j < resources.length; j++) {
  +                        if (j != i) {
  +                            ExpandWar.delete(new File(resources[j]));
  +                        }
  +                    }
  +                    deployed.remove(app.name);
                       return;
                   }
               } else {
                   // Undeploy application
  -                host.removeChild(host.findChild(app.name));
  -                // FIXME: Delete all redeploy resources
  -                deployed.remove(app);
  +                ContainerBase context = (ContainerBase) host.findChild(app.name);
  +                host.removeChild(context);
  +                try {
  +                    context.destroy();
  +                } catch (Exception e) {
  +                    log.warn(sm.getString
  +                             ("hostConfig.context.destroy", app.name), e);
  +                }
  +                // Delete all redeploy resources
  +                for (int j = 0; j < resources.length; j++) {
  +                    ExpandWar.delete(new File(resources[j]));
  +                }
  +                // Delete reload resources as well (to remove any remaining .xml descriptor)
  +                String[] resources2 = (String[]) app.reloadResources.keySet().toArray(new String[0]);
  +                for (int j = 0; j < resources2.length; j++) {
  +                    ExpandWar.delete(new File(resources2[j]));
  +                }
  +                deployed.remove(app.name);
                   return;
               }
           }
  -        resources = (String[]) app.reloadResources.toArray(new String[0]);
  +        resources = (String[]) app.reloadResources.keySet().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)) {
  +            if (log.isDebugEnabled())
  +                log.debug("Checking context[" + app.name + "] reload resource " + resource);
  +            long lastModified = ((Long) app.reloadResources.get(resources[i])).longValue();
  +            if ((!resource.exists()) || (resource.lastModified() != lastModified)) {
                   // Reload application
                   Container context = host.findChild(app.name);
                   try {
  @@ -825,7 +879,7 @@
   
   
       /**
  -     * Deploy webapps.
  +     * Check status of all webapps.
        */
       protected void check() {
   
  @@ -834,7 +888,8 @@
               DeployedApplication[] apps = 
                   (DeployedApplication[]) deployed.values().toArray(new DeployedApplication[0]);
               for (int i = 0; i < apps.length; i++) {
  -                checkResources(apps[i]);
  +                if (!isServiced(apps[i].name))
  +                    checkResources(apps[i]);
               }
               // Hotdeploy applications
               deployApps();
  @@ -843,6 +898,20 @@
       }
   
       
  +    /**
  +     * Check status of a specific webapp, for use with stuff like management webapps.
  +     */
  +    protected void check(String name) {
  +        DeployedApplication app = (DeployedApplication) deployed.get(name);
  +        if (app != null) {
  +            checkResources(app);
  +        } else {
  +            // FIXME: add a version of deployApps which would check only for
  +            //        deployment of a specified app.name
  +        }
  +    }
  +    
  +    
       // ----------------------------------------------------- Instance Variables
   
   
  @@ -866,16 +935,18 @@
       	 * 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.
  +         * The value is the last modification time.
       	 */
  -    	public ArrayList redeployResources = new ArrayList();
  +    	public HashMap redeployResources = new HashMap();
   
       	/**
       	 * 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.
  +         * The value is the last modification time.
       	 */
  -    	public ArrayList reloadResources = new ArrayList();
  +    	public HashMap reloadResources = new HashMap();
   
       	/**
       	 * Instant where the application was last put in service.
  
  
  
  1.48      +3 -3      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.47
  retrieving revision 1.48
  diff -u -r1.47 -r1.48
  --- ContextConfig.java	23 Jul 2004 22:57:34 -0000	1.47
  +++ ContextConfig.java	25 Jul 2004 23:35:37 -0000	1.48
  @@ -737,8 +737,8 @@
       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
  +        // contents of the docBase to a unique folder in temp, and setting the resources
  +        // to point to that. Note: Don't mutate the docBase here.
       }
       
   
  
  
  
  1.7       +18 -3     jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/ExpandWar.java
  
  Index: ExpandWar.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-catalina/catalina/src/share/org/apache/catalina/startup/ExpandWar.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- ExpandWar.java	23 Jun 2004 13:51:37 -0000	1.6
  +++ ExpandWar.java	25 Jul 2004 23:35:37 -0000	1.7
  @@ -187,7 +187,22 @@
        *
        * @param dir File object representing the directory to be deleted
        */
  -    public static void deleteDir(File dir) {
  +    public static boolean delete(File dir) {
  +        if (dir.isDirectory()) {
  +            return deleteDir(dir);
  +        } else {
  +            return dir.delete();
  +        }
  +    }
  +    
  +    
  +    /**
  +     * Delete the specified directory, including all of its contents and
  +     * subdirectories recursively.
  +     *
  +     * @param dir File object representing the directory to be deleted
  +     */
  +    public static boolean deleteDir(File dir) {
   
           String files[] = dir.list();
           if (files == null) {
  @@ -201,7 +216,7 @@
                   file.delete();
               }
           }
  -        dir.delete();
  +        return dir.delete();
   
       }
   
  
  
  

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