| HARDeployer.java |
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.hibernate.har;
import java.io.File;
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.management.Attribute;
import javax.management.MBeanInfo;
import org.jboss.deployment.DeploymentInfo;
import org.jboss.deployment.SubDeployerSupport;
import org.jboss.deployment.DeploymentException;
import org.jboss.system.ServiceControllerMBean;
import org.jboss.mx.util.MBeanProxyExt;
import org.jboss.mx.util.ObjectNameFactory;
import org.jboss.hibernate.jmx.Hibernate;
/**
* Deployer for Hibernate <tt>har</tt> archives. A Hibernate archive
* is expected to have a .har extension and include:<ul>
* <li> <tt>hibernate-service.xml</tt>
* <li> persistent classes
* <li> <tt>hbm.xml</tt> mapping documents.</ul>
*
* @jmx:mbean
* name="jboss.har:service=HARDeployer"
* extends="org.jboss.deployment.SubDeployerMBean"
*
* @author <a href="mailto:alex@jboss.org">Alexey Loubyansky</a>
* @author <a href="mailto:steve@hibernate.org">Steve Ebersole</a>
* @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
*
* @version <tt>$Revision: 1.2.2.4 $</tt>
*/
public class HARDeployer extends SubDeployerSupport
implements HARDeployerMBean
{
private static final ObjectName OBJECT_NAME = ObjectNameFactory.create("jboss.har:service=HARDeployer");
private static final String DESCRIPTOR_NAME = "hibernate-service.xml";
private static final String RELATIVE_DESCRIPTOR_URL = "META-INF/" + DESCRIPTOR_NAME;
private ServiceControllerMBean serviceController;
/** A map of current deployments. */
private HashMap deployments = new HashMap();
/** A flag indicating if deployment descriptors should be validated */
private boolean validateDTDs;
/**
* 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 HARDeployer()
{
setSuffixes( new String[] { ".har" } );
setRelativeOrder( RELATIVE_ORDER_400 );
}
/**
* 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
);
// 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
{
Iterator modules = deployments.values().iterator();
while ( modules.hasNext() )
{
DeploymentInfo di = (DeploymentInfo)modules.next();
stop(di);
}
// avoid concurrent modification exception
modules = new ArrayList( deployments.values() ).iterator();
while ( modules.hasNext() )
{
DeploymentInfo di = (DeploymentInfo)modules.next();
destroy(di);
}
deployments.clear();
// deregister with MainDeployer
super.stopService();
serviceController = null;
}
/**
* 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;
}
// -------------------------------------------------------------------------
// Sub-deployable bit ------------------------------------------------------
// This is all essentially saying that the hibernate-service.xml file
// consitutes a nested deployment (i.e., isDeployable) such that a
// subdeployment is generated for that file by itself. The whole
// purpose of this fact is to get SARDeployer to pick up the service
// xml file and parse it. At some point this gets "linked back" to our
// DeploymentInfo.subDeployments so that we have access to it.
//
// TODO : this all seems very hacky.
// Would prefer having SARDeployer xml parsing logic encapsulated so that it
// can just get used directly from here.
protected void processNestedDeployments(DeploymentInfo di) throws DeploymentException
{
super.processNestedDeployments( di );
}
protected void deployUrl(DeploymentInfo di, URL url, String name) throws DeploymentException
{
super.deployUrl( di, url, name );
}
public boolean isDeployable(String name, URL url)
{
// Allow nested jars to get deployed.
//
// We also mark the 'hibernate-service.xml' file as deployable so that
// the SARDeployer can pick it up and parse its content as described above; yick!
log.debug( "Checking deployability of [name=" + name + ", url=" + url.getFile() + "]" );
return name.endsWith( ".jar" ) || name.endsWith( RELATIVE_DESCRIPTOR_URL );
}
// -------------------------------------------------------------------------
/**
* The HARDeployer accepts either archives ending in '.har' or exploded
* directories (with name ending in '.har/'). Furthermore, the deployment
* must contain a file named 'META-INF/hibernate-service.xml' in order to
* be accepted.
*
* @param di The deployment info for the deployment to be checked for
* acceptance.
* @return True if the conditions mentioned above hold true; false otherwise.
*/
public boolean accepts(DeploymentInfo di)
{
// To be accepted the deployment's root name must end in .har
String urlStr = di.url.getFile();
if ( !urlStr.endsWith( ".har" ) && !urlStr.endsWith( ".har/" ) )
{
return false;
}
// However the har must also contain a ${RELATIVE_DESCRIPTOR_URL}
boolean accept = false;
try
{
URL dd = di.localCl.findResource( RELATIVE_DESCRIPTOR_URL );
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 an ejb-jar
if ( di.localUrl != null )
{
urlStr = di.localUrl.toString();
}
String ddStr = dd.toString();
if( ddStr.indexOf( urlStr ) >= 0 )
{
accept = true;
}
}
}
catch( Exception ignore )
{
}
log.debug( "accept> url=" + di.url + ", accepted=" + accept );
return accept;
}
/**
* Initialize the given deployment. Overriden to perform custom watch logic.
*
* @param di The deployment to be initialized.
* @throws DeploymentException
*/
public void init(DeploymentInfo di) throws DeploymentException
{
log.debug( "Deploying HAR; init; " + di );
try
{
if( "file".equalsIgnoreCase( di.url.getProtocol() ) )
{
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, DESCRIPTOR_NAME );
}
}
else
{
// We watch the top only, no directory support
di.watch = di.url;
}
}
catch( Exception e )
{
if ( e instanceof DeploymentException )
{
throw ( DeploymentException ) e;
}
throw new DeploymentException( "failed to initialize", e );
}
// invoke super-class initialization
super.init( di );
}
public synchronized void create(DeploymentInfo di) throws DeploymentException
{
log.debug( "Deploying HAR; create; " + di );
super.create( di );
// Impl note: this, again, is relying on the fact that the SARDeployer has
// already parsed the hibernate-service.xml file and created any defined
// mbeans; these are then available through the 'subDeployments' attribute
// of the DeploymentInfo related to the overall har file. clear as mud? :)
//
// *there should only ever be one sub-deployment here...
Iterator subdeployments = di.subDeployments.iterator();
while ( subdeployments.hasNext() )
{
DeploymentInfo nested = ( DeploymentInfo ) subdeployments.next();
log.debug( "Checking sub-deployment [" + nested.url + "] for descriptor-name" );
// check each sub-deployment to find the one relating to our
// hibernate-service.xml file.
if ( nested.url.getFile().endsWith( DESCRIPTOR_NAME ) )
{
log.debug( "Attempting to locate HibernateMBean in sub-deployment [" + nested.url + "]" );
Iterator mbeans = nested.mbeans.iterator();
// For all the mbeans defined on our service descriptor, locate the
// Hibernate mbean and pass it the har deployment's url so that
// it can auto detect the mapping files.
while ( mbeans.hasNext() )
{
ObjectName service = ( ObjectName ) mbeans.next();
log.debug( "Testing [" + service + "] as HibernateMBean" );
if ( isHibernateMBean( service ) )
{
log.debug( "Located HibernateMBean" );
Attribute attr = new Attribute( "HarUrl", di.url );
try
{
server.setAttribute( service, attr );
}
catch( Exception e )
{
throw new DeploymentException( "Failed to set HarUrl attribute: " + e.getMessage(), e );
}
}
}
break;
}
}
}
public synchronized void start(DeploymentInfo di) throws DeploymentException
{
log.debug( "Deploying HAR; start; " + di );
super.start( di );
}
public void stop(DeploymentInfo di) throws DeploymentException
{
log.debug( "Undeploying HAR; stop; " + di );
try
{
serviceController.stop(di.deployedObject);
}
catch(Exception e)
{
throw new DeploymentException("problem stopping har module: " + di.url, e);
}
super.stop(di);
}
public void destroy(DeploymentInfo di) throws DeploymentException
{
log.debug( "Undeploying HAR; destroy; " + di );
// 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 har module: " + di.url, e);
}
super.destroy( di );
}
private boolean isHibernateMBean(ObjectName service)
{
try
{
MBeanInfo serviceInfo = server.getMBeanInfo( service );
return Hibernate.class.getName().equals( serviceInfo.getClassName() );
}
catch( Throwable t )
{
log.warn( "Unable to determine whether MBean [" + service + "] is Hibernate MBean" );
return false;
}
}
}
| HARDeployer.java |