/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This 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 (at your option) any later version.
*
* This software 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 software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.soa.esb.listeners.config;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;
import java.util.zip.ZipInputStream;
import javax.management.JMException;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.jboss.deployment.DeploymentException;
import org.jboss.deployment.DeploymentInfo;
import org.jboss.deployment.SubDeployer;
import org.jboss.deployment.SubDeployerSupport;
import org.jboss.internal.soa.esb.util.JBossDeployerUtil;
import org.jboss.logging.Logger;
import org.jboss.metadata.MetaData;
import org.jboss.metadata.XmlFileLoader;
import org.jboss.mx.loading.LoaderRepositoryFactory;
import org.jboss.mx.util.MBeanProxyExt;
import org.jboss.mx.util.ObjectNameConverter;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.Service;
import org.jboss.soa.esb.lifecycle.LifecycleResourceManager;
import org.jboss.soa.esb.listeners.config.model.ModelAdapter;
import org.jboss.system.ServiceControllerMBean;
import org.jboss.web.AbstractWebContainer;
import org.w3c.dom.Element;
/**
* The JBoss AS 4 ESB deployer
*
* @author <a href="bill@jboss.com">Bill Burke</a>
* @author <a href="mailto:mageshbk@jboss.com">Magesh Kumar B</a>
* @version $Revision: 1.1 $
*/
public class JBoss4ESBDeployer extends SubDeployerSupport
implements SubDeployer, JBoss4ESBDeployerMBean
{
private final static Logger log = Logger.getLogger(JBoss4ESBDeployer.class);
private ServiceControllerMBean serviceController;
private Properties actionArtifactProperties;
private File esbWarFiles ;
private final Set<String> esbNames = new HashSet<String>() ;
private static final String PREFIX_CANONICAL_NAME = "jboss.esb:deployment=" ;
private static final String ESB_ARTIFACT_NAME = "jbossesb.esb" ;
/**
* The path the the directory that will be used to generate the war file
* for all web gateway deployments (EBWS and HTTP Gateways).
*/
private String warFilesDir;
/**
* Default CTOR used to set default values to the Suffixes and RelativeOrder
* attributes. Those are read at subdeployer registration time by the MainDeployer
* to alter its SuffixOrder.
*/
public JBoss4ESBDeployer()
{
setSuffixes(new String[]{".esb"});
setRelativeOrder(1000); // before old EJB 2.1 deployer
}
public static boolean hasFile(DeploymentInfo di, String filePath)
{
String urlStr = di.url.getFile();
try
{
URL dd = di.localCl.findResource(filePath);
if (dd != null)
{
// If the DD url is not a subset of the urlStr then this is coming
// from a jar referenced by the deployment jar manifest and the
// this deployment jar it should not be treated as persistence
if (di.localUrl != null)
{
urlStr = di.localUrl.toString();
}
String ddStr = dd.toString();
if (ddStr.indexOf(urlStr) >= 0)
{
return true;
}
}
}
catch (Exception ignore)
{
}
return false;
}
/**
* Returns true if this deployer can deploy the given DeploymentInfo.
*
* @return True if this deployer can deploy the given DeploymentInfo.
* @jmx:managed-operation
*/
public boolean accepts(DeploymentInfo di)
{
String urlStr = di.url.toString();
return urlStr.endsWith(".esb") || urlStr.endsWith(".esb/") ||
urlStr.endsWith("-esb.xml");
}
/**
* Get a reference to the ServiceController
*/
protected void startService() throws Exception
{
actionArtifactProperties = JBossDeployerUtil.getArtifactProperties("/actionArtifactMap.properties");
serviceController = (ServiceControllerMBean)
MBeanProxyExt.create(ServiceControllerMBean.class,
ServiceControllerMBean.OBJECT_NAME, server);
mainDeployer.addDeployer(this);
LifecycleResourceManager.deactivateHook() ;
}
@Override
protected void stopService() throws Exception
{
LifecycleResourceManager.getSingleton().cleanupAllResources() ;
super.stopService();
}
protected URL getDocumentUrl(DeploymentInfo di)
{
String urlStr = di.url.toString();
if (urlStr.endsWith(".esb") || urlStr.endsWith(".esb/"))
{
return di.localCl.getResource("META-INF/jboss-esb.xml");
}
return di.url;
}
public void init(DeploymentInfo di) throws DeploymentException
{
if (warFilesDir == null)
{
final String errorMsg = String.format("No property named '%s' was configured in jbossesb.sar/META-INF/jboss-service.xml for %s", "warFilesDir", getClass().getName());
throw new DeploymentException(errorMsg);
}
final File tmpDir = new File(warFilesDir);
if (!tmpDir.exists())
{
final String errorMsg = String.format("The directory configured for %s='%s' does not exist.", "warFilesDir", tmpDir);
throw new DeploymentException(errorMsg);
}
esbWarFiles = JBossDeployerUtil.createDir(tmpDir, "esbwarfiles");
try
{
if (di.url.getProtocol().equalsIgnoreCase("file"))
{
File file = new File(di.url.getFile());
if (!file.isDirectory())
{
// If not directory we watch the package
di.watch = di.url;
}
else
{
// If directory we watch the xml files
di.watch = new URL(di.url, "META-INF/jboss-esb.xml");
}
}
else
{
// We watch the top only, no directory support
di.watch = di.url;
}
XmlFileLoader xfl = new XmlFileLoader();
InputStream in = di.localCl.getResourceAsStream("META-INF/deployment.xml");
if (in != null)
{
try
{
Element jboss = xfl.getDocument(in, "META-INF/deployment.xml").getDocumentElement();
// Check for a ejb level class loading config
Element loader = MetaData.getOptionalChild(jboss, "loader-repository");
if (loader != null)
{
LoaderRepositoryFactory.LoaderRepositoryConfig config =
LoaderRepositoryFactory.parseRepositoryConfig(loader);
di.setRepositoryInfo(config);
}
}
finally
{
in.close();
}
}
final URL document = getDocumentUrl(di);
if (document == null)
{
throw new DeploymentException("Unable to find document url of META-INF/jboss-esb.xml in: "
+ di.url);
}
JBoss4ESBDeployment deployment = new JBoss4ESBDeployment(document, di, esbWarFiles);
final Set<ObjectName> deps = new HashSet<ObjectName>();
addActionDependencies(di.shortName, deployment.getModel(), deps) ;
initialiseDeploymentName(deployment) ;
final String deploymentName = deployment.getDeploymentName() ;
di.context.put(JBoss4ESBDeploymentMetaData.class, new JBoss4ESBDeploymentMetaData(deployment, deploymentName, deps)) ;
// invoke super-class initialization
super.init(di);
}
catch (Exception e)
{
if (e instanceof DeploymentException)
{
throw(DeploymentException) e;
}
throw new DeploymentException("failed to initialize", e);
}
}
public synchronized void create(DeploymentInfo di) throws DeploymentException
{
log.info("create esb service, " + di.shortName);
try
{
final JBoss4ESBDeploymentMetaData metaData = (JBoss4ESBDeploymentMetaData)di.context.get(JBoss4ESBDeploymentMetaData.class) ;
final Set<ObjectName> deps = metaData.getDependencies() ;
InputStream in = di.localCl.getResourceAsStream("META-INF/deployment.xml");
if (in != null)
{
try
{
XmlFileLoader xfl = new XmlFileLoader();
Element jboss = xfl.getDocument(in, "META-INF/deployment.xml").getDocumentElement();
// Check for a ejb level class loading config
Iterator depends = MetaData.getChildrenByTagName(jboss, "depends");
if (depends != null)
{
while (depends.hasNext())
{
Element depend = (Element)depends.next();
ObjectName depOn = new ObjectName(MetaData.getElementContent(depend));
deps.add(depOn);
}
}
Iterator esbDepends = MetaData.getChildrenByTagName(jboss, "esb-depends");
if ((esbDepends != null) && esbDepends.hasNext())
{
final Map<String, DeploymentInfo> subDeploymentLocationMap ;
if (di.subDeployments.size() > 0)
{
subDeploymentLocationMap = new HashMap<String, DeploymentInfo>() ;
final Set<DeploymentInfo> subDeployments = (Set<DeploymentInfo>)di.subDeployments ;
for(DeploymentInfo subDI : subDeployments)
{
final String urlPath = subDI.url.getPath() ;
final String deployablePath = (urlPath.endsWith("/") ? urlPath.substring(0, urlPath.length()-1) : urlPath) ;
final int lastSeparator = deployablePath.lastIndexOf('/') ;
final String deployable = (lastSeparator >= 0 ? deployablePath.substring(lastSeparator+1) : deployablePath) ;
if (subDeploymentLocationMap.put(deployable, subDI) != null)
{
throw new DeploymentException("Duplicate subDeployment name: " + deployable) ;
}
}
}
else
{
throw new DeploymentException("No subdeployments to match esb-depends") ;
}
do
{
Element depend = (Element)esbDepends.next();
final String deployable = MetaData.getElementContent(depend) ;
final DeploymentInfo subDI = subDeploymentLocationMap.get(deployable) ;
if ((subDI != null) && subDI.context.containsKey(AbstractWebContainer.WEB_MODULE))
{
final ObjectName jmxName = (ObjectName) subDI.context.get(AbstractWebContainer.WEB_MODULE) ;
deps.add(jmxName) ;
}
else
{
throw new DeploymentException("Could not locate WAR subdeployment matching: " + deployable) ;
}
}
while (esbDepends.hasNext()) ;
}
}
finally
{
in.close();
}
}
String name = PREFIX_CANONICAL_NAME + metaData.getDeploymentName();
ObjectName on = ObjectNameConverter.convert(name);
// Check that the name is not registered
if (server.isRegistered(on) == true)
{
throw new DeploymentException("Duplicate registration for " + name) ;
}
final JBoss4ESBDeployment deployment = metaData.getDeployment() ;
deployment.setClassloader(di.ucl);
server.registerMBean(deployment, on);
di.deployedObject = on;
log.debug("Deploying: " + di.url);
// Invoke the create life cycle method
serviceController.create(di.deployedObject, deps);
}
catch (Exception e)
{
throw new DeploymentException("Error during create of ESB Module: "
+ di.url, e);
}
super.create(di);
}
private void addActionDependencies(final String deploymentName,
final ModelAdapter model, final Set<ObjectName> deps)
throws MalformedObjectNameException
{
final Set<String> artifacts = new HashSet<String>() ;
artifacts.add(ESB_ARTIFACT_NAME) ;
final Set<String> actionClasses = model.getActions() ;
final int numActionClasses = (actionClasses == null ? 0 : actionClasses.size()) ;
if (numActionClasses > 0)
{
for(final String actionClass: actionClasses)
{
final String artifact = (String)actionArtifactProperties.get(actionClass) ;
if (artifact != null)
{
artifacts.add(artifact) ;
}
}
}
for(final String artifact: artifacts)
{
if (!deploymentName.equals(artifact))
{
final String canonicalName = PREFIX_CANONICAL_NAME + artifact ;
final ObjectName on = ObjectNameConverter.convert(canonicalName) ;
deps.add(on) ;
}
}
}
public synchronized void start(DeploymentInfo di)
throws DeploymentException
{
try
{
serviceController.start(di.deployedObject);
}
catch (Exception e)
{
try
{
stop(di);
destroy(di);
}
catch (DeploymentException ignore)
{
}
throw new DeploymentException("Error during start of ESB Module: "
+ di.url, e);
}
super.start(di);
}
public void stop(DeploymentInfo di)
throws DeploymentException
{
if (di.deployedObject != null)
{
try
{
serviceController.stop(di.deployedObject);
}
catch (Exception e)
{
throw new DeploymentException("Error during stop of ESB Module: "
+ di.url, e);
}
}
super.stop(di);
}
public void destroy(DeploymentInfo di)
throws DeploymentException
{
final JBoss4ESBDeploymentMetaData metaData = (JBoss4ESBDeploymentMetaData)di.context.get(JBoss4ESBDeploymentMetaData.class) ;
if (metaData != null)
{
final String deploymentName = metaData.getDeploymentName() ;
removeDeploymentName(deploymentName) ;
}
if (di.deployedObject != null)
{
try
{
serviceController.destroy(di.deployedObject);
server.unregisterMBean(di.deployedObject);
}
catch (Exception e)
{
throw new DeploymentException("Error during stop of ESB Module: "
+ di.url, e);
}
}
super.destroy(di);
}
private synchronized void initialiseDeploymentName(final JBoss4ESBDeployment deployment)
{
final String deploymentName = deployment.getDeploymentName() ;
if (!esbNames.add(deploymentName))
{
deployment.setDeploymentName(deploymentName + ",uid=" + System.identityHashCode(deployment)) ;
}
}
private synchronized void removeDeploymentName(final String deploymentName)
{
esbNames.remove(deploymentName) ;
}
/**
* Sets the directory that will be used for generating ESWS wars.
* @param dir The directory to be used.
*/
public void setWarFilesDir(final String dir)
{
this.warFilesDir = dir;
}
}