package org.mobicents.slee.container.deployment.jboss;
import java.io.IOException;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.slee.InvalidStateException;
import org.jboss.deployment.DeploymentException;
import org.jboss.deployment.SubDeployerSupport;
import org.jboss.logging.Logger;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.DeployableUnitDescriptorFactory;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.DeployableUnitDescriptorImpl;
import org.mobicents.slee.container.management.jmx.MobicentsManagement;
/**
* This is the Deployer main class where the AS will invoke the lifecycle methods
* for new deployments.
*
* @author Alexandre Mendon�a
* @version 1.0
*/
@SuppressWarnings("deprecation")
public class SLEESubDeployer extends SubDeployerSupport implements SLEESubDeployerMBean, NotificationListener
{
public final static SLEESubDeployer INSTANCE = new SLEESubDeployer();
// Constants -----------------------------------------------------
// The suffixes to be accepted by this deployer (other than jars).
public static final String DEPLOYMENT_EXTENSION = ".mar";
public static final String BASE_SCRIPT_OBJECT_NAME = "jboss.scripts:type=BeanShell";
// The Logger.
private static Logger logger = Logger.getLogger( SLEESubDeployer.class );
private static Timer timer = new Timer();
// Attributes ----------------------------------------------------
// The manager instance.
//private DeploymentManager dm;
// The DIs to be accepted by this deployer.
private ConcurrentHashMap<String, DeployableUnitWrapper> toAccept = new ConcurrentHashMap<String, DeployableUnitWrapper>();
// Deployable Units present.
private ConcurrentHashMap<String, DeployableUnit> deployableUnits = new ConcurrentHashMap<String, DeployableUnit>();
private boolean isNotificationEnabled = false;
private boolean isServerShuttingDown = false;
// Constructors -------------------------------------------------------------
/**
* Default Constructor.
*/
private SLEESubDeployer()
{
//super();
setSuffixes( new String[] { DEPLOYMENT_EXTENSION, "xml", "jar" } );
//logger.info( "##### SLEE Deployer Initialized. #####" );
}
// SubDeployerSupport overrides ---------------------------------------------
// /**
// * Method for processing nested deployments.
// */
// @Override
// protected void processNestedDeployments( Deploy di ) throws DeploymentException
// {
// if( logger.isDebugEnabled() )
// logger.debug( "Method processNestedDeployments called for " + di.url );
//
// // We'd like some to pass (mar with DUs inside.. we'll check it at accept).
// //super.processNestedDeployments( di );
// }
/**
* Method for deciding whether or not to accept the file.
*/
public boolean accepts(DeployableUnitWrapper du)
{
URL url = du.getUrl();
if( logger.isDebugEnabled() )
logger.debug( "Method accepts called for " + url );
try
{
String fullPath = url.getFile();
String fileName = fullPath.substring( fullPath.lastIndexOf( '/' )+1, fullPath.length());
// Handle directory deployments
if( du.isDirectory() )
{
// We only care about MAR directories... for now.
if( fileName.endsWith( DEPLOYMENT_EXTENSION ) )
{
if( logger.isDebugEnabled() )
logger.debug( "Accepting MAR DIRECTORY " + url.toString() + "." );
return true;
}
}
// Is it in the toAccept list or is a MAR ? Direct accept.
else if( toAccept.containsKey( fileName ) || fileName.endsWith( DEPLOYMENT_EXTENSION ) )
{
if( logger.isDebugEnabled() )
logger.debug( "Accepting " + url.toString() + "." );
return true;
}
// If not it the accept list but it's a jar might be a DU jar...
else if( fileName.endsWith( ".jar" ) )
{
JarFile duJarFile = null;
try
{
// Obtain the reference to the file
//duJarFile = new JarFile( fullPath );
// Try to obtain the DU descriptor...
//JarEntry duXmlEntry = duJarFile.getJarEntry( "META-INF/deployable-unit.xml" );
// If we got it, we're accepting it!
if( du.getEntry( "META-INF/deployable-unit.xml" ) != null )
{
if( logger.isDebugEnabled() )
logger.debug( "Accepting " + url.toString() + "." );
return true;
}
}
finally
{
// Clean up!
if( duJarFile != null )
{
try
{
duJarFile.close();
}
catch ( IOException ignore )
{
}
finally
{
duJarFile = null;
}
}
}
}
}
catch ( Exception ignore )
{
// Ignore.. will reject.
}
// Uh-oh.. looks like it will stay outside.
return false;
}
/**
* Initializer method for accepted files. Will parse descriptors at this point.
*/
public void init(DeployableUnitWrapper du) throws DeploymentException
{
URL url = du.getUrl();
if( logger.isDebugEnabled() )
logger.debug( "Method init called for " + url );
if(server != null && !isNotificationEnabled)
{
try
{
server.addNotificationListener( new ObjectName("jboss.system:type=Server"), this, null, "" );
isNotificationEnabled = true;
}
catch ( Exception e )
{
logger.error( "Failed to register notification listener.", e );
}
}
// Get the filename for this di
String fullPath = du.getFullPath();
String fileName = du.getFileName();
try
{
DeployableUnitWrapper duWrapper = null;
// If we're able to remove it from toAccept was because it was there!
if( (duWrapper = toAccept.remove( fileName )) != null )
{
// Create a new Deployable Component from this DI.
DeployableComponent dc = new DeployableComponent( du, url, fileName );
// Also get the deployable unit for this (it exists, we've checked!)
DeployableUnit deployerDU = deployableUnits.get( duWrapper.getFileName() );
for( DeployableComponent subDC : dc.getSubComponents() )
{
// Add the sub-component to the DU object.
deployerDU.addComponent( subDC );
}
}
// If the DU for this component doesn't exists.. it's a new DU!
else if( fileName.endsWith( ".jar" ) )
{
JarFile duJarFile = null;
try
{
// Get a reference to the DU jar file
duJarFile = new JarFile( fullPath );
// Try to get the Deployable Unit descriptor
JarEntry duXmlEntry = duJarFile.getJarEntry( "META-INF/deployable-unit.xml" );
// Got descriptor?
if( duXmlEntry != null )
{
// Create a new Deployable Unit object.
DeployableUnit deployerDU = new DeployableUnit( du );
// Let's parse the descriptor to see what we've got...
DeployableUnitDescriptorFactory dudf = new DeployableUnitDescriptorFactory();
DeployableUnitDescriptorImpl duDesc = dudf.parse( duJarFile.getInputStream( duXmlEntry ) );
// Add it to the deployable units map.
deployableUnits.put( fileName, deployerDU );
// Go through each jar entry in the DU descriptor
for( String componentJarName : duDesc.getJarEntries() )
{
// Might have path... strip it!
int beginIndex;
if( ( beginIndex = componentJarName.lastIndexOf( '/' ) ) == -1 )
beginIndex = componentJarName.lastIndexOf( '\\' );
beginIndex++;
// Got a clean jar name, no paths.
componentJarName = componentJarName.substring( beginIndex, componentJarName.length() );
// Put it in the accept list.
toAccept.put( componentJarName, du );
}
// Do the same as above... but for services
for( String serviceXMLName : duDesc.getServiceEntries() )
{
// Might have path... strip it!
int beginIndex;
if( ( beginIndex = serviceXMLName.lastIndexOf( '/' ) ) == -1 )
beginIndex = serviceXMLName.lastIndexOf( '\\' );
beginIndex++;
// Got a clean XML filename
serviceXMLName = serviceXMLName.substring( beginIndex, serviceXMLName.length() );
// Add it to the accept list.
toAccept.put( serviceXMLName, du );
}
}
}
finally
{
// Clean up!
if( duJarFile != null )
{
try
{
duJarFile.close();
}
catch ( IOException ignore )
{
}
finally
{
duJarFile = null;
}
}
}
}
}
catch ( Exception e )
{
// Something went wrong...
if( logger.isDebugEnabled() )
logger.debug( "Deployment of " + fileName + " failed.", e );
logger.error( "Deployment of " + fileName + " failed. ", e );
return;
}
//super.init( di );
}
/**
* Nothing to do here.
*/
public void create( DeployableUnitWrapper du ) throws DeploymentException
{
if( logger.isDebugEnabled() )
logger.debug( "Method create called for " + du.getUrl() );
//super.create( di );
}
/**
* This is where the fun begins. Time to deploy!
*/
public void start( DeployableUnitWrapper du ) throws DeploymentException
{
if( logger.isDebugEnabled() )
logger.debug( "Method start called for " + du.getUrl() );
try
{
// Get the deployable unit object
DeployableUnit realDU = deployableUnits.get( du.getFileName() );
// If it exists, install it.
if( realDU != null)
DeploymentManager.INSTANCE.installDeployableUnit( realDU );
}
catch (Exception e)
{
logger.error( "", e );
}
//super.start( di );
}
/**
* Fun has ended. Time to undeploy.
*/
public void stop( DeployableUnitWrapper du ) throws DeploymentException
{
DeployableUnit realDU = null;
String fileName = du.getFileName();
if ((realDU = deployableUnits.get(du.getFileName())) != null)
{
if(logger.isTraceEnabled())
{
logger.trace( "Got DU: " + realDU.getDeploymentInfoShortName() );
}
// We get here when we have components depending on this...
try {
// Let's store the 'old' UCL.
//dm.addReplacedUCL(du, di.ucl);
// Just to make sure we won't lose our classes, we 'hide' the
// UCL
//di.ucl = null;
// If the above fails, might think about using this :)
// di.createClassLoaders();
} catch (Exception e1) {
logger.debug("Failed to add old UCL to list.", e1);
}
if( isServerShuttingDown ) {
doStop( fileName );
}
else
{
// Schedule removal in a recurring task
timer.scheduleAtFixedRate(new UndeploymentTask(fileName), 0, getWaitTimeBetweenOperations());
}
}
}
private boolean doStop( String filename )
{
if( logger.isDebugEnabled() )
logger.debug( "Method stop called for " + filename );
DeployableUnit du = null;
try
{
// Get and remove deployable unit object
if( (du = deployableUnits.get( filename )) != null )
{
// Uninstall it
DeploymentManager.INSTANCE.uninstallDeployableUnit( du );
// Remove it from list if successful
deployableUnits.remove( filename );
// Make it null. clean.
du = null;
}
}
catch (Exception e)
{
Throwable cause = e.getCause();
if(cause instanceof InvalidStateException) {
logger.warn(cause.getLocalizedMessage() + "... WAITING ...");
}
else if (e instanceof DeploymentException){
throw new IllegalStateException(e.getLocalizedMessage(), e);
}
else {
logger.error(e.getMessage(), e);
}
return false;
}
return true;
//FIXME: alexandre: Hope this is not needed... worked previously on the stop method.
//super.stop( di );
}
/**
* Nothing left to do here.
*/
public void destroy( DeployableUnitWrapper du ) throws DeploymentException
{
if( logger.isDebugEnabled() )
logger.debug( "Method destroy called for " + du.getUrl() );
// Should we clean temporary files here?
//FIXME: alexandre: Hope this is not needed... remove due to the new stop method.
//super.destroy( di );
}
// MBean Operations ---------------------------------------------------------
/**
* MBean operation for getting Deployer status.
*/
public String showStatus() throws DeploymentException
{
String output = "";
output += "<p>Deployable Units List:</p>";
for( String key : deployableUnits.keySet() )
{
output += "<" + key + "> [" + deployableUnits.get( key ) + "]<br>";
for( String duComponent : deployableUnits.get( key ).getComponents() )
{
output += "+-- " + duComponent + "<br>";
}
}
output += "<p>To Accept List:</p>";
for( String key : toAccept.keySet() )
{
output += "<" + key + "> [" + toAccept.get( key ) + "]<br>";
}
output += "<p>Deployment Manager Status</p>";
output += DeploymentManager.INSTANCE.showStatus();
return output;
}
/**
* MBean WaitTimeBetweenOperations property getter
*/
public long getWaitTimeBetweenOperations()
{
return DeploymentManager.INSTANCE.waitTimeBetweenOperations;
}
/**
* MBean WaitTimeBetweenOperations property getter
*/
public void setWaitTimeBetweenOperations(long waitTime)
{
DeploymentManager.INSTANCE.waitTimeBetweenOperations = waitTime;
}
// Aux Functions ------------------------------------------------------------
// /**
// * Method for parsing a Deployable Unit descriptor. Gotten from DeployableUnitDeployer.
// */
// private DeployableUnitDescriptorImpl parseDUDescriptor( JarFile unitJarFile ) throws DeploymentException
// {
// // Get the deployable unit descriptor entry
// JarEntry duXmlEntry = unitJarFile.getJarEntry( "META-INF/deployable-unit.xml" );
//
// // Don't have one? Go away!
// if( duXmlEntry == null )
// throw new DeploymentException( "No DeployableUnitDeploymentDescriptor descriptor (META-INF/deployable-unit.xml) was found in deployable unit" + unitJarFile.getName() );
//
// // The Document
// Document doc = null;
//
// InputStream is = null;
//
// try
// {
// // Get the InputStream
// is = unitJarFile.getInputStream( duXmlEntry );
//
// // Parse the descriptor
// doc = XMLUtils.parseDocument( is, false );
// }
// catch ( IOException ex )
// {
// throw new DeploymentException( "Failed to extract the DU depl " + "descriptor from " + unitJarFile.getName() );
// }
// finally
// {
// // Clean up!
// if( is != null )
// {
// try
// {
// is.close();
// }
// catch ( IOException ignore )
// {
// }
// finally
// {
// is = null;
// }
// }
// }
//
// Element duNode = doc.getDocumentElement();
// DeployableUnitDescriptorImpl deployableUnitDescriptor = new DeployableUnitDescriptorImpl( unitJarFile.getName(), new Date() ); // getDescriptor();
//
// try
// {
// // Get the description, if any.
// String description = XMLUtils.getElementTextValue( duNode, XMLConstants.DESCRIPTION_ND );
//
// // Store it.
// if( description != null )
// deployableUnitDescriptor.setDescription( description );
// }
// catch ( Exception ex )
// {
// throw new DeploymentException( ex.getMessage() );
// }
//
// // Get a list of the jars in the deployable unit.
// List<Element> jarNodes = XMLUtils.getAllChildElements( duNode, XMLConstants.JAR_ND );
// deployableUnitDescriptor.setJarNodes( jarNodes );
//
// // Same for the services
// List<Element> serviceNodes = XMLUtils.getAllChildElements( duNode, XMLConstants.SERVICE_XML_ND );
// deployableUnitDescriptor.setServiceNodes( serviceNodes );
//
// // Got nothing on this DU?
// if( jarNodes.size() == 0 && serviceNodes.size() == 0 )
// {
// throw new DeploymentException( "The " + unitJarFile.getName() + " deployable unit contains no jars or services" );
// }
//
// // All good. Return the parsed descriptor.
// return deployableUnitDescriptor;
// }
private class UndeploymentTask extends TimerTask
{
String filename;
long startTime;
public UndeploymentTask(String filename)
{
this.filename = filename;
this.startTime = System.currentTimeMillis();
}
public void run()
{
try
{
long elapsedTime = System.currentTimeMillis() - this.startTime;
if( doStop(filename) || elapsedTime > 60 * 60 * 1000 + MobicentsManagement.entitiesRemovalDelay )
this.cancel();
}
catch (IllegalStateException ise) {
// This only happens when there are dependents. Let's cancel, it'll return.
this.cancel();
}
catch (Exception ignore)
{
// do nothing...
}
}
}
public void handleNotification( Notification notification, Object o )
{
if( notification.getType().equals( "org.jboss.system.server.stopped" ) )
isServerShuttingDown = true;
}
}