pero        2004/11/27 13:10:20

  Modified:    modules/cluster build.xml to-do.txt
               modules/cluster/src/share/org/apache/catalina/cluster
                        ClusterDeployer.java mbeans-descriptors.xml
               modules/cluster/src/share/org/apache/catalina/cluster/deploy
                        FarmWarDeployer.java FileMessageFactory.java
                        WarWatcher.java
               modules/cluster/src/share/org/apache/catalina/cluster/tcp
                        SimpleTcpCluster.java
  Log:
  Fix cluster FarmWarDeployer
  add controlled WarWatcher with engine backgroundProcess()
  add more log messages
  
  Revision  Changes    Path
  1.9       +1 -0      jakarta-tomcat-catalina/modules/cluster/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-catalina/modules/cluster/build.xml,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- build.xml 13 Jul 2004 15:24:47 -0000      1.8
  +++ build.xml 27 Nov 2004 21:10:19 -0000      1.9
  @@ -18,6 +18,7 @@
     <path id="cluster.classpath">
       <pathelement location="${catalina.build}/server/lib/catalina.jar"/>
       <pathelement location="${catalina.build}/server/lib/tomcat-util.jar"/>
  +    <pathelement location="${commons-modeler.jar}"/>
       <pathelement location="${commons-logging.jar}"/>
       <pathelement location="${jmx.jar}"/>
       <pathelement location="${catalina.build}/common/lib/servlet-api.jar"/>
  
  
  
  1.4       +20 -1     jakarta-tomcat-catalina/modules/cluster/to-do.txt
  
  Index: to-do.txt
  ===================================================================
  RCS file: /home/cvs/jakarta-tomcat-catalina/modules/cluster/to-do.txt,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- to-do.txt 29 Sep 2004 18:59:32 -0000      1.3
  +++ to-do.txt 27 Nov 2004 21:10:19 -0000      1.4
  @@ -1,12 +1,31 @@
   1. Fix farm deployment for 5.5
  +   pero:
  +     Every start all application are deployed only to all running cluster 
nodes
  +             New registered nodes don't get the applications!
  +             Deploy must send a GET_ALL_APP to all other Deployers.
  +                     Only watchEnabled Deployer send this member all 
deployed application.
  +     Add JMX Support
  +             Resend Deployed Applications to all or one cluster node.
  +             Show all watch Resource
  +             Processing Time
  +             Change fileMessage Buffersize.
  +             Start/Stop Cluster wide application
  +     Deployer and Watcher sync with engine background thread! 
  +             Fixed!          
  +     Last FileMessage fragment need longe ackTimeout 
  +     <Cluster ..> <Sender ... ackTimeout="60000"/> </Cluster>        
   2. Extend StandardSession if possible
   3. Implement primary/secondary replication logic
   4. Implement fragmentation of large replication objects
  -5. Implementa NonSerializable interface for session attributes that do not
  +5. Implement a NonSerializable interface for session attributes that do not
   wish to be replicated
   6. Implement context attribute replication (?)
  +     pero:
  +             Also send Start/Stop messages from Context to complete cluster!
   7. JMX friendly
   8. Documentation
   9. Add a flag for replicated  attribute events, to enable or disable them -
  +     pero:
  +             Why different defaults from notifyListenersOnReplication at 
SimpleTcpCluster and DeltaManager exists?
   COMPLETED
   
  
  
  
  1.4       +6 -1      
jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/ClusterDeployer.java
  
  Index: ClusterDeployer.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/ClusterDeployer.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- ClusterDeployer.java      26 Oct 2004 14:29:02 -0000      1.3
  +++ ClusterDeployer.java      27 Nov 2004 21:10:20 -0000      1.4
  @@ -98,4 +98,9 @@
        *  removal
        */
       public void remove(String contextPath, boolean undeploy) throws 
IOException;
  +
  +    /**
  +     * call from container Background Process
  +     */
  +    public void backgroundProcess();
   }
  
  
  
  1.2       +0 -8      
jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/mbeans-descriptors.xml
  
  Index: mbeans-descriptors.xml
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/mbeans-descriptors.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- mbeans-descriptors.xml    25 Apr 2003 21:14:36 -0000      1.1
  +++ mbeans-descriptors.xml    27 Nov 2004 21:10:20 -0000      1.2
  @@ -37,10 +37,6 @@
                    type="java.lang.String"
               writeable="false"/>
   
  -    <attribute   name="debug"
  -          description="The debugging detail level for this component"
  -                 type="int"/>
  -
       <attribute   name="distributable"
             description="The distributable flag for Sessions created by this
                          Manager"
  @@ -71,10 +67,6 @@
                          (for logging)"
                    type="java.lang.String"
               writeable="false"/>
  -
  -    <attribute   name="pathname"
  -          description="Path name of the disk file in which active sessions"
  -                 type="java.lang.String"/>
   
     </mbean>
   
  
  
  
  1.4       +503 -146  
jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FarmWarDeployer.java
  
  Index: FarmWarDeployer.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FarmWarDeployer.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- FarmWarDeployer.java      5 Sep 2004 22:00:52 -0000       1.3
  +++ FarmWarDeployer.java      27 Nov 2004 21:10:20 -0000      1.4
  @@ -16,131 +16,271 @@
   
   package org.apache.catalina.cluster.deploy;
   
  -import org.apache.catalina.cluster.ClusterDeployer;
  -import org.apache.catalina.cluster.ClusterMessage;
  -import org.apache.catalina.cluster.CatalinaCluster;
  -import org.apache.catalina.LifecycleException;
   import java.io.File;
  -import java.net.URL;
   import java.io.IOException;
  -import org.apache.catalina.cluster.Member;
  +import java.net.URL;
   import java.util.HashMap;
   
  +import javax.management.MBeanServer;
  +import javax.management.ObjectName;
  +
  +import org.apache.catalina.Context;
  +import org.apache.catalina.Engine;
  +import org.apache.catalina.Host;
  +import org.apache.catalina.Lifecycle;
  +import org.apache.catalina.LifecycleException;
  +import org.apache.catalina.cluster.CatalinaCluster;
  +import org.apache.catalina.cluster.ClusterDeployer;
  +import org.apache.catalina.cluster.ClusterMessage;
  +import org.apache.catalina.cluster.Member;
  +import org.apache.commons.modeler.Registry;
  +
   /**
    * <p>
  - * A farm war deployer is a class that is able to
  - * deploy/undeploy web applications in WAR form
  - * within the cluster.</p>
  + * A farm war deployer is a class that is able to deploy/undeploy web
  + * applications in WAR form within the cluster.
  + * </p>
    * Any host can act as the admin, and will have three directories
    * <ul>
  - * <li> deployDir - the directory where we watch for changes</li>
  - * <li> applicationDir - the directory where we install applications</li>
  - * <li> tempDir - a temporaryDirectory to store binary data when downloading 
a war
  - *      from the cluster </li>
  + * <li>deployDir - the directory where we watch for changes</li>
  + * <li>applicationDir - the directory where we install applications</li>
  + * <li>tempDir - a temporaryDirectory to store binary data when downloading a
  + * war from the cluster</li>
    * </ul>
  - * Currently we only support deployment of WAR files since they are easier 
to send
  - * across the wire.
  - *
  + * Currently we only support deployment of WAR files since they are easier to
  + * send across the wire.
  + * 
    * @author Filip Hanik
  - * @version 1.0
  + * @author Peter Rossbach
  + * @version 1.1
  + */
  +/**
  + * @author peter
  + *
  + * TODO To change the template for this generated type comment go to
  + * Window - Preferences - Java - Code Style - Code Templates
  + */
  +/**
  + * @author peter
  + * 
  + * TODO To change the template for this generated type comment go to Window -
  + * Preferences - Java - Code Style - Code Templates
    */
   public class FarmWarDeployer implements ClusterDeployer, FileChangeListener {
       /*--Static Variables----------------------------------------*/
  -    public static org.apache.commons.logging.Log log =
  -        org.apache.commons.logging.LogFactory.getLog(FarmWarDeployer.class);
  +    public static org.apache.commons.logging.Log log = 
org.apache.commons.logging.LogFactory
  +            .getLog(FarmWarDeployer.class);
  +
       /*--Instance Variables--------------------------------------*/
       protected CatalinaCluster cluster = null;
  +
       protected boolean started = false; //default 5 seconds
  +
       protected HashMap fileFactories = new HashMap();
  +
       protected String deployDir;
  +
       protected String tempDir;
  +
       protected String watchDir;
  +
       protected boolean watchEnabled = false;
  +
       protected WarWatcher watcher = null;
  +
  +    /**
  +     * Iteration count for background processing.
  +     */
  +    private int count = 0;
  +
  +    /**
  +     * Frequency of the Form deploydir check. Cluster wide deployment will be
  +     * done once for the specified amount of backgrondProcess calls (ie, the
  +     * lower the amount, the most often the checks will occur).
  +     */
  +    protected int processExpiresFrequency = 2;
  +
  +    /**
  +     * Path where context descriptors should be deployed.
  +     */
  +    protected File configBase = null;
  +
  +    /**
  +     * The associated host.
  +     */
  +    protected Host host = null;
  +
  +    /**
  +     * The host appBase.
  +     */
  +    protected File appBase = null;
  +
  +    /**
  +     * MBean server.
  +     */
  +    protected MBeanServer mBeanServer = null;
  +
  +    /**
  +     * The associated deployer ObjectName.
  +     */
  +    protected ObjectName oname = null;
  +
       /*--Constructor---------------------------------------------*/
       public FarmWarDeployer() {
       }
   
       /*--Logic---------------------------------------------------*/
       public void start() throws Exception {
  -        if (started)return;
  +        if (started)
  +            return;
           getCluster().addClusterListener(this);
           if (watchEnabled) {
  -            watcher = new WarWatcher(this, new File(getWatchDir()), (long) 
5000);
  -            Thread t = new Thread(watcher);
  -            t.start();
  -            log.info("Cluster deployment is watching " + getWatchDir() +
  -                     " for changes.");
  +            watcher = new WarWatcher(this, new File(getWatchDir()));
  +            if (log.isInfoEnabled())
  +                log.info("Cluster deployment is watching " + getWatchDir()
  +                        + " for changes.");
           } //end if
  +
  +        // Check to correct engine and host setup
  +        host = (Host) getCluster().getContainer();
  +        Engine engine = (Engine) host.getParent();
  +        try {
  +            oname = new ObjectName(engine.getName() + ":type=Deployer,host="
  +                    + host.getName());
  +        } catch (Exception e) {
  +            log.error("Can't construct MBean object name" + e);
  +        }
  +        configBase = new File(System.getProperty("catalina.base"), "conf");
  +        if (engine != null) {
  +            configBase = new File(configBase, engine.getName());
  +        }
  +        if (host != null) {
  +            configBase = new File(configBase, host.getName());
  +        }
  +
  +        // Retrieve the MBean server
  +        mBeanServer = Registry.getRegistry(null, null).getMBeanServer();
  +
           started = true;
  -        log.info("Cluster FarmWarDeployer started.");
  +        count = 0;
  +        if (log.isInfoEnabled())
  +            log.info("Cluster FarmWarDeployer started.");
       }
   
  +    /*
  +     * stop cluster wide deployments
  +     * 
  +     * @see org.apache.catalina.cluster.ClusterDeployer#stop()
  +     */
       public void stop() throws LifecycleException {
           started = false;
           getCluster().removeClusterListener(this);
  -        if (watcher != null) watcher.stop();
  -        log.info("Cluster FarmWarDeployer stopped.");
  +        count = 0;
  +        if (watcher != null) {
  +            watcher.clear();
  +            watcher = null;
  +
  +        }
  +        if (log.isInfoEnabled())
  +            log.info("Cluster FarmWarDeployer stopped.");
       }
   
       public void cleanDeployDir() {
           throw new java.lang.UnsupportedOperationException(
  -            "Method cleanDeployDir() not yet implemented.");
  +                "Method cleanDeployDir() not yet implemented.");
       }
   
       /**
  -     * Callback from the cluster, when a message is received,
  -     * The cluster will broadcast it invoking the messageReceived
  -     * on the receiver.
  -     * @param msg ClusterMessage - the message received from the cluster
  +     * Callback from the cluster, when a message is received, The cluster 
will
  +     * broadcast it invoking the messageReceived on the receiver.
  +     * 
  +     * @param msg
  +     *            ClusterMessage - the message received from the cluster
        */
       public void messageReceived(ClusterMessage msg) {
           try {
               if (msg instanceof FileMessage && msg != null) {
                   FileMessage fmsg = (FileMessage) msg;
  +                if (log.isDebugEnabled())
  +                    log.debug("receive cluster deployment [ path: "
  +                            + fmsg.getContextPath() + " war:  "
  +                            + fmsg.getFileName() + " ]");
                   FileMessageFactory factory = getFactory(fmsg);
  +                // TODO correct second try after app is in service!
                   if (factory.writeMessage(fmsg)) {
  -                    //last message received
  +                    //last message received war file is completed
                       String name = factory.getFile().getName();
                       if (!name.endsWith(".war"))
                           name = name + ".war";
                       File deployable = new File(getDeployDir(), name);
  -                    factory.getFile().renameTo(deployable);
  -                    // FIXME
  -                    /*
                       try {
  -                        if 
(getDeployer().findDeployedApp(fmsg.getContextPath()) != null)
  -                            getDeployer().remove(fmsg.getContextPath(), 
true);
  -                    } catch (Exception x) {
  -                        log.info(
  -                            "Error removing existing context before 
installing a new one.",
  -                            x);
  +                        String path = fmsg.getContextPath();
  +                        if (!isServiced(path)) {
  +                            addServiced(path);
  +                            try {
  +                                remove(path);
  +                                factory.getFile().renameTo(deployable);
  +                                check(path);
  +                            } finally {
  +                                removeServiced(path);
  +                            }
  +                            if (log.isDebugEnabled())
  +                                log.debug("deployment from " + path
  +                                        + " finished.");
  +                        } else
  +                            log.error("Application " + path
  +                                    + " in used. touch war file " + name
  +                                    + " again!");
  +                    } catch (Exception ex) {
  +                        log.error(ex);
  +                    } finally {
  +                        removeFactory(fmsg);
                       }
  -                    getDeployer().install(fmsg.getContextPath(),
  -                                          deployable.toURL());
  -                                          */
  -                    removeFactory(fmsg);
                   } //end if
               } else if (msg instanceof UndeployMessage && msg != null) {
  -                UndeployMessage umsg = (UndeployMessage) msg;
  -                // FIXME
  -                /*
  -                if (getDeployer().findDeployedApp(umsg.getContextPath()) != 
null)
  -                    getDeployer().remove(umsg.getContextPath(),
  -                                         umsg.getUndeploy());
  -                                         */
  +                try {
  +                    UndeployMessage umsg = (UndeployMessage) msg;
  +                    String path = umsg.getContextPath();
  +                    if (log.isDebugEnabled())
  +                        log.debug("receive cluster undeployment from " + 
path);
  +                    if (!isServiced(path)) {
  +                        addServiced(path);
  +                        try {
  +                            remove(path);
  +                        } finally {
  +                            removeServiced(path);
  +                        }
  +                        if (log.isDebugEnabled())
  +                            log.debug("undeployment from " + path
  +                                    + " finished.");
  +                    } else
  +                        log
  +                                .error("Application "
  +                                        + path
  +                                        + " in used. Sorry not remove from 
backup cluster nodes!");
  +                } catch (Exception ex) {
  +                    log.error(ex);
  +                }
               } //end if
           } catch (java.io.IOException x) {
               log.error("Unable to read farm deploy file message.", x);
           }
       }
   
  -    public synchronized FileMessageFactory getFactory(FileMessage msg) throws
  -        java.io.FileNotFoundException, java.io.IOException {
  +    /**
  +     * create factory for all transported war files
  +     * 
  +     * @param msg
  +     * @return Factory for all app message (war files)
  +     * @throws java.io.FileNotFoundException
  +     * @throws java.io.IOException
  +     */
  +    public synchronized FileMessageFactory getFactory(FileMessage msg)
  +            throws java.io.FileNotFoundException, java.io.IOException {
           File tmpFile = new File(msg.getFileName());
           File writeToFile = new File(getTempDir(), tmpFile.getName());
  -        FileMessageFactory factory = (FileMessageFactory) 
fileFactories.get(msg.
  -            getFileName());
  +        FileMessageFactory factory = (FileMessageFactory) 
fileFactories.get(msg
  +                .getFileName());
           if (factory == null) {
               factory = FileMessageFactory.getInstance(writeToFile, true);
               fileFactories.put(msg.getFileName(), factory);
  @@ -148,124 +288,157 @@
           return factory;
       }
   
  +    /**
  +     * Remove file (war) from messages)
  +     * 
  +     * @param msg
  +     */
       public void removeFactory(FileMessage msg) {
           fileFactories.remove(msg.getFileName());
       }
   
       /**
  -     * Before the cluster invokes messageReceived the
  -     * cluster will ask the receiver to accept or decline the message,
  -     * In the future, when messages get big, the accept method will only take
  -     * a message header
  -     * @param msg ClusterMessage
  -     * @return boolean - returns true to indicate that messageReceived
  -     * should be invoked. If false is returned, the messageReceived method
  -     * will not be invoked.
  +     * Before the cluster invokes messageReceived the cluster will ask the
  +     * receiver to accept or decline the message, In the future, when 
messages
  +     * get big, the accept method will only take a message header
  +     * 
  +     * @param msg
  +     *            ClusterMessage
  +     * @return boolean - returns true to indicate that messageReceived 
should be
  +     *         invoked. If false is returned, the messageReceived method will
  +     *         not be invoked.
        */
       public boolean accept(ClusterMessage msg) {
  -        return (msg instanceof FileMessage) ||
  -            (msg instanceof UndeployMessage);
  +        return (msg instanceof FileMessage) || (msg instanceof 
UndeployMessage);
       }
   
       /**
        * Install a new web application, whose web application archive is at the
  -     * specified URL, into this container and all the other
  -     * members of the cluster with the specified context path.
  -     * A context path of "" (the empty string) should be used for the root
  -     * application for this container.  Otherwise, the context path must
  -     * start with a slash.
  +     * specified URL, into this container and all the other members of the
  +     * cluster with the specified context path. A context path of "" (the 
empty
  +     * string) should be used for the root application for this container.
  +     * Otherwise, the context path must start with a slash.
        * <p>
  -     * If this application is successfully installed locally,
  -     * a ContainerEvent of type
  -     * <code>INSTALL_EVENT</code> will be sent to all registered listeners,
  -     * with the newly created <code>Context</code> as an argument.
  -     *
  -     * @param contextPath The context path to which this application should
  -     *  be installed (must be unique)
  -     * @param war A URL of type "jar:" that points to a WAR file, or type
  -     *  "file:" that points to an unpacked directory structure containing
  -     *  the web application to be installed
  -     *
  -     * @exception IllegalArgumentException if the specified context path
  -     *  is malformed (it must be "" or start with a slash)
  -     * @exception IllegalStateException if the specified context path
  -     *  is already attached to an existing web application
  -     * @exception IOException if an input/output error was encountered
  -     *  during installation
  +     * If this application is successfully installed locally, a 
ContainerEvent
  +     * of type <code>INSTALL_EVENT</code> will be sent to all registered
  +     * listeners, with the newly created <code>Context</code> as an argument.
  +     * 
  +     * @param contextPath
  +     *            The context path to which this application should be 
installed
  +     *            (must be unique)
  +     * @param war
  +     *            A URL of type "jar:" that points to a WAR file, or type
  +     *            "file:" that points to an unpacked directory structure
  +     *            containing the web application to be installed
  +     * 
  +     * @exception IllegalArgumentException
  +     *                if the specified context path is malformed (it must be 
""
  +     *                or start with a slash)
  +     * @exception IllegalStateException
  +     *                if the specified context path is already attached to an
  +     *                existing web application
  +     * @exception IOException
  +     *                if an input/output error was encountered during
  +     *                installation
        */
       public void install(String contextPath, URL war) throws IOException {
  -     // FIXME
  -     /*
  -        if (getDeployer().findDeployedApp(contextPath) != null)
  -            getDeployer().remove(contextPath, true);
  -            //step 1. Install it locally
  -        getDeployer().install(contextPath, war);
  -        */
  -        //step 2. Send it to each member in the cluster
           Member[] members = getCluster().getMembers();
           Member localMember = getCluster().getLocalMember();
           FileMessageFactory factory = FileMessageFactory.getInstance(new File(
  -            war.getFile()), false);
  +                war.getFile()), false);
           FileMessage msg = new FileMessage(localMember, war.getFile(),
  -                                          contextPath);
  +                contextPath);
  +        if(log.isDebugEnabled())
  +            log.debug("Send cluster war deployment [ path:"
  +                    + contextPath + " war: " + war + " ] started.");
           msg = factory.readMessage(msg);
           while (msg != null) {
               for (int i = 0; i < members.length; i++) {
  +                if (log.isDebugEnabled())
  +                    log.debug("Send cluster war fragment [ path: "
  +                            + contextPath + " war: " + war + " to: " +  
members[i] + " ]");
                   getCluster().send(msg, members[i]);
               } //for
               msg = factory.readMessage(msg);
           } //while
  +        if(log.isDebugEnabled())
  +            log.debug("Send cluster war deployment [ path: "
  +                    + contextPath + " war: " + war + " ] finished.");
       }
   
       /**
        * Remove an existing web application, attached to the specified context
  -     * path.  If this application is successfully removed, a
  -     * ContainerEvent of type <code>REMOVE_EVENT</code> will be sent to all
  -     * registered listeners, with the removed <code>Context</code> as
  -     * an argument. Deletes the web application war file and/or directory
  -     * if they exist in the Host's appBase.
  -     *
  -     * @param contextPath The context path of the application to be removed
  -     * @param undeploy boolean flag to remove web application from server
  -     *
  -     * @exception IllegalArgumentException if the specified context path
  -     *  is malformed (it must be "" or start with a slash)
  -     * @exception IllegalArgumentException if the specified context path does
  -     *  not identify a currently installed web application
  -     * @exception IOException if an input/output error occurs during
  -     *  removal
  +     * path. If this application is successfully removed, a ContainerEvent of
  +     * type <code>REMOVE_EVENT</code> will be sent to all registered
  +     * listeners, with the removed <code>Context</code> as an argument.
  +     * Deletes the web application war file and/or directory if they exist in
  +     * the Host's appBase.
  +     * 
  +     * @param contextPath
  +     *            The context path of the application to be removed
  +     * @param undeploy
  +     *            boolean flag to remove web application from server
  +     * 
  +     * @exception IllegalArgumentException
  +     *                if the specified context path is malformed (it must be 
""
  +     *                or start with a slash)
  +     * @exception IllegalArgumentException
  +     *                if the specified context path does not identify a
  +     *                currently installed web application
  +     * @exception IOException
  +     *                if an input/output error occurs during removal
        */
       public void remove(String contextPath, boolean undeploy) throws 
IOException {
  -        log.info("Cluster wide remove of web app " + contextPath);
  -        //step 1. Remove it locally
  -        
  -        // FIXME
  -        /*
  -        if (getDeployer().findDeployedApp(contextPath) != null)
  -            getDeployer().remove(contextPath, undeploy);
  -            */
  -            //step 2. Send it to each member in the cluster
  +        if (log.isInfoEnabled())
  +            log.info("Cluster wide remove of web app " + contextPath);
  +        // send to other nodes
           Member[] members = getCluster().getMembers();
           Member localMember = getCluster().getLocalMember();
  -        UndeployMessage msg = new UndeployMessage(localMember,
  -                                                  System.currentTimeMillis(),
  -                                                  "Undeploy:" + contextPath +
  -                                                  ":" +
  -                                                  System.currentTimeMillis(),
  -                                                  contextPath,
  -                                                  undeploy);
  +        UndeployMessage msg = new UndeployMessage(localMember, System
  +                .currentTimeMillis(), "Undeploy:" + contextPath + ":"
  +                + System.currentTimeMillis(), contextPath, undeploy);
  +        if (log.isDebugEnabled())
  +            log.debug("Send cluster wide undeployment from "
  +                    + contextPath );
           cluster.send(msg);
  +        // remove locally
  +        if (undeploy) {
  +            try {
  +                if (!isServiced(contextPath)) {
  +                    addServiced(contextPath);
  +                    try {
  +                        remove(contextPath);
  +                    } finally {
  +                        removeServiced(contextPath);
  +                    }
  +                } else
  +                    log.error("Local remove from " + contextPath
  +                            + "failed, other manager has app in service!");
  +
  +            } catch (Exception ex) {
  +                log.error("local remove from " + contextPath + " failed", 
ex);
  +            }
  +        }
  +
       }
   
  +    /*
  +     * Modifcation from watchDir war detected!
  +     * 
  +     * @see 
org.apache.catalina.cluster.deploy.FileChangeListener#fileModified(java.io.File)
  +     */
       public void fileModified(File newWar) {
           try {
  -            File deployWar = new File(getDeployDir(),newWar.getName());
  -            copy(newWar,deployWar);
  -            String contextName = "/" + deployWar.getName().substring(0,
  -                deployWar.getName().lastIndexOf(".war"));
  -            log.info("Installing webapp[" + contextName + "] from " + 
deployWar.getAbsolutePath());
  +            File deployWar = new File(getDeployDir(), newWar.getName());
  +            copy(newWar, deployWar);
  +            String contextName = "/"
  +                    + deployWar.getName().substring(0,
  +                            deployWar.getName().lastIndexOf(".war"));
  +            if (log.isInfoEnabled())
  +                log.info("Installing webapp[" + contextName + "] from "
  +                        + deployWar.getAbsolutePath());
               try {
  -                remove(contextName, true);
  +                remove(contextName, false);
               } catch (Exception x) {
                   log.error("No removal", x);
               }
  @@ -275,17 +448,180 @@
           }
       }
   
  +    /*
  +     * War remvoe from watchDir
  +     * 
  +     * @see 
org.apache.catalina.cluster.deploy.FileChangeListener#fileRemoved(java.io.File)
  +     */
       public void fileRemoved(File removeWar) {
           try {
  -            String contextName = "/" + removeWar.getName().substring(0,
  -                removeWar.getName().lastIndexOf(".war"));
  -            log.info("Removing webapp[" + contextName + "]");
  +            String contextName = "/"
  +                    + removeWar.getName().substring(0,
  +                            removeWar.getName().lastIndexOf(".war"));
  +            if (log.isInfoEnabled())
  +                log.info("Removing webapp[" + contextName + "]");
               remove(contextName, true);
           } catch (Exception x) {
               log.error("Unable to remove WAR file", x);
           }
       }
   
  +    /**
  +     * Given a context path, get the config file name.
  +     */
  +    protected String getConfigFile(String path) {
  +        String basename = null;
  +        if (path.equals("")) {
  +            basename = "ROOT";
  +        } else {
  +            basename = path.substring(1).replace('/', '#');
  +        }
  +        return (basename);
  +    }
  +
  +    /**
  +     * Given a context path, get the config file name.
  +     */
  +    protected String getDocBase(String path) {
  +        String basename = null;
  +        if (path.equals("")) {
  +            basename = "ROOT";
  +        } else {
  +            basename = path.substring(1);
  +        }
  +        return (basename);
  +    }
  +
  +    /**
  +     * Return a File object representing the "application root" directory for
  +     * our associated Host.
  +     */
  +    protected File getAppBase() {
  +
  +        if (appBase != null) {
  +            return appBase;
  +        }
  +
  +        File file = new File(host.getAppBase());
  +        if (!file.isAbsolute())
  +            file = new File(System.getProperty("catalina.base"), host
  +                    .getAppBase());
  +        try {
  +            appBase = file.getCanonicalFile();
  +        } catch (IOException e) {
  +            appBase = file;
  +        }
  +        return (appBase);
  +
  +    }
  +
  +    /**
  +     * Invoke the remove method on the deployer.
  +     */
  +    protected void remove(String path) throws Exception {
  +        // TODO Handle remove also work dir content !
  +        // Stop the context first to be nicer
  +        Context context = (Context) host.findChild(path);
  +        if (context != null) {
  +            if(log.isDebugEnabled())
  +                log.debug("Undeploy local context " +path );
  +            ((Lifecycle) context).stop();
  +            File war = new File(getAppBase(), getDocBase(path) + ".war");
  +            File dir = new File(getAppBase(), getDocBase(path));
  +            File xml = new File(configBase, getConfigFile(path) + ".xml");
  +            if (war.exists()) {
  +                war.delete();
  +            } else if (dir.exists()) {
  +                undeployDir(dir);
  +            } else {
  +                xml.delete();
  +            }
  +            // Perform new deployment and remove internal HostConfig state
  +            check(path);
  +        }
  +
  +    }
  +
  +    /**
  +     * Delete the specified directory, including all of its contents and
  +     * subdirectories recursively.
  +     * 
  +     * @param dir
  +     *            File object representing the directory to be deleted
  +     */
  +    protected void undeployDir(File dir) {
  +
  +        String files[] = dir.list();
  +        if (files == null) {
  +            files = new String[0];
  +        }
  +        for (int i = 0; i < files.length; i++) {
  +            File file = new File(dir, files[i]);
  +            if (file.isDirectory()) {
  +                undeployDir(file);
  +            } else {
  +                file.delete();
  +            }
  +        }
  +        dir.delete();
  +
  +    }
  +
  +    /*
  +     * Call watcher to check for deploy changes
  +     * 
  +     * @see org.apache.catalina.cluster.ClusterDeployer#backgroundProcess()
  +     */
  +    public void backgroundProcess() {
  +        if (started) {
  +            count = (count + 1) % processExpiresFrequency;
  +            if (count == 0 && watchEnabled) {
  +                watcher.check();
  +            }
  +        }
  +
  +    }
  +
  +    /*--Deployer Operations ------------------------------------*/
  +
  +    /**
  +     * Invoke the check method on the deployer.
  +     */
  +    protected void check(String name) throws Exception {
  +        String[] params = { name };
  +        String[] signature = { "java.lang.String" };
  +        mBeanServer.invoke(oname, "check", params, signature);
  +    }
  +
  +    /**
  +     * Invoke the check method on the deployer.
  +     */
  +    protected boolean isServiced(String name) throws Exception {
  +        String[] params = { name };
  +        String[] signature = { "java.lang.String" };
  +        Boolean result = (Boolean) mBeanServer.invoke(oname, "isServiced",
  +                params, signature);
  +        return result.booleanValue();
  +    }
  +
  +    /**
  +     * Invoke the check method on the deployer.
  +     */
  +    protected void addServiced(String name) throws Exception {
  +        String[] params = { name };
  +        String[] signature = { "java.lang.String" };
  +        mBeanServer.invoke(oname, "addServiced", params, signature);
  +    }
  +
  +    /**
  +     * Invoke the check method on the deployer.
  +     */
  +    protected void removeServiced(String name) throws Exception {
  +        String[] params = { name };
  +        String[] signature = { "java.lang.String" };
  +        mBeanServer.invoke(oname, "removeServiced", params, signature);
  +    }
  +
       /*--Instance Getters/Setters--------------------------------*/
       public CatalinaCluster getCluster() {
           return cluster;
  @@ -338,20 +674,41 @@
       public void setWatchEnabled(boolean watchEnabled) {
           this.watchEnabled = watchEnabled;
       }
  -    
  +
       /**
  +     * Return the frequency of watcher checks.
  +     */
  +    public int getProcessExpiresFrequency() {
  +
  +        return (this.processExpiresFrequency);
  +
  +    }
   
  +    /**
  +     * Set the watcher checks frequency.
  +     * 
  +     * @param processExpiresFrequency
  +     *            the new manager checks frequency
  +     */
  +    public void setProcessExpiresFrequency(int processExpiresFrequency) {
   
  +        if (processExpiresFrequency <= 0) {
  +            return;
  +        }
  +        this.processExpiresFrequency = processExpiresFrequency;
  +    }
   
       /**
        * Copy a file to the specified temp directory. This is required only
        * because Jasper depends on it.
        */
  -    private boolean copy(File from, File to) {
  +    protected boolean copy(File from, File to) {
           try {
  -            if ( !to.exists() ) to.createNewFile();
  +            if (!to.exists())
  +                to.createNewFile();
               java.io.FileInputStream is = new java.io.FileInputStream(from);
  -            java.io.FileOutputStream os = new 
java.io.FileOutputStream(to,false);
  +            java.io.FileOutputStream os = new java.io.FileOutputStream(to,
  +                    false);
               byte[] buf = new byte[4096];
               while (true) {
                   int len = is.read(buf);
  @@ -362,10 +719,10 @@
               is.close();
               os.close();
           } catch (IOException e) {
  -            log.error("Unable to copy file from:"+from+" to:"+to,e);
  +            log.error("Unable to copy file from:" + from + " to:" + to, e);
               return false;
           }
           return true;
       }
   
  -}
  +}
  \ No newline at end of file
  
  
  
  1.2       +9 -2      
jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FileMessageFactory.java
  
  Index: FileMessageFactory.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/deploy/FileMessageFactory.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- FileMessageFactory.java   4 Jun 2004 20:22:27 -0000       1.1
  +++ FileMessageFactory.java   27 Nov 2004 21:10:20 -0000      1.2
  @@ -32,7 +32,10 @@
    * @version 1.0
    */
   public class FileMessageFactory {
  -    
  +    /*--Static Variables----------------------------------------*/
  +    public static org.apache.commons.logging.Log log = 
org.apache.commons.logging.LogFactory
  +            .getLog(FileMessageFactory.class);
  +   
       /**
        * The number of bytes that we read from file
        */
  @@ -102,10 +105,12 @@
           throws FileNotFoundException, IOException{
           this.file = f;
           this.openForWrite = openForWrite;
  +        if(log.isDebugEnabled())
  +            log.debug("open file " + f + " write " +  openForWrite);
           if ( openForWrite ) {
               if (!file.exists()) file.createNewFile();
               out = new FileOutputStream(f);
  -        } else {
  +          } else {
               size = file.length();
               totalNrOfMessages = (size / READ_SIZE) + 1;
               in = new FileInputStream(f);
  @@ -167,6 +172,8 @@
        */
       public boolean writeMessage(FileMessage msg) throws 
IllegalArgumentException, IOException {
           if ( !openForWrite ) throw new IllegalArgumentException("Can't write 
message, this factory is reading.");
  +        if(log.isTraceEnabled())
  +            log.trace("Message " + msg + " data " + msg.getData() + " data 
length " + msg.getDataLength() + " out " + out );
           out.write(msg.getData(),0,msg.getDataLength());
           nrOfMessagesProcessed++;
           out.flush();
  
  
  
  1.3       +121 -101  
jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/deploy/WarWatcher.java
  
  Index: WarWatcher.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/deploy/WarWatcher.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- WarWatcher.java   8 Jun 2004 19:41:17 -0000       1.2
  +++ WarWatcher.java   27 Nov 2004 21:10:20 -0000      1.3
  @@ -15,24 +15,29 @@
    */
   
   package org.apache.catalina.cluster.deploy;
  +
   import java.io.File;
   import java.util.HashMap;
   import java.util.Map;
   import java.util.Iterator;
  +
   /**
  - * <p>The <b>WarWatcher</b> watches the deployDir for changes made to
  - * the directory (adding new WAR files->deploy or remove WAR files->undeploy)
  - * And notifies a listener of the changes made</p>    
  + * <p>
  + * The <b>WarWatcher </b> watches the deployDir for changes made to the
  + * directory (adding new WAR files->deploy or remove WAR files->undeploy) And
  + * notifies a listener of the changes made
  + * </p>
    * 
    * @author Filip Hanik
  - * @version 1.0
  + * @author Peter Rossbach
  + * @version 1.1
    */
   
  -public class WarWatcher implements Runnable {
  +public class WarWatcher {
   
       /*--Static Variables----------------------------------------*/
  -    public static org.apache.commons.logging.Log log =
  -        org.apache.commons.logging.LogFactory.getLog( WarWatcher.class );
  +    public static org.apache.commons.logging.Log log = 
org.apache.commons.logging.LogFactory
  +            .getLog(WarWatcher.class);
   
       /*--Instance Variables--------------------------------------*/
       /**
  @@ -44,144 +49,161 @@
        * Parent to be notified of changes
        */
       protected FileChangeListener listener = null;
  -    
  -    /**
  -     * 
  -     * Check interval
  -     */
  -    protected long interval = 5000; //5 seconds
  -    
  -    /**
  -     * Run status 
  -     */
  -    protected boolean alive = true;
  -    
  +
       /**
        * Currently deployed files
        */
       protected Map currentStatus = new HashMap();
  -    
  +
       /*--Constructor---------------------------------------------*/
   
  -    
  -    public WarWatcher(FileChangeListener listener,
  -                      File deployDir,
  -                      long interval) {
  +    public WarWatcher() {
  +    }
  +
  +    public WarWatcher(FileChangeListener listener, File deployDir) {
           this.listener = listener;
           this.deployDir = deployDir;
  -        this.interval = interval;
       }
   
       /*--Logic---------------------------------------------------*/
  -    
  -    public void run() {
  -        while ( alive ) {
  -            try {
  -                File[] list = deployDir.listFiles(new WarFilter());
  -                if ( list == null ) list = new File[0];
  -                //first make sure all the files are listed in our current 
status
  -                for ( int i=0; i<list.length; i++ ) {
  -                    addWarInfo(list[i]);
  -                }//for
  -                
  -                //check all the status codes and update the FarmDeployer
  -                for ( Iterator i=currentStatus.entrySet().iterator(); 
i.hasNext();) {
  -                    Map.Entry entry = (Map.Entry)i.next();
  -                    WarInfo info = (WarInfo)entry.getValue();
  -                    int check = info.check(); 
  -                    if ( check == 1 ) {
  -                        listener.fileModified(info.getWar());
  -                    } else if ( check == -1 ) {
  -                        listener.fileRemoved(info.getWar());
  -                        //no need to keep in memory
  -                        currentStatus.remove(info.getWar());
  -                    }//end if
  -                }//for
  -
  -                //sleep for the set interval
  -                Thread.sleep(interval);
  -            } catch ( Exception x ) {
  -                log.error("Unable to watch for WAR deployments in 
cluster.",x);
  -            }
  -        }//while
  -    }//run
  -    
  -    protected void addWarInfo(File f) { 
  -        WarInfo info = (WarInfo)currentStatus.get(f.getAbsolutePath());
  -        if ( info == null ) {
  -            info = new WarInfo(f);
  +
  +    /**
  +     * check for modification and send notifcation to listener
  +     */
  +    public void check() {
  +        if (log.isInfoEnabled())
  +            log.info("check cluster wars at " + deployDir);
  +        File[] list = deployDir.listFiles(new WarFilter());
  +        if (list == null)
  +            list = new File[0];
  +        //first make sure all the files are listed in our current status
  +        for (int i = 0; i < list.length; i++) {
  +            addWarInfo(list[i]);
  +        }//for
  +
  +        //check all the status codes and update the FarmDeployer
  +        for (Iterator i = currentStatus.entrySet().iterator(); i.hasNext();) 
{
  +            Map.Entry entry = (Map.Entry) i.next();
  +            WarInfo info = (WarInfo) entry.getValue();
  +            int check = info.check();
  +            if (check == 1) {
  +                listener.fileModified(info.getWar());
  +            } else if (check == -1) {
  +                listener.fileRemoved(info.getWar());
  +                //no need to keep in memory
  +                currentStatus.remove(info.getWar());
  +            }//end if
  +        }//for
  +
  +    }
  +
  +    /**
  +     * add cluster war to the watcher state
  +     * @param warfile
  +     */
  +    protected void addWarInfo(File warfile) {
  +        WarInfo info = (WarInfo) 
currentStatus.get(warfile.getAbsolutePath());
  +        if (info == null) {
  +            info = new WarInfo(warfile);
               info.setLastState(-1); //assume file is non existent
  -            currentStatus.put(f.getAbsolutePath(),info);
  +            currentStatus.put(warfile.getAbsolutePath(), info);
           }
       }
  -    
  -    public void stop() {
  -        alive = false;
  +
  +    /**
  +     * clear watcher state
  +     */
  +    public void clear() {
           currentStatus.clear();
  -        listener = null;
       }
  -    
  -    
  -    
  +
  +    /**
  +     * @return Returns the deployDir.
  +     */
  +    public File getDeployDir() {
  +        return deployDir;
  +    }
  +
  +    /**
  +     * @param deployDir
  +     *            The deployDir to set.
  +     */
  +    public void setDeployDir(File deployDir) {
  +        this.deployDir = deployDir;
  +    }
  +
  +    /**
  +     * @return Returns the listener.
  +     */
  +    public FileChangeListener getListener() {
  +        return listener;
  +    }
  +
  +    /**
  +     * @param listener
  +     *            The listener to set.
  +     */
  +    public void setListener(FileChangeListener listener) {
  +        this.listener = listener;
  +    }
  +
       /*--Inner classes-------------------------------------------*/
  -    
   
  -    
  -    
       /**
        * File name filter for war files
        */
       protected class WarFilter implements java.io.FilenameFilter {
           public boolean accept(File path, String name) {
  -            if ( name == null ) return false;
  +            if (name == null)
  +                return false;
               return name.endsWith(".war");
           }
       }
  -    
  -    
  +
       /**
        * File information on existing WAR files
        */
       protected class WarInfo {
           protected File war = null;
  +
           protected long lastChecked = 0;
  +
           protected long lastState = 0;
  -        
  +
           public WarInfo(File war) {
               this.war = war;
               this.lastChecked = war.lastModified();
  -            if ( !war.exists() ) lastState = -1;
  +            if (!war.exists())
  +                lastState = -1;
           }
  -        
  +
           public boolean modified() {
  -            return war.exists() && 
  -                war.lastModified() > lastChecked;
  +            return war.exists() && war.lastModified() > lastChecked;
           }
  -            
  +
           public boolean exists() {
               return war.exists();
           }
  -        
  +
           /**
  -         * Returns 
  -         * 1 if the file has been added/modified, 
  -         * 0 if the file is unchanged and 
  -         * -1 if the file has been removed
  +         * Returns 1 if the file has been added/modified, 0 if the file is
  +         * unchanged and -1 if the file has been removed
  +         * 
            * @return int 1=file added; 0=unchanged; -1=file removed
            */
           public int check() {
               //file unchanged by default
               int result = 0;
  -            
  -            if ( modified() ) {
  +
  +            if (modified()) {
                   //file has changed - timestamp
                   result = 1;
                   lastState = result;
  -            } else if ( (!exists()) && (!(lastState==-1)) ) {
  +            } else if ((!exists()) && (!(lastState == -1))) {
                   //file was removed
                   result = -1;
                   lastState = result;
  -            } else if ( (lastState==-1) && exists() ) {
  +            } else if ((lastState == -1) && exists()) {
                   //file was added
                   result = 1;
                   lastState = result;
  @@ -189,30 +211,28 @@
               this.lastChecked = System.currentTimeMillis();
               return result;
           }
  -        
  +
           public File getWar() {
               return war;
           }
  -        
  +
           public int hashCode() {
               return war.getAbsolutePath().hashCode();
           }
  -        
  +
           public boolean equals(Object other) {
  -            if ( other instanceof WarInfo ) {
  -                WarInfo wo = (WarInfo)other;
  +            if (other instanceof WarInfo) {
  +                WarInfo wo = (WarInfo) other;
                   return wo.getWar().equals(getWar());
               } else {
                   return false;
               }
           }
  -        
  +
           protected void setLastState(int lastState) {
               this.lastState = lastState;
           }
  -        
  +
       }
  -    
  -    
   
  -}
  +}
  \ No newline at end of file
  
  
  
  1.55      +4 -1      
jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SimpleTcpCluster.java
  
  Index: SimpleTcpCluster.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-tomcat-catalina/modules/cluster/src/share/org/apache/catalina/cluster/tcp/SimpleTcpCluster.java,v
  retrieving revision 1.54
  retrieving revision 1.55
  diff -u -r1.54 -r1.55
  --- SimpleTcpCluster.java     22 Nov 2004 14:51:52 -0000      1.54
  +++ SimpleTcpCluster.java     27 Nov 2004 21:10:20 -0000      1.55
  @@ -324,6 +324,9 @@
        * throwables will be caught and logged.
        */
       public void backgroundProcess() {
  +        if(clusterDeployer != null)
  +            clusterDeployer.backgroundProcess();
  +
       }
   
       
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to