/***************************************************
* *
* Mobicents: The Open Source VoIP Platform *
* *
* Distributable under LGPL license. *
* See terms of license at gnu.org. *
* *
***************************************************/
package org.mobicents.slee.container.component.deployment;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.slee.ComponentID;
import javax.slee.EventTypeID;
import javax.slee.SLEEException;
import javax.slee.SbbID;
import javax.slee.ServiceID;
import javax.slee.management.AlreadyDeployedException;
import javax.slee.management.DependencyException;
import javax.slee.management.DeployableUnitID;
import javax.slee.management.DeploymentException;
import javax.slee.management.LibraryID;
import javax.slee.profile.ProfileSpecificationID;
import javax.slee.resource.ResourceAdaptorID;
import javax.slee.resource.ResourceAdaptorTypeID;
import org.apache.log4j.Logger;
//import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.container.component.ComponentRepository;
import org.mobicents.slee.container.component.EventTypeComponent;
import org.mobicents.slee.container.component.ProfileSpecificationComponent;
import org.mobicents.slee.container.component.ResourceAdaptorComponent;
import org.mobicents.slee.container.component.ResourceAdaptorTypeComponent;
import org.mobicents.slee.container.component.SbbComponent;
import org.mobicents.slee.container.component.ServiceComponent;
import org.mobicents.slee.container.component.SleeComponent;
import org.mobicents.slee.container.component.deployment.classloading.ComponentClassLoader;
import org.mobicents.slee.container.component.deployment.classloading.URLClassLoaderDomain;
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.component.deployment.jaxb.descriptors.common.MUsageParametersInterface;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.profile.MProfileTableInterface;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.sbb.MSbbActivityContextInterface;
import org.mobicents.slee.container.component.deployment.jaxb.descriptors.sbb.MSbbLocalInterface;
public class DeployableUnitBuilder {
private static final Logger logger = Logger
.getLogger(DeployableUnitBuilder.class);
private static final DeployableUnitJarComponentBuilder duComponentBuilder = new DeployableUnitJarComponentBuilder();
private static final DeployableUnitServiceComponentBuilder duServiceComponentBuilder = new DeployableUnitServiceComponentBuilder();
/**
* Installs a JAIN SLEE DU.
*
* @param sourceUrl
* the original URL of the deployable unit jar
* @param deploymentRoot
* the root dir where this du will be installed
* @param componentRepository
* the repository to retrieve components
* @return
* @throws DeploymentException
*/
public DeployableUnit build(String url, File deploymentRoot,
ComponentRepository componentRepository)
throws DeploymentException, AlreadyDeployedException,
MalformedURLException {
if (logger.isDebugEnabled()) {
logger.debug("Building DU from " + url);
}
DeployableUnitID deployableUnitID = new DeployableUnitID(url);
if (deploymentRoot == null) {
throw new NullPointerException("null deploymentRoot");
}
URL sourceUrl = new URL(url);
// create jar file
JarFile deployableUnitJar = null;
try {
deployableUnitJar = new JarFile(sourceUrl.getFile());
} catch (IOException e) {
throw new DeploymentException(
"Failed to open DU file as JAR file: "
+ sourceUrl.getFile(), e);
}
if (deployableUnitJar == null) {
throw new NullPointerException("null deployableUnitJar");
}
// get and parse du descriptor
JarEntry duXmlEntry = deployableUnitJar
.getJarEntry("META-INF/deployable-unit.xml");
if (duXmlEntry == null) {
throw new DeploymentException(
"META-INF/deployable-unit.xml was not found in "
+ deployableUnitJar.getName());
}
DeployableUnitDescriptorFactory descriptorFactory = new DeployableUnitDescriptorFactory();
DeployableUnitDescriptorImpl deployableUnitDescriptor = null;
try {
deployableUnitDescriptor = descriptorFactory
.parse(deployableUnitJar.getInputStream(duXmlEntry));
} catch (IOException e) {
try {
deployableUnitJar.close();
} catch (IOException e1) {
logger.error(e.getMessage(), e);
}
throw new DeploymentException(
"Failed to get DU descriptor DU inputstream from JAR file "
+ sourceUrl.getFile(), e);
}
// create the du dir
File deploymentDir = createTempDUDeploymentDir(deploymentRoot,
deployableUnitID);
// build du object
DeployableUnit deployableUnit = null;
try {
deployableUnit = new DeployableUnit(deployableUnitID,
deployableUnitDescriptor, componentRepository,
deploymentDir);
// build each du jar component
for (String jarFileName : deployableUnitDescriptor.getJarEntries()) {
for (SleeComponent sleeComponent : duComponentBuilder
.buildComponents(jarFileName, deployableUnitJar,
deployableUnit.getDeploymentDir())) {
sleeComponent.setDeployableUnit(deployableUnit);
if (componentRepository.isInstalled(sleeComponent
.getComponentID())) {
throw new AlreadyDeployedException("Component "
+ sleeComponent.getComponentID()
+ " already deployed");
}
sleeComponent.setDeploymentUnitSource(jarFileName);
}
}
// build each du service component
for (String serviceDescriptorFileName : deployableUnitDescriptor
.getServiceEntries()) {
for (ServiceComponent serviceComponent : duServiceComponentBuilder
.buildComponents(serviceDescriptorFileName,
deployableUnitJar)) {
serviceComponent.setDeployableUnit(deployableUnit);
if (componentRepository.isInstalled(serviceComponent
.getComponentID())) {
throw new AlreadyDeployedException("Component "
+ serviceComponent.getComponentID()
+ " already deployed");
}
// set the direct reference to the sbb component
serviceComponent.setRootSbbComponent(deployableUnit
.getDeployableUnitRepository().getComponentByID(
serviceComponent.getDescriptor()
.getRootSbbID()));
serviceComponent
.setDeploymentUnitSource(serviceDescriptorFileName);
}
}
// get a set with all components of the DU
Set<SleeComponent> duComponentsSet = deployableUnit
.getDeployableUnitComponents();
// now that all components are built we need to
for (SleeComponent sleeComponent : duComponentsSet) {
// check if all
// dependencies are available
checkDependencies(sleeComponent, deployableUnit);
// build its class loader
createClassLoader(sleeComponent);
}
// load the provided classes for the component
for (SleeComponent sleeComponent : duComponentsSet) {
loadAndSetNonGeneratedComponentClasses(sleeComponent);
}
//boolean secEnabled = SleeContainer.isSecurityEnabled();
// validate each component
for (SleeComponent sleeComponent : duComponentsSet) {
ClassLoader componentClassLoader = sleeComponent
.getClassLoader();
ClassLoader oldClassLoader = Thread.currentThread()
.getContextClassLoader();
try {
if (componentClassLoader != null) {
Thread.currentThread().setContextClassLoader(
componentClassLoader);
}
if (logger.isDebugEnabled()) {
logger.debug("Validating " + sleeComponent);
}
if (!sleeComponent.validate()) {
throw new DeploymentException(
sleeComponent.toString()
+ " validation failed, check logs for errors found");
}
//Make permissions object, this instruments codebase etc, and store POJOs in component.
//if(secEnabled)
//{
sleeComponent.processSecurityPermissions();
//}
} catch (Throwable e) {
throw new DeploymentException("failed to validate "
+ sleeComponent, e);
} finally {
if (componentClassLoader != null) {
Thread.currentThread().setContextClassLoader(
oldClassLoader);
}
}
}
try {
deployableUnitJar.close();
} catch (IOException e) {
logger.error("failed to close deployable jar from " + url, e);
}
return deployableUnit;
} catch (Throwable e) {
if (deployableUnit != null) {
if (logger.isInfoEnabled()) {
logger
.info(
"Undeploying deployable unit due to building error",
e);
}
deployableUnit.undeploy();
} else {
if (logger.isDebugEnabled()) {
logger.debug("Deleting deployable unit temp dir "
+ deploymentDir);
}
deploymentDir.delete();
}
if (e instanceof DeploymentException) {
throw (DeploymentException) e;
} else if (e instanceof AlreadyDeployedException) {
throw (AlreadyDeployedException) e;
} else {
throw new DeploymentException(
"failed to build deployable unit", e);
}
}
}
public static void createClassLoader(SleeComponent component) {
URLClassLoaderDomain classLoaderDomain = component.getClassLoaderDomain();
if (classLoaderDomain != null) {
// add all dependency domains to the component domain
for (ComponentID componentID : component.getDependenciesSet()) {
SleeComponent dependency = null;
if (componentID instanceof EventTypeID) {
dependency = component.getDeployableUnit()
.getDeployableUnitRepository().getComponentByID(
(EventTypeID) componentID);
} else if (componentID instanceof LibraryID) {
dependency = component.getDeployableUnit()
.getDeployableUnitRepository().getComponentByID(
(LibraryID) componentID);
} else if (componentID instanceof ProfileSpecificationID) {
dependency = component.getDeployableUnit()
.getDeployableUnitRepository().getComponentByID(
(ProfileSpecificationID) componentID);
} else if (componentID instanceof ResourceAdaptorID) {
dependency = component.getDeployableUnit()
.getDeployableUnitRepository().getComponentByID(
(ResourceAdaptorID) componentID);
} else if (componentID instanceof ResourceAdaptorTypeID) {
dependency = component.getDeployableUnit()
.getDeployableUnitRepository().getComponentByID(
(ResourceAdaptorTypeID) componentID);
} else if (componentID instanceof SbbID) {
dependency = component.getDeployableUnit()
.getDeployableUnitRepository().getComponentByID(
(SbbID) componentID);
}
if (dependency != null && dependency.getClassLoaderDomain() != null) {
classLoaderDomain.getDependencies().add(dependency.getClassLoaderDomain());
if (logger.isDebugEnabled()) {
logger.debug(classLoaderDomain+" added "+dependency.getClassLoaderDomain()+ " in dependencies");
}
}
else {
throw new SLEEException(component.toString()+" dependency "+componentID+" not found or doesn't have class loading domain");
}
}
// create class loader
component.setClassLoader(new ComponentClassLoader(component.getComponentID(),classLoaderDomain));
}
}
/**
* Loads all non SLEE generated classes from the component class loader to
* the component, those will be needed for validation or runtime purposes
*
* @param sleeComponent
* @throws DeploymentException
*/
private void loadAndSetNonGeneratedComponentClasses(
SleeComponent sleeComponent) throws DeploymentException {
if (logger.isDebugEnabled()) {
logger.debug("Loading classes for component " + sleeComponent);
}
ClassLoader oldClassLoader = Thread.currentThread()
.getContextClassLoader();
try {
ComponentClassLoader componentClassLoader = sleeComponent
.getClassLoader();
if (componentClassLoader != null) {
// change class loader
Thread.currentThread().setContextClassLoader(
componentClassLoader);
// load and set non generated component classes
if (sleeComponent instanceof EventTypeComponent) {
EventTypeComponent component = (EventTypeComponent) sleeComponent;
Class<?> eventTypeClass = componentClassLoader
.loadClass(component.getDescriptor()
.getEventClassName());
component.setEventTypeClass(eventTypeClass);
} else if (sleeComponent instanceof ProfileSpecificationComponent) {
ProfileSpecificationComponent component = (ProfileSpecificationComponent) sleeComponent;
Class<?> profileCmpInterfaceClass = componentClassLoader
.loadClass(component.getDescriptor()
.getProfileClasses()
.getProfileCMPInterface()
.getProfileCmpInterfaceName());
component
.setProfileCmpInterfaceClass(profileCmpInterfaceClass);
if (component.getDescriptor().getProfileClasses()
.getProfileLocalInterface() != null) {
Class<?> profileLocalInterfaceClass = componentClassLoader
.loadClass(component.getDescriptor()
.getProfileClasses()
.getProfileLocalInterface()
.getProfileLocalInterfaceName());
component
.setProfileLocalInterfaceClass(profileLocalInterfaceClass);
}
if (component.getDescriptor().getProfileClasses()
.getProfileManagementInterface() != null) {
Class<?> profileManagementInterfaceClass = componentClassLoader
.loadClass(component.getDescriptor()
.getProfileClasses()
.getProfileManagementInterface()
.getProfileManagementInterfaceName());
component
.setProfileManagementInterfaceClass(profileManagementInterfaceClass);
}
if (component.getDescriptor().getProfileClasses()
.getProfileAbstractClass() != null) {
boolean decoratedClass = new ProfileAbstractClassDecorator(component).decorateAbstractClass();
Class<?> profileAbstractClass = null;
if (decoratedClass) {
// need to ensure we load the class from disk, not one coming from SLEE shared class loading domain
profileAbstractClass = componentClassLoader
.loadClassLocally(component.getDescriptor()
.getProfileClasses()
.getProfileAbstractClass()
.getProfileAbstractClassName());
}
else {
profileAbstractClass = componentClassLoader
.loadClass(component.getDescriptor()
.getProfileClasses()
.getProfileAbstractClass()
.getProfileAbstractClassName());
}
component.setProfileAbstractClass(profileAbstractClass);
}
MProfileTableInterface mProfileTableInterface = component
.getDescriptor().getProfileClasses()
.getProfileTableInterface();
if (mProfileTableInterface != null) {
component
.setProfileTableInterfaceClass(componentClassLoader
.loadClass(mProfileTableInterface
.getProfileTableInterfaceName()));
}
MUsageParametersInterface mUsageParametersInterface = component
.getDescriptor().getProfileClasses()
.getProfileUsageParameterInterface();
if (mUsageParametersInterface != null) {
component
.setUsageParametersInterface(componentClassLoader
.loadClass(mUsageParametersInterface
.getUsageParametersInterfaceName()));
}
} else if (sleeComponent instanceof ResourceAdaptorComponent) {
ResourceAdaptorComponent component = (ResourceAdaptorComponent) sleeComponent;
Class<?> resourceAdaptorClass = componentClassLoader
.loadClass(component.getDescriptor()
.getResourceAdaptorClassName());
component.setResourceAdaptorClass(resourceAdaptorClass);
MUsageParametersInterface mUsageParametersInterface = component
.getDescriptor()
.getResourceAdaptorUsageParametersInterface();
if (mUsageParametersInterface != null) {
component
.setUsageParametersInterface(componentClassLoader
.loadClass(mUsageParametersInterface
.getUsageParametersInterfaceName()));
}
} else if (sleeComponent instanceof ResourceAdaptorTypeComponent) {
ResourceAdaptorTypeComponent component = (ResourceAdaptorTypeComponent) sleeComponent;
if (component.getDescriptor()
.getActivityContextInterfaceFactoryInterface() != null) {
Class<?> activityContextInterfaceFactoryInterface = componentClassLoader
.loadClass(component
.getDescriptor()
.getActivityContextInterfaceFactoryInterface()
.getActivityContextInterfaceFactoryInterfaceName());
component
.setActivityContextInterfaceFactoryInterface(activityContextInterfaceFactoryInterface);
}
} else if (sleeComponent instanceof SbbComponent) {
SbbComponent component = (SbbComponent) sleeComponent;
// before loading the abstract class, we may have to decorate it
boolean decoratedClass = new SbbAbstractClassDecorator(component).decorateAbstractSbb();
Class<?> abstractSbbClass = null;
if (decoratedClass) {
// need to ensure we load the class from disk, not one coming from SLEE shared class loading domain
abstractSbbClass = componentClassLoader
.loadClassLocally(component.getDescriptor()
.getSbbClasses().getSbbAbstractClass()
.getSbbAbstractClassName());
}
else {
abstractSbbClass = componentClassLoader
.loadClass(component.getDescriptor()
.getSbbClasses().getSbbAbstractClass()
.getSbbAbstractClassName());
}
component.setAbstractSbbClass(abstractSbbClass);
MSbbLocalInterface mSbbLocalInterface = component
.getDescriptor().getSbbClasses()
.getSbbLocalInterface();
if (mSbbLocalInterface != null) {
component
.setSbbLocalInterfaceClass(componentClassLoader
.loadClass(mSbbLocalInterface
.getSbbLocalInterfaceName()));
}
MSbbActivityContextInterface mSbbActivityContextInterface = component
.getDescriptor().getSbbClasses()
.getSbbActivityContextInterface();
if (mSbbActivityContextInterface != null) {
component
.setActivityContextInterface(componentClassLoader
.loadClass(mSbbActivityContextInterface
.getInterfaceName()));
}
MUsageParametersInterface mUsageParametersInterface = component
.getDescriptor().getSbbClasses()
.getSbbUsageParametersInterface();
if (mUsageParametersInterface != null) {
component
.setUsageParametersInterface(componentClassLoader
.loadClass(mUsageParametersInterface
.getUsageParametersInterfaceName()));
}
}
}
} catch (ClassNotFoundException e) {
throw new DeploymentException("Component "
+ sleeComponent.getComponentID()
+ " requires a class that was not found", e);
} finally {
Thread.currentThread().setContextClassLoader(oldClassLoader);
}
}
/**
* Checks if all dependencies of a DU component exists
*
* @param sleeComponent
* @param deployableUnit
* @throws DependencyException
* if a dependency is missing
*/
private void checkDependencies(SleeComponent sleeComponent,
DeployableUnit deployableUnit) throws DependencyException {
for (ComponentID componentID : sleeComponent.getDependenciesSet()) {
if (componentID instanceof EventTypeID) {
if (deployableUnit.getDeployableUnitRepository()
.getComponentByID((EventTypeID) componentID) == null) {
throw new DependencyException(
"Component "
+ sleeComponent.getComponentID()
+ " depends on "
+ componentID
+ " which is not available in the component repository or in the deployable unit");
}
} else if (componentID instanceof LibraryID) {
if (deployableUnit.getDeployableUnitRepository()
.getComponentByID((LibraryID) componentID) == null) {
throw new DependencyException(
"Component "
+ sleeComponent.getComponentID()
+ " depends on "
+ componentID
+ " which is not available in the component repository or in the deployable unit");
}
} else if (componentID instanceof ProfileSpecificationID) {
if (deployableUnit.getDeployableUnitRepository()
.getComponentByID((ProfileSpecificationID) componentID) == null) {
throw new DependencyException(
"Component "
+ sleeComponent.getComponentID()
+ " depends on "
+ componentID
+ " which is not available in the component repository or in the deployable unit");
}
} else if (componentID instanceof ResourceAdaptorID) {
if (deployableUnit.getDeployableUnitRepository()
.getComponentByID((ResourceAdaptorID) componentID) == null) {
throw new DependencyException(
"Component "
+ sleeComponent.getComponentID()
+ " depends on "
+ componentID
+ " which is not available in the component repository or in the deployable unit");
}
} else if (componentID instanceof ResourceAdaptorTypeID) {
if (deployableUnit.getDeployableUnitRepository()
.getComponentByID((ResourceAdaptorTypeID) componentID) == null) {
throw new DependencyException(
"Component "
+ sleeComponent.getComponentID()
+ " depends on "
+ componentID
+ " which is not available in the component repository or in the deployable unit");
}
} else if (componentID instanceof SbbID) {
if (deployableUnit.getDeployableUnitRepository()
.getComponentByID((SbbID) componentID) == null) {
throw new DependencyException(
"Component "
+ sleeComponent.getComponentID()
+ " depends on "
+ componentID
+ " which is not available in the component repository or in the deployable unit");
}
} else if (componentID instanceof ServiceID) {
throw new SLEEException(
"Component "
+ sleeComponent.getComponentID()
+ " depends on a service component "
+ componentID
+ " which is not available in the component repository or in the deployable unit");
}
}
}
/**
* Creates the directory that will be used for unpacking the child jars for
* a given DU.
*
* @param rootDir
* @param sourceUrl
* @throws SLEEException
* if the dir can't be created
* @return
*/
private File createTempDUDeploymentDir(File deploymentRoot,
DeployableUnitID deployableUnitID) {
try {
// first create a dummy file to gurantee uniqueness. I would have
// been nice if the File class had a createTempDir() method
// IVELIN -- do not use jarName here because windows cannot see the
// path (exceeds system limit)
File tempFile = File.createTempFile("mobicents-slee-du-", "",
deploymentRoot);
File tempDUDeploymentDir = new File(tempFile.getAbsolutePath()
+ "-contents");
if (!tempDUDeploymentDir.exists()) {
tempDUDeploymentDir.mkdirs();
} else {
throw new SLEEException(
"Dir "
+ tempDUDeploymentDir
+ " already exists, unable to create deployment dir for DU "
+ deployableUnitID);
}
tempFile.delete();
return tempDUDeploymentDir;
} catch (IOException e) {
throw new SLEEException("Failed to create deployment dir for DU "
+ deployableUnitID, e);
}
}
}