/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.ejb;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.transaction.TransactionManager;
import org.jboss.deployment.DeploymentInfo;
import org.jboss.deployment.SubDeployerSupport;
import org.jboss.deployment.DeploymentException;
import org.jboss.logging.Logger;
import org.jboss.system.ServiceControllerMBean;
import org.jboss.metadata.ApplicationMetaData;
import org.jboss.metadata.XmlFileLoader;
import org.jboss.metadata.MetaData;
import org.jboss.mx.loading.LoaderRepositoryFactory;
import org.jboss.mx.util.MBeanProxyExt;
import org.jboss.mx.util.ObjectNameConverter;
import org.jboss.verifier.BeanVerifier;
import org.jboss.verifier.event.VerificationEvent;
import org.jboss.verifier.event.VerificationListener;
import org.w3c.dom.Element;
/**
* A EJBDeployer is used to deploy EJB applications. It can be given a
* URL to an EJB-jar or EJB-JAR XML file, which will be used to instantiate
* containers and make them available for invocation.
*
* @jmx:mbean
* name="jboss.ejb:service=EJBDeployer"
* extends="org.jboss.deployment.SubDeployerMBean"
*
* @see Container
*
* @version $Revision: 1.23.2.13 $
* @author Rickard Öberg
* @author Marc Fleury
* @author Juha Lindfors
* @author Sebastien Alborini
* @author Peter Antman.
* @author Scott Stark
* @author Sacha Labourey
* @author Jason Dillon
*/
public class EJBDeployer
extends SubDeployerSupport
implements EJBDeployerMBean
{
private ServiceControllerMBean serviceController;
/** A map of current deployments. */
private HashMap deployments = new HashMap();
/** Verify EJB-jar contents on deployments */
private boolean verifyDeployments;
/** Enable verbose verification. */
private boolean verifierVerbose;
/** Enable strict verification: deploy JAR only if Verifier reports
* no problems */
private boolean strictVerifier;
/** Enable metrics interceptor */
private boolean metricsEnabled;
/** A flag indicating if deployment descriptors should be validated */
private boolean validateDTDs;
private ObjectName webServiceName;
private ObjectName transactionManagerServiceName;
private TransactionManager tm;
/**
* Returns the deployed applications.
*
* @jmx:managed-operation
*/
public Iterator getDeployedApplications()
{
return deployments.values().iterator();
}
protected ObjectName getObjectName(MBeanServer server, ObjectName name)
throws MalformedObjectNameException
{
return name == null ? OBJECT_NAME : name;
}
/**
* Get a reference to the ServiceController
*/
protected void startService() throws Exception
{
serviceController = (ServiceControllerMBean)
MBeanProxyExt.create(ServiceControllerMBean.class,
ServiceControllerMBean.OBJECT_NAME, server);
tm = (TransactionManager)getServer().getAttribute(transactionManagerServiceName,
"TransactionManager");
// register with MainDeployer
super.startService();
}
/**
* Implements the template method in superclass. This method stops all the
* applications in this server.
*/
protected void stopService() throws Exception
{
for( Iterator modules = deployments.values().iterator();
modules.hasNext(); )
{
DeploymentInfo di = (DeploymentInfo) modules.next();
stop(di);
}
// avoid concurrent modification exception
for( Iterator modules = new ArrayList(deployments.values()).iterator();
modules.hasNext(); )
{
DeploymentInfo di = (DeploymentInfo) modules.next();
destroy(di);
}
deployments.clear();
// deregister with MainDeployer
super.stopService();
serviceController = null;
tm = null;
}
/**
* Enables/disables the application bean verification upon deployment.
*
* @jmx:managed-attribute
*
* @param verify true to enable; false to disable
*/
public void setVerifyDeployments( boolean verify )
{
verifyDeployments = verify;
}
/**
* Returns the state of bean verifier (on/off)
*
* @jmx:managed-attribute
*
* @return true if enabled; false otherwise
*/
public boolean getVerifyDeployments()
{
return verifyDeployments;
}
/**
* Enables/disables the verbose mode on the verifier.
*
* @jmx:managed-attribute
*
* @param verbose true to enable; false to disable
*/
public void setVerifierVerbose(boolean verbose)
{
verifierVerbose = verbose;
}
/**
* Returns the state of the bean verifier (verbose/non-verbose mode)
*
* @jmx:managed-attribute
*
* @return true if enabled; false otherwise
*/
public boolean getVerifierVerbose()
{
return verifierVerbose;
}
/**
* Enables/disables the strict mode on the verifier.
*
* @jmx:managed-attribute
*
* @param strictVerifier true to enable; false
* to disable
*/
public void setStrictVerifier( boolean strictVerifier )
{
this.strictVerifier = strictVerifier;
}
/**
* Returns the mode of the bean verifier (strict/non-strict mode)
*
* @jmx:managed-attribute
*
* @return true if the Verifier is in strict mode,
* false otherwise
*/
public boolean getStrictVerifier()
{
return strictVerifier;
}
/**
* Enables/disables the metrics interceptor for containers.
*
* @jmx:managed-attribute
*
* @param enable true to enable; false to disable
*/
public void setMetricsEnabled(boolean enable)
{
metricsEnabled = enable;
}
/**
* Checks if this container factory initializes the metrics interceptor.
*
* @jmx:managed-attribute
*
* @return true if metrics are enabled; false otherwise
*/
public boolean isMetricsEnabled()
{
return metricsEnabled;
}
/**
* Get the flag indicating that ejb-jar.dtd, jboss.dtd &
* jboss-web.dtd conforming documents should be validated
* against the DTD.
*
* @jmx:managed-attribute
*/
public boolean getValidateDTDs()
{
return validateDTDs;
}
/**
* Set the flag indicating that ejb-jar.dtd, jboss.dtd &
* jboss-web.dtd conforming documents should be validated
* against the DTD.
*
* @jmx:managed-attribute
*/
public void setValidateDTDs(boolean validate)
{
this.validateDTDs = validate;
}
/**
* Get the WebServiceName value.
* @return the WebServiceName value.
*
* @jmx:managed-attribute
*/
public ObjectName getWebServiceName()
{
return webServiceName;
}
/**
* Set the WebServiceName value.
* @param newWebServiceName The new WebServiceName value.
*
* @jmx:managed-attribute
*/
public void setWebServiceName(ObjectName webServiceName)
{
this.webServiceName = webServiceName;
}
/**
* Get the TransactionManagerServiceName value.
* @return the TransactionManagerServiceName value.
*
* @jmx:managed-attribute
*/
public ObjectName getTransactionManagerServiceName()
{
return transactionManagerServiceName;
}
/**
* Set the TransactionManagerServiceName value.
* @param transactionManagerServiceName The new TransactionManagerServiceName value.
*
* @jmx:managed-attribute
*/
public void setTransactionManagerServiceName(ObjectName transactionManagerServiceName)
{
this.transactionManagerServiceName = transactionManagerServiceName;
}
public boolean accepts(DeploymentInfo di)
{
// To be accepted the deployment's root name must end in jar
String urlStr = di.url.getFile();
if( !urlStr.endsWith("jar") && !urlStr.endsWith("jar/") )
{
return false;
}
// However the jar must also contain at least one ejb-jar.xml
boolean accepts = false;
try
{
URL dd = di.localCl.findResource("META-INF/ejb-jar.xml");
if (dd == null)
{
return false;
}
// 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 an ejb-jar
if( di.localUrl != null )
{
urlStr = di.localUrl.toString();
}
String ddStr = dd.toString();
if ( ddStr.indexOf(urlStr) >= 0 )
{
accepts = true;
}
}
catch( Exception ignore )
{
}
return accepts;
}
public void init(DeploymentInfo di)
throws DeploymentException
{
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/ejb-jar.xml");
}
}
else
{
// We watch the top only, no directory support
di.watch = di.url;
}
// Check for a loader-repository
XmlFileLoader xfl = new XmlFileLoader();
InputStream in = di.localCl.getResourceAsStream("META-INF/jboss.xml");
if( in != null )
{
Element jboss = xfl.getDocument(in, "META-INF/jboss.xml").getDocumentElement();
in.close();
// 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);
}
}
}
catch (Exception e)
{
if (e instanceof DeploymentException)
throw (DeploymentException)e;
throw new DeploymentException( "failed to initialize", e );
}
// invoke super-class initialization
super.init(di);
}
/** This is here as a reminder that we may not want to allow ejb jars to
* have arbitrary sub deployments. Currently we do.
* @param di
* @throws DeploymentException
*/
protected void processNestedDeployments(DeploymentInfo di)
throws DeploymentException
{
super.processNestedDeployments(di);
}
public synchronized void create(DeploymentInfo di)
throws DeploymentException
{
try
{
// Create a file loader with which to load the files
XmlFileLoader efm = new XmlFileLoader(validateDTDs);
efm.setClassLoader(di.localCl);
// Load XML
di.metaData = efm.load();
}
catch (Exception e)
{
if (e instanceof DeploymentException)
throw (DeploymentException)e;
throw new DeploymentException( "Failed to load metadata", e );
}
if( verifyDeployments )
{
// we have a positive attitude
boolean allOK = true;
// wrapping this into a try - catch block to prevent errors in
// verifier from stopping the deployment
try
{
BeanVerifier verifier = new BeanVerifier();
// add a listener so we can log the results
verifier.addVerificationListener(new VerificationListener()
{
Logger log = Logger.getLogger(EJBDeployer.class,
"verifier" );
public void beanChecked(VerificationEvent event)
{
log.debug( "Bean checked: " + event.getMessage() );
}
public void specViolation(VerificationEvent event)
{
log.warn( "EJB spec violation: " +
(verifierVerbose ? event.getVerbose() : event.getMessage()));
}
});
log.debug("Verifying " + di.url);
verifier.verify( di.url, (ApplicationMetaData) di.metaData,
di.ucl );
allOK = verifier.getSuccess();
}
catch (Throwable t)
{
log.warn("Verify failed; continuing", t );
allOK = false;
}
// If the verifier is in strict mode and an error/warning
// was found in the Verification process, throw a Deployment
// Exception
if( strictVerifier && !allOK )
{
throw new DeploymentException( "Verification of Enterprise " +
"Beans failed, see above for error messages." );
}
}
// Create an MBean for the EJB module
try
{
ApplicationMetaData metadata = (ApplicationMetaData) di.metaData;
EjbModule ejbModule = new EjbModule(di, tm, webServiceName);
String name = metadata.getJmxName();
if( name == null )
{
name = EjbModule.BASE_EJB_MODULE_NAME + ",module=" + di.shortName;
}
// Build an escaped JMX name including deployment shortname
ObjectName ejbModuleName = ObjectNameConverter.convert(name);
// Check that the name is not registered
if( server.isRegistered(ejbModuleName) == true )
{
log.debug("The EJBModule name: "+ejbModuleName
+"is already registered, adding uid="+System.identityHashCode(ejbModule));
name = name + ",uid="+System.identityHashCode(ejbModule);
ejbModuleName = ObjectNameConverter.convert(name);
}
server.registerMBean(ejbModule, ejbModuleName);
di.deployedObject = ejbModuleName;
log.debug( "Deploying: " + di.url );
// Invoke the create life cycle method
serviceController.create(di.deployedObject);
}
catch (Exception e)
{
throw new DeploymentException("Error during create of EjbModule: "
+ di.url, e);
}
super.create(di);
}
public synchronized void start(DeploymentInfo di)
throws DeploymentException
{
try
{
// Start application
log.debug( "start application, deploymentInfo: " + di +
", short name: " + di.shortName +
", parent short name: " +
(di.parent == null ? "null" : di.parent.shortName) );
serviceController.start(di.deployedObject);
log.info( "Deployed: " + di.url );
// Register deployment. Use the application name in the hashtable
// FIXME: this is obsolete!! (really?!)
deployments.put(di.url, di);
}
catch (Exception e)
{
stop(di);
destroy(di);
throw new DeploymentException( "Could not deploy " + di.url, e );
}
super.start(di);
}
public void stop(DeploymentInfo di)
throws DeploymentException
{
try
{
serviceController.stop(di.deployedObject);
}
catch (Exception e)
{
throw new DeploymentException( "problem stopping ejb module: " +
di.url, e );
}
super.stop(di);
}
public void destroy(DeploymentInfo di)
throws DeploymentException
{
// FIXME: If the put() is obsolete above, this is obsolete, too
deployments.remove(di.url);
try
{
serviceController.destroy( di.deployedObject );
serviceController.remove( di.deployedObject );
}
catch (Exception e)
{
throw new DeploymentException( "problem destroying ejb module: " +
di.url, e );
}
super.destroy(di);
}
}
/*
vim:ts=3:sw=3:et
*/