TomcatDeployer.java :  » J2EE » ow2-easybeans » org » ow2 » easybeans » deployer » Java Open Source

Java Open Source » J2EE » ow2 easybeans 
ow2 easybeans » org » ow2 » easybeans » deployer » TomcatDeployer.java
/**
 * EasyBeans
 * Copyright (C) 2007 Bull S.A.S.
 * Contact: easybeans@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id: JOnASDeployer.java 1335 2007-04-24 12:47:31Z benoitf $
 * --------------------------------------------------------------------------
 */

package org.ow2.easybeans.deployer;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.List;
import java.util.Set;
import java.util.jar.JarFile;

import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;

import org.ow2.easybeans.jmx.CommonsModelerException;
import org.ow2.easybeans.jmx.CommonsModelerHelper;
import org.ow2.easybeans.jmx.JMXRemoteException;
import org.ow2.easybeans.jmx.MBeanServerHelper;
import org.ow2.easybeans.util.files.FileUtils;
import org.ow2.easybeans.util.files.FileUtilsException;
import org.ow2.easybeans.util.url.URLUtils;
import org.ow2.util.ee.deploy.api.archive.ArchiveException;
import org.ow2.util.ee.deploy.api.deployable.EARDeployable;
import org.ow2.util.ee.deploy.api.deployable.EJBDeployable;
import org.ow2.util.ee.deploy.api.deployable.IDeployable;
import org.ow2.util.ee.deploy.api.deployable.WARDeployable;
import org.ow2.util.ee.deploy.api.deployer.DeployerException;
import org.ow2.util.ee.deploy.api.deployer.IDeployer;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
 * Implementation of the Deployer for EasyBeans in Tomcat. <br />
 * It will deploy EJB3 and EAR. EJB3 will be deployed in EasyBeans while WAR
 * file will go in Tomcat.
 * @author Florent Benoit
 */
public class TomcatDeployer extends AbsWebContainerDeployer implements IDeployer {

    /**
     * Logger.
     */
    private static Log logger = LogFactory.getLog(TomcatDeployer.class);

    /**
     * Name of the Standard Context class.
     */
    private static final String CATALINA_CONTEXT_CLASSNAME = "org.apache.catalina.core.StandardContext";

    /**
     * Class object for the catalina context.
     */
    private Class catalinaContextClass = null;

    /**
     * Name of the start method for the context object.
     */
    private static final String START_METHOD_NAME = "start";

    /**
     * Method for the start of the catalina context.
     */
    private Method startContextMethod = null;

    /**
     * Method name for setting the parent class loader on the context.
     */
    private static final String SET_PARENT_CLASSLOADER_METHOD_NAME = "setParentClassLoader";

    /**
     * Method for setting the parent class loader on the context.
     */
    private Method setParentClassLoaderMethod = null;

    /**
     * Method name for setting the URL to the war file.
     */
    private static final String SET_DOC_BASE_METHOD_NAME = "setDocBase";

    /**
     * Method for setting the URL to the doc base.
     */
    private Method setDocBaseMethod = null;

    /**
     * Method name for setting the context-root of this context.
     */
    private static final String SET_PATH_METHOD_NAME = "setPath";

    /**
     * Method for setting the context-root of this context.
     */
    private Method setPathMethod = null;

    /**
     * Method name for setting the default context xml file.
     */
    private static final String SET_DEFAULT_CONTEXT_XML_METHOD_NAME = "setDefaultContextXml";

    /**
     * Method for setting the default context xml file.
     */
    private Method setDefaultContextXmlMethod = null;

    /**
     * Method name for setting the default web xml file.
     */
    private static final String SET_DEFAULT_WEB_XML_METHOD_NAME = "setDefaultWebXml";

    /**
     * Name of the method for changing the Java Delegation model.
     */
    private static final String SET_JAVA_DELEGATION_MODEL_METHOD_NAME = "setDelegate";

    /**
     * Name of the method for setting the META-INF/context.xml file.
     */
    private static final String SET_CONFIG_FILE_METHOD_NAME = "setConfigFile";

    /**
     * Method object used for changing the Java Delegation model.
     */
    private Method setJavaDelegationModelMethod = null;

    /**
     * Method for setting the default web xml file.
     */
    private Method setDefaultWebXmlMethod = null;

    /**
     * Method for setting the path to META-INF/context.xml file.
     */
    private Method setConfigFileMethod = null;

    /**
     * Engine Object Name.
     */
    private static final String ENGINE_OBJECT_NAME = "*:type=Engine";

    /**
     * Destroy operation on the Tomcat context to undeploy the war.
     */
    private static final String DESTROY_OPERATION = "destroy";

    /**
     * Build a new instance of this deployer.
     * @throws DeployerException if the instance is not built.
     */
    public TomcatDeployer() throws DeployerException {
        super();

        // First, load the context class
        catalinaContextClass = loadClass(CATALINA_CONTEXT_CLASSNAME);

        // get methods for the context
        startContextMethod = getMethod(catalinaContextClass, START_METHOD_NAME);
        setParentClassLoaderMethod = getMethod(catalinaContextClass, SET_PARENT_CLASSLOADER_METHOD_NAME, ClassLoader.class);
        setDocBaseMethod = getMethod(catalinaContextClass, SET_DOC_BASE_METHOD_NAME, String.class);
        setPathMethod = getMethod(catalinaContextClass, SET_PATH_METHOD_NAME, String.class);
        setDefaultContextXmlMethod = getMethod(catalinaContextClass, SET_DEFAULT_CONTEXT_XML_METHOD_NAME, String.class);
        setDefaultWebXmlMethod = getMethod(catalinaContextClass, SET_DEFAULT_WEB_XML_METHOD_NAME, String.class);
        setJavaDelegationModelMethod = getMethod(catalinaContextClass, SET_JAVA_DELEGATION_MODEL_METHOD_NAME, boolean.class);
        setConfigFileMethod = getMethod(catalinaContextClass, SET_CONFIG_FILE_METHOD_NAME, String.class);
    }

    /**
     * Deploy a deployable. It can be an EJB jar, EAR, WAR, etc.
     * @param deployable a given deployable
     * @throws DeployerException if the deployment is not done.
     */
    public void deploy(final IDeployable deployable) throws DeployerException {
        checkSupportedDeployable(deployable);
        if (deployable instanceof EJBDeployable) {
            deployEJB((EJBDeployable) deployable);
        } else if (deployable instanceof EARDeployable) {
            // needs to unpack it before deploying it
            EARDeployable earDeployable = unpackEARDeployable((EARDeployable) deployable);
            deployEAR(earDeployable);
        }
    }

    /**
     * Deploy the WAR files present in the given EAR.
     * @param earDeployable the EAR containing the WARs
     * @param earURL the EAR URL
     * @param earClassLoader the EAR classloader
     * @param parentClassLoader the parent classloader (EJB) to use
     * @throws DeployerException if the wars are not deployed.
     */
    @Override
    protected void deployWARs(final EARDeployable earDeployable, final URL earURL, final ClassLoader earClassLoader,
            final ClassLoader parentClassLoader) throws DeployerException {
        // First, try to see if there are .war in this EAR
        List<WARDeployable> wars = earDeployable.getWARDeployables();

        for (WARDeployable war : wars) {

            // Build a new context for this war file
            Object ctx = newInstance(catalinaContextClass);

            // Build Object Name
            String objectName = buildObjectName(war);

            // Register Object MBean
            try {
                CommonsModelerHelper.registerModelerMBean(ctx, objectName);
            } catch (CommonsModelerException e) {
                throw new DeployerException("Cannot register the object '" + ctx + "' with the objectname '" + objectName
                        + "'.", e);
            }

            // set the context-root
            invoke(setPathMethod, ctx, war.getContextRoot());

            // Get the URL for this War
            URL warURL = null;
            try {
                warURL = war.getArchive().getURL();
            } catch (ArchiveException e) {
                throw new DeployerException("Cannot get the URL for the archive '" + war.getArchive() + "'.", e);
            }

            // File of this war
            File warFile = URLUtils.urlToFile(warURL);

            // docbase
            String docBase = warFile.getPath();

            // File and not a directory --> unpack it
            if (warFile.isFile()) {
                // Need to unpack the file
                File unpackDir = unpack(warFile, war, earURL);
                docBase = unpackDir.getPath();
            }

            // set the path to the war file (needs to be unpacked else it will
            // be unpacked by tomcat in the webapps folder and it will try to
            // deploy twice the webapp with wrong classloader)
            invoke(setDocBaseMethod, ctx, docBase);

            // default XML files
            invoke(setDefaultWebXmlMethod, ctx, "conf/web.xml");
            invoke(setDefaultContextXmlMethod, ctx, "context.xml");


            File contextXmlFile = new File(docBase + File.separator + "META-INF" + File.separator + "context.xml");
            // META-INF/context.xml file support
            if (contextXmlFile.exists()) {
                invoke(setConfigFileMethod, ctx, contextXmlFile.getAbsolutePath());
            }


            // Set the parent class loader
            invoke(setParentClassLoaderMethod, ctx, parentClassLoader);

            // Set the java delegation model
            invoke(setJavaDelegationModelMethod, ctx, Boolean.TRUE);

            // Invoke init method
            // invoke(initContextMethod, ctx);

            // Invoke start method
            invoke(startContextMethod, ctx);

            // War has been deployed
            logger.info("The war ''{0}'' has been deployed on the ''{1}'' context.", war, war.getContextRoot());
        }
    }

    /**
     * Check that the given deployable is supported by this deployer. If it is
     * not supported, throw an error.
     * @param deployable the deployable that needs to be deployed
     * @throws DeployerException if this deployable is not supported.
     */
    private void checkSupportedDeployable(final IDeployable deployable) throws DeployerException {
        if (!(deployable instanceof EARDeployable || deployable instanceof EJBDeployable)) {
            throw new DeployerException("The deployable '" + deployable + "' is not supported by this deployer");
        }
    }

    /**
     * Undeploy an given WAR (called by the undeploy method).
     * @param warDeployable a given WAR deployable
     * @throws DeployerException if the undeployment is not done.
     */
    @Override
    protected void undeployWAR(final WARDeployable warDeployable) throws DeployerException {
        // get the root context of this deployable
        String contextRoot = warDeployable.getContextRoot();

        // Now, search the MBean of this context
        ObjectName contextObjectName = null;
        try {
            contextObjectName = new ObjectName(buildObjectName(warDeployable));
        } catch (MalformedObjectNameException e) {
            throw new DeployerException("Cannot get the ObjectName for the WAR deployable '" + warDeployable + "'.", e);
        } catch (NullPointerException e) {
            throw new DeployerException("Cannot get the ObjectName for the WAR deployable '" + warDeployable + "'.", e);
        }

        // Now, search if the MBean of this context is present
        try {
            if (!MBeanServerHelper.getMBeanServerServer().isRegistered(contextObjectName)) {
                throw new DeployerException("There is no MBean with the ObjectName '" + contextObjectName
                        + "' in the MBean Server for the WAR deployable '" + warDeployable + "'.");
            }
        } catch (JMXRemoteException e) {
            throw new DeployerException("Cannot check if the MBean with the ObjectName '" + contextObjectName
                    + "'is registered in the MBean Server for the WAR deployable '" + warDeployable + "'.");
        }

        // Undeploy
        try {
            MBeanServerHelper.getMBeanServerServer().invoke(contextObjectName, DESTROY_OPERATION, null, null);
        } catch (InstanceNotFoundException e) {
            throw new DeployerException("Cannot remove the context '" + contextRoot + "' of the war deployable '"
                    + warDeployable + "'.", e);
        } catch (MBeanException e) {
            throw new DeployerException("Cannot remove the context '" + contextRoot + "' of the war deployable '"
                    + warDeployable + "'.", e);
        } catch (ReflectionException e) {
            throw new DeployerException("Cannot remove the context '" + contextRoot + "' of the war deployable '"
                    + warDeployable + "'.", e);
        } catch (JMXRemoteException e) {
            throw new DeployerException("Cannot remove the context '" + contextRoot + "' of the war deployable '"
                    + warDeployable + "'.", e);
        }

        logger.info("The context ''{0}'' of the War ''{1}'' has been undeployed", contextRoot, warDeployable);
    }

    /**
     * Build an objectname for the given war.
     * @param war the given war deployable that contains the datas.
     * @return a JMX object name.
     * @throws DeployerException if the object name cannot be built.
     */
    private String buildObjectName(final WARDeployable war) throws DeployerException {
        String objectName = getDomain() + ":j2eeType=WebModule,name=//" + getDefaultHost() + "/" + war.getContextRoot()
                + ",J2EEServer=EasyBeans,J2EEApplication=EAR";
        return objectName;
    }

    /**
     * Gets the JMX domain of Tomcat.
     * @return the JMX domain of Tomcat.
     * @throws DeployerException if the domain cannot be found
     */
    private String getDomain() throws DeployerException {
        return getEngineObjectName().getDomain();
    }

    /**
     * @return the first Engine object name found in the MBean server
     * @throws DeployerException if the engine object name is not found in
     *         the MBean server.
     */
    private ObjectName getEngineObjectName() throws DeployerException {
        // Build Engine object name
        ObjectName engineObjectName = null;
        try {
            engineObjectName = new ObjectName(ENGINE_OBJECT_NAME);
        } catch (MalformedObjectNameException e) {
            throw new DeployerException("Cannot build Tomcat Engine MBean.", e);
        } catch (NullPointerException e) {
            throw new DeployerException("Cannot build Tomcat Engine MBean.", e);
        }

        // Ask the MBean server
        Set objectNames = null;
        try {
            // Get the list
            objectNames = MBeanServerHelper.getMBeanServerServer().queryNames(engineObjectName, null);
        } catch (JMXRemoteException e) {
            throw new DeployerException("Cannot get Tomcat Engine MBean.", e);
        }

        // Find objects ?
        if (objectNames.size() == 0) {
            throw new DeployerException("No Tomcat Engine MBean was found in the MBean server");
        }

        // Return the domain of this object name
        return ((ObjectName) objectNames.iterator().next());
    }

    /**
     * @return the default host of the first engine found in the MBean server
     * @throws DeployerException if the MBean is not found.
     */
    private String getDefaultHost() throws DeployerException {
        ObjectName engineObjectName = getEngineObjectName();
        try {
            return MBeanServerHelper.getMBeanServerServer().getAttribute(engineObjectName, "defaultHost").toString();
        } catch (AttributeNotFoundException e) {
            throw new DeployerException("Cannot get the default host on the object name '" + engineObjectName + "'.", e);
        } catch (InstanceNotFoundException e) {
            throw new DeployerException("Cannot get the default host on the object name '" + engineObjectName + "'.", e);
        } catch (MBeanException e) {
            throw new DeployerException("Cannot get the default host on the object name '" + engineObjectName + "'.", e);
        } catch (ReflectionException e) {
            throw new DeployerException("Cannot get the default host on the object name '" + engineObjectName + "'.", e);
        } catch (JMXRemoteException e) {
            throw new DeployerException("Cannot get the default host on the object name '" + engineObjectName + "'.", e);
        }
    }

    /**
     * Unpack the given war into a temp directory.
     * @param warFile the war to unpack
     * @param war data on the given war
     * @param earURL url of the EAR that contains the war
     * @return the File to the unpack directory
     * @throws DeployerException if unpack fails
     */
    private File unpack(final File warFile, final WARDeployable war, final URL earURL) throws DeployerException {
        // Get EAR application name
        String earName = URLUtils.urlToFile(earURL).getName();

        // unpack Root directory
        String rootUnpackDir = System.getProperty("java.io.tmpdir") + File.separator + System.getProperty("user.name")
                + "-EasyBeans-unpack" + File.separator;

        // Unpack directory
        File unpackDir = new File(rootUnpackDir, earName + File.separator + warFile.getName());

        // Build a JarFile on the war
        JarFile packedJar;
        try {
            packedJar = new JarFile(warFile);
        } catch (IOException e) {
            throw new DeployerException("The war file '" + warFile + "' is not a valid jar file", e);
        }

        // Unpack the war
        try {
            FileUtils.unpack(packedJar, unpackDir);
        } catch (FileUtilsException e) {
            throw new DeployerException("Cannot unpack the file '" + packedJar + "' in the directory '" + unpackDir + "'.", e);
        }

        return unpackDir;

    }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.