FileDocCategorySizeDatePackage
FarmWarDeployer.javaAPI DocApache Tomcat 6.0.1426263Fri Jul 20 04:20:30 BST 2007org.apache.catalina.ha.deploy

FarmWarDeployer

public class FarmWarDeployer extends org.apache.catalina.ha.ClusterListener implements org.apache.catalina.ha.ClusterDeployer, FileChangeListener

A farm war deployer is a class that is able to deploy/undeploy web applications in WAR form within the cluster.

Any host can act as the admin, and will have three directories
  • deployDir - the directory where we watch for changes
  • applicationDir - the directory where we install applications
  • tempDir - a temporaryDirectory to store binary data when downloading a war from the cluster
Currently we only support deployment of WAR files since they are easier to send across the wire.
author
Filip Hanik
author
Peter Rossbach
version
$Revision: 516438 $

Fields Summary
public static org.apache.juli.logging.Log
log
private static final String
info
The descriptive information about this implementation.
protected org.apache.catalina.ha.CatalinaCluster
cluster
protected boolean
started
protected HashMap
fileFactories
protected String
deployDir
protected String
tempDir
protected String
watchDir
protected boolean
watchEnabled
protected WarWatcher
watcher
private int
count
Iteration count for background processing.
protected int
processDeployFrequency
Frequency of the Farm watchDir 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 File
configBase
Path where context descriptors should be deployed.
protected org.apache.catalina.Host
host
The associated host.
protected File
appBase
The host appBase.
protected MBeanServer
mBeanServer
MBean server.
protected ObjectName
oname
The associated deployer ObjectName.
Constructors Summary
public FarmWarDeployer()


    /*--Constructor---------------------------------------------*/
      
    
Methods Summary
public booleanaccept(org.apache.catalina.ha.ClusterMessage msg)
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.

        return (msg instanceof FileMessage) || (msg instanceof UndeployMessage);
    
protected voidaddServiced(java.lang.String name)
Invoke the check method on the deployer.

        String[] params = { name };
        String[] signature = { "java.lang.String" };
        mBeanServer.invoke(oname, "addServiced", params, signature);
    
public voidbackgroundProcess()

        if (started) {
            count = (count + 1) % processDeployFrequency;
            if (count == 0 && watchEnabled) {
                watcher.check();
            }
        }

    
protected voidcheck(java.lang.String name)
Invoke the check method on the deployer.

        String[] params = { name };
        String[] signature = { "java.lang.String" };
        mBeanServer.invoke(oname, "check", params, signature);
    
public voidcleanDeployDir()

        throw new java.lang.UnsupportedOperationException(
                "Method cleanDeployDir() not yet implemented.");
    
protected booleancopy(java.io.File from, java.io.File to)
Copy a file to the specified temp directory.

param
from copy from temp
param
to to host appBase directory
return
true, copy successful

        try {
            if (!to.exists())
                to.createNewFile();
            java.io.FileInputStream is = new java.io.FileInputStream(from);
            java.io.FileOutputStream os = new java.io.FileOutputStream(to,
                    false);
            byte[] buf = new byte[4096];
            while (true) {
                int len = is.read(buf);
                if (len < 0)
                    break;
                os.write(buf, 0, len);
            }
            is.close();
            os.close();
        } catch (IOException e) {
            log.error("Unable to copy file from:" + from + " to:" + to, e);
            return false;
        }
        return true;
    
public booleanequals(java.lang.Object listener)

        return super.equals(listener);
    
public voidfileModified(java.io.File newWar)

        try {
            File deployWar = new File(getDeployDir(), newWar.getName());
            copy(newWar, deployWar);
            String contextName = getContextName(deployWar);
            if (log.isInfoEnabled())
                log.info("Installing webapp[" + contextName + "] from "
                        + deployWar.getAbsolutePath());
            try {
                remove(contextName, false);
            } catch (Exception x) {
                log.error("No removal", x);
            }
            install(contextName, deployWar.toURL());
        } catch (Exception x) {
            log.error("Unable to install WAR file", x);
        }
    
public voidfileRemoved(java.io.File removeWar)

        try {
            String contextName = getContextName(removeWar);
            if (log.isInfoEnabled())
                log.info("Removing webapp[" + contextName + "]");
            remove(contextName, true);
        } catch (Exception x) {
            log.error("Unable to remove WAR file", x);
        }
    
protected java.io.FilegetAppBase()
Return a File object representing the "application root" directory for our associated Host.


        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);

    
public org.apache.catalina.ha.CatalinaClustergetCluster()

        return cluster;
    
protected java.lang.StringgetConfigFile(java.lang.String path)
Given a context path, get the config file name.

        String basename = null;
        if (path.equals("")) {
            basename = "ROOT";
        } else {
            basename = path.substring(1).replace('/", '#");
        }
        return (basename);
    
protected java.lang.StringgetContextName(java.io.File war)
Create a context path from war

param
war War filename
return
'/filename' or if war name is ROOT.war context name is empty string ''

        String contextName = "/"
        + war.getName().substring(0,
                war.getName().lastIndexOf(".war"));
        if("/ROOT".equals(contextName))
            contextName= "" ;
        return contextName ;
    
public java.lang.StringgetDeployDir()

        return deployDir;
    
protected java.lang.StringgetDocBase(java.lang.String path)
Given a context path, get the config file name.

        String basename = null;
        if (path.equals("")) {
            basename = "ROOT";
        } else {
            basename = path.substring(1);
        }
        return (basename);
    
public synchronized FileMessageFactorygetFactory(FileMessage msg)
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

        File tmpFile = new File(msg.getFileName());
        File writeToFile = new File(getTempDir(), tmpFile.getName());
        FileMessageFactory factory = (FileMessageFactory) fileFactories.get(msg
                .getFileName());
        if (factory == null) {
            factory = FileMessageFactory.getInstance(writeToFile, true);
            fileFactories.put(msg.getFileName(), factory);
        }
        return factory;
    
public java.lang.StringgetInfo()
Return descriptive information about this deployer implementation and the corresponding version number, in the format <description>/<version>.


        return (info);

    
public intgetProcessDeployFrequency()
Return the frequency of watcher checks.


        return (this.processDeployFrequency);

    
public java.lang.StringgetTempDir()

        return tempDir;
    
public java.lang.StringgetWatchDir()

        return watchDir;
    
public booleangetWatchEnabled()

        return watchEnabled;
    
public inthashCode()

        return super.hashCode();
    
public voidinstall(java.lang.String contextPath, java.net.URL war)
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.

If this application is successfully installed locally, a ContainerEvent of type INSTALL_EVENT will be sent to all registered listeners, with the newly created Context 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

        Member[] members = getCluster().getMembers();
        Member localMember = getCluster().getLocalMember();
        FileMessageFactory factory = FileMessageFactory.getInstance(new File(
                war.getFile()), false);
        FileMessage msg = new FileMessage(localMember, war.getFile(),
                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]);
            }
            msg = factory.readMessage(msg);
        }
        if(log.isDebugEnabled())
            log.debug("Send cluster war deployment [ path: "
                    + contextPath + " war: " + war + " ] finished.");
    
protected booleanisServiced(java.lang.String name)
Invoke the check method on the deployer.

        String[] params = { name };
        String[] signature = { "java.lang.String" };
        Boolean result = (Boolean) mBeanServer.invoke(oname, "isServiced",
                params, signature);
        return result.booleanValue();
    
public booleanisWatchEnabled()

        return watchEnabled;
    
public voidmessageReceived(org.apache.catalina.ha.ClusterMessage msg)
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

        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 war file is completed
                    String name = factory.getFile().getName();
                    if (!name.endsWith(".war"))
                        name = name + ".war";
                    File deployable = new File(getDeployDir(), name);
                    try {
                        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);
                    }
                }
            } else if (msg instanceof UndeployMessage && msg != null) {
                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);
                }
            }
        } catch (java.io.IOException x) {
            log.error("Unable to read farm deploy file message.", x);
        }
    
public voidremove(java.lang.String contextPath, boolean undeploy)
Remove an existing web application, attached to the specified context path. If this application is successfully removed, a ContainerEvent of type REMOVE_EVENT will be sent to all registered listeners, with the removed Context 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

        if (log.isInfoEnabled())
            log.info("Cluster wide remove of web app " + contextPath);
        Member localMember = getCluster().getLocalMember();
        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);
            }
        }

    
protected voidremove(java.lang.String path)
Invoke the remove method on the deployer.

        // 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);
        }

    
public voidremoveFactory(FileMessage msg)
Remove file (war) from messages)

param
msg

        fileFactories.remove(msg.getFileName());
    
protected voidremoveServiced(java.lang.String name)
Invoke the check method on the deployer.

        String[] params = { name };
        String[] signature = { "java.lang.String" };
        mBeanServer.invoke(oname, "removeServiced", params, signature);
    
public voidsetCluster(org.apache.catalina.ha.CatalinaCluster cluster)

        this.cluster = cluster;
    
public voidsetDeployDir(java.lang.String deployDir)

        this.deployDir = deployDir;
    
public voidsetProcessDeployFrequency(int processExpiresFrequency)
Set the watcher checks frequency.

param
processExpiresFrequency the new manager checks frequency


        if (processExpiresFrequency <= 0) {
            return;
        }
        this.processDeployFrequency = processExpiresFrequency;
    
public voidsetTempDir(java.lang.String tempDir)

        this.tempDir = tempDir;
    
public voidsetWatchDir(java.lang.String watchDir)

        this.watchDir = watchDir;
    
public voidsetWatchEnabled(boolean watchEnabled)

        this.watchEnabled = watchEnabled;
    
public voidstart()

        if (started)
            return;
        getCluster().addClusterListener(this);
        if (watchEnabled) {
            watcher = new WarWatcher(this, new File(getWatchDir()));
            if (log.isInfoEnabled())
                log.info("Cluster deployment is watching " + getWatchDir()
                         + " for changes.");
        }
    
        // Check to correct engine and host setup
        Object parent = getCluster().getContainer();
        Engine engine = null;
        String hostname = null;
        if ( parent instanceof Host ) {
            host = (Host) parent;
            engine = (Engine) host.getParent();
            hostname = host.getName();
        }else {
            engine = (Engine)parent;
            hostname = engine.getDefaultHost();
        }
        try {
            oname = new ObjectName(engine.getName() + ":type=Deployer,host="
                    + hostname);
        } 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());
        } else if (host != null) {
            configBase = new File(configBase, host.getName());
        }

        // Retrieve the MBean server
        mBeanServer = Registry.getRegistry(null, null).getMBeanServer();

        started = true;
        count = 0;
        if (log.isInfoEnabled())
            log.info("Cluster FarmWarDeployer started.");
    
public voidstop()

        started = false;
        getCluster().removeClusterListener(this);
        count = 0;
        if (watcher != null) {
            watcher.clear();
            watcher = null;

        }
        if (log.isInfoEnabled())
            log.info("Cluster FarmWarDeployer stopped.");
    
protected voidundeployDir(java.io.File dir)
Delete the specified directory, including all of its contents and subdirectories recursively.

param
dir File object representing the directory to be deleted


        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();