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]