/*
* JBoss, Home of Professional Open Source Copyright 2009, Red Hat Middleware
* LLC, and individual contributors 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.deployers.mc.as6;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.log4j.Logger;
import org.jboss.deployers.spi.deployer.DeploymentStages;
import org.jboss.deployers.vfs.spi.deployer.AbstractVFSParsingDeployer;
import org.jboss.deployers.vfs.spi.structure.VFSDeploymentUnit;
import org.jboss.deployment.DeploymentException;
import org.jboss.internal.soa.esb.util.JBossDeployerUtil;
import org.jboss.metadata.MetaData;
import org.jboss.metadata.XmlFileLoader;
import org.jboss.mx.util.ObjectNameConverter;
import org.jboss.soa.esb.listeners.config.model.ModelAdapter;
import org.jboss.vfs.VirtualFile;
import org.jboss.vfs.VirtualFileFilter;
import org.w3c.dom.Element;
/**
* EsbConfigParser is a Microcontainer deployer that picks up jboss-esb.xml files, parses the content
* and produces an {@link EsbMetaData} instance.
* <p/>
* Other implementations could read/parse a configuration form another source, for example store
* the configurations in a database. As long as they produce the EsbMetaData they will be able to
* be deployed.
*
* Sample configuration:
* <pre>{@code
* <bean name="EsbConfigParser" class="org.jboss.soa.esb.listeners.deployers.mc.EsbConfigParser">
* <property name="esbDeploymentPrefix">jboss.esb:deployment=</property>
* <property name="warDeploymentPrefix">jboss.web.deployment:war=</property>
* <property name="actionArtifactsFile">/actionArtifactMap.properties</property>
* </bean>
* }</pre>
*
* <lu>
* <li><i>esbDeploymentPrefix</i> This is the prefix that a ESB archive deployments will have in JBoss AS. Defaults to 'jboss.esb:deployment='.</li>
* <li><i>warDeploymentPrefix</i> This is the prefix that a war archive deployments will have in JBoss AS. These
* are used for .war archives specified in the 'esb-depends' section of a deployment.xml file. Defaults to 'jboss.web.deployment:war='
* </li>
* <li><i>actionArtifactsFile</i> Properties file containing an action name to .esb archive mapping. Defaults to '/actionArtifactMap.properties'.
* <br>
* For example, and entry in the file could look like this: <br>
* org.jboss.soa.esb.smooks.SmooksAction=smooks.esb
* <br>
* This says that the SmooksAction exists in the smooks.esb archive. Adding these mappings means that commonly
* used actions don't need to be explicetely added to the deployment.xml of all deployments. These will be implicit
* instead.
* </li>
* </lu>
*
* @author <a href="mailto:dbevenius@jboss.com">Daniel Bevenius</a>
* @author <a href="mailto:mageshbk@jboss.com">Magesh Kumar B</a>
*/
public class EsbConfigParser extends AbstractVFSParsingDeployer<EsbMetaData>
{
/**
* Name and path of the esb deployment.xml file.
*/
private static final String ESB_DEPLOYMENT_XML = "META-INF/deployment.xml";
/**
* File filter for esb config files.
*/
private static EsbConfigFileFilter configFileFilter = new EsbConfigFileFilter();
/**
* Default actions file name.
*/
private String actionArtifactsFile = "/actionArtifactMap.properties";
/**
* The actions to .esb archive mappings file.
*/
private Properties actionArtifactProperties;
/**
* Deployment prefix for esb deployments.
*/
private String esbDeploymentPrefix = "jboss.esb:deployment=";
/**
* The simple name of the jbossesb.esb deployment.
*/
private String esbArtifactName = "jbossesb" ;
/**
* Deployment prefix for war deployments. The are for the war declared in the 'esb-depends' section
* of deployment.xml.
*/
private String warDeploymentPrefix = "jboss.web.deployment:war=";
/**
* Logger.
*/
private Logger log = Logger.getLogger(EsbConfigParser.class);
/**
* Sole constructor that performs the following steps:
* <lu>
* <li>Sets the output of this deployer to be {@link EsbMetaData}.</li>
* <li>Sets the suffix to {@link EsbConfigParser#ESB_FILE_SUFFIX}.</li>
* <li>Sets the jar extension to {@link EsbConfigParser#ESB_ARCHIVE_SUFFIX}.</li>
* <li>Sets this deployers deployment stage to {@link DeploymentStages#PARSE}./li>
* </lu>
*/
public EsbConfigParser()
{
super(EsbMetaData.class);
setSuffix(EsbConstants.ESB_FILE_SUFFIX);
setJarExtension(EsbConstants.ESB_ARCHIVE_SUFFIX);
setStage(DeploymentStages.PARSE);
}
/**
* Create will load the action artifacts file configured.
*
* @throws Exception If the action artifacts files cannot be loaded.
*/
public void create() throws Exception
{
log.info("Created");
actionArtifactProperties = JBossDeployerUtil.getArtifactProperties(actionArtifactsFile);
}
/**
* Will parse the VirtualFile representing the deployment and parse the esb configuration xml and extract information from
* the archive to create an {@link EsbMetaData} instance that will be returned.
*/
@Override
protected EsbMetaData parse(final VFSDeploymentUnit deploymentUnit, final VirtualFile file, final EsbMetaData metadata) throws Exception
{
VirtualFile configFile = findEsbConfigFile(file);
final String esbConfigXml = JBossDeployerUtil.readEsbConfig(configFile.openStream());
final String archiveName = deploymentUnit.getSimpleName();
final String deploymentName = getDeploymentName(deploymentUnit);
//This model is used only to add the action dependecies, later the model is recreated in EsbDeployment destroy/create cycle.
final ModelAdapter model = JBossDeployerUtil.getJbossEsbModel(esbConfigXml);
// Get dependencies from deployment.xml.
final Set<ObjectName> dependencies = getDependenciesFromDeploymentXml(deploymentUnit);
// Get implicit action dependencies.
final Set<ObjectName> actionDependencies = getActionDependencies(deploymentName, model, actionArtifactProperties);
// Add all dependencies to set.
dependencies.addAll(actionDependencies);
final EsbMetaData esbMetaData = new EsbMetaData(configFile, archiveName, deploymentName, dependencies);
esbMetaData.setModel(model);
log.debug("Parsed ESB configuration'" + esbMetaData + "'");
return esbMetaData;
}
/**
* Tries to rescursively find a file that ends with "-esb.xml".
*
* @param file The virtual file. Can point to a file or a directory which will be searched.
* @return VirtualFile VirtualFile representing a found configuration file.
* @throws DeploymentException If not configuration file could be found, or more than one was found.
* @throws IOException
*/
private VirtualFile findEsbConfigFile(final VirtualFile file) throws DeploymentException, IOException
{
if (file.getName().endsWith(EsbConstants.ESB_FILE_SUFFIX))
{
return file;
}
VirtualFile child = file.getChild("META-INF");
log.info("META-INF : " + child);
List<VirtualFile> esbConfigFiles;
try
{
esbConfigFiles = file.getChildrenRecursively(configFileFilter);
}
catch (final IOException e)
{
throw new DeploymentException(e.getMessage(), e);
}
if (esbConfigFiles.size() == 0)
{
throw new DeploymentException("No JBossESB configuration could be located the archive '" + file + "'");
}
else if (esbConfigFiles.size() > 1)
{
throw new DeploymentException("Only one JBossESB configuration can exist in an archive. Please check '" + file + "'");
}
else
{
return esbConfigFiles.get(0);
}
}
Set<ObjectName> getDependenciesFromDeploymentXml(final VFSDeploymentUnit unit) throws DeploymentException
{
final Set<ObjectName> dependencies = new HashSet<ObjectName>();
final VirtualFile deploymentXml = unit.getFile(ESB_DEPLOYMENT_XML);
try
{
if (deploymentXml != null && deploymentXml.exists())
{
try
{
XmlFileLoader xfl = new XmlFileLoader();
Element jboss = xfl.getDocument(deploymentXml.openStream(), ESB_DEPLOYMENT_XML).getDocumentElement();
// Check for a ejb level class loading config
@SuppressWarnings("unchecked")
Iterator depends = MetaData.getChildrenByTagName(jboss, "depends");
if (depends != null)
{
while (depends.hasNext())
{
Element depend = (Element) depends.next();
dependencies.add(new ObjectName(MetaData.getElementContent(depend)));
}
}
@SuppressWarnings("unchecked")
Iterator esbDepends = MetaData.getChildrenByTagName(jboss, "esb-depends");
if ((esbDepends != null) && esbDepends.hasNext())
{
Element depend = (Element) esbDepends.next();
final String deployable = MetaData.getElementContent(depend);
if (deployable.endsWith(".war"))
{
String objectName = warDeploymentPrefix + "/" + deployable.substring(0, deployable.indexOf('.'));
dependencies.add(new ObjectName(objectName));
}
}
}
catch (final MalformedObjectNameException e)
{
throw new org.jboss.deployment.DeploymentException(e.getMessage(), e);
}
}
}
catch (final IOException e)
{
throw new DeploymentException(e.getMessage(), e);
}
return dependencies;
}
/**
* Will go through the actions defined in the model (model of the esb configuration) and for every
* action that is defined in action artifacts properties adds that action as a dependency.
* This way there is no need to explicetely define dependencies in a separate deployment.xml file.
*
* @param deploymentName The name of the deployment
* @param model The {@link ModelAdapter} representing the esb configuration.
* @param actionArtifactProperties The predefined actions that are to be automatically included as dependencies.
* @return Set<ObjectName> A set of {@link ObjectName}s that this esb deployment depends on.
* @throws DeploymentException
*/
private Set<ObjectName> getActionDependencies(final String deploymentName, final ModelAdapter model, final Properties actionArtifactProperties) throws DeploymentException
{
final Set<ObjectName> deps = new HashSet<ObjectName>();
final Set<String> artifacts = new HashSet<String>() ;
if (!deploymentName.equals(esbArtifactName))
{
artifacts.add(esbArtifactName + EsbConstants.ESB_ARCHIVE_SUFFIX) ;
}
final Set<String> actionClasses = model.getActions() ;
if (actionArtifactProperties != null)
{
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 = esbDeploymentPrefix + artifact ;
try
{
ObjectName on = ObjectNameConverter.convert(canonicalName);
deps.add(on) ;
}
catch (MalformedObjectNameException e)
{
throw new DeploymentException(e.getMessage(), e);
}
}
}
return deps;
}
String getDeploymentName(final VFSDeploymentUnit deploymentUnit)
{
final String simpleName = deploymentUnit.getSimpleName();
int idx = simpleName.indexOf(EsbConstants.ESB_ARCHIVE_SUFFIX);
if (idx == -1)
{
return simpleName;
}
return simpleName.substring(0, simpleName.indexOf(EsbConstants.ESB_ARCHIVE_SUFFIX));
}
public void setActionArtifactsFile(final String actionArtifactsFile)
{
this.actionArtifactsFile = actionArtifactsFile;
}
public void setEsbDeploymentPrefix(final String deploymentPrefix)
{
this.esbDeploymentPrefix = deploymentPrefix;
}
public void setEsbArtifactName(final String esbArtifactName)
{
this.esbArtifactName = esbArtifactName;
}
public void setWarDeploymentPrefix(final String deploymentPrefix)
{
this.warDeploymentPrefix = deploymentPrefix;
}
/**
* Filter for filtering out ESB configuration files.
*
*/
private static class EsbConfigFileFilter implements VirtualFileFilter
{
/**
* Accepts only files ending with the ESB_FILE_SUFFIX.
*
* @param file The file to filter.
* @return true If the file name ends with "-esb.xml".
*/
public boolean accepts(final VirtualFile file)
{
return file.getName().endsWith(EsbConstants.ESB_FILE_SUFFIX);
}
}
}