package org.mobicents.slee.container.deployment.jboss;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.slee.ComponentID;
import javax.slee.resource.ConfigProperties;
import javax.slee.resource.ResourceAdaptorID;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.jboss.logging.Logger;
import org.mobicents.slee.container.SleeContainer;
import org.mobicents.slee.container.management.jmx.editors.ComponentIDPropertyEditor;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
/**
* This class represents a SLEE Deployable Unit, represented by a collection of
* Deployable Components. Contains all the DU dependencies, install/uninstall
* actions needed for the DU and post-install and pre-uninstall actions.
*
* @author Alexandre Mendon�a
* @version 1.0
*/
public class DeployableUnit {
// The logger.
private static Logger logger = Logger.getLogger(DeployableUnit.class);
// The DeploymentInfo short name
private String diShortName;
// The DeploymentInfo URL object
private URL diURL;
// A collection of the Deployable Components in this DU
private Collection<DeployableComponent> components = new ArrayList<DeployableComponent>();
// A collection of the IDs of the components in this DU.
private Collection<String> componentIDs = new ArrayList<String>();
// A collection of the IDs of the components that this DU depends on.
private Collection<String> dependencies = new ArrayList<String>();
// The install actions needed to install/activate this DU components.
private Collection<Object[]> installActions = new ArrayList<Object[]>();
// The post-install actions needed to install/activate this DU components.
private HashMap<String, Collection<Object[]>> postInstallActions = new HashMap<String, Collection<Object[]>>();
// The pre-uninstall actions needed to deactivate/uninstall this DU components.
private HashMap<String, Collection<Object[]>> preUninstallActions = new HashMap<String, Collection<Object[]>>();
// The install actions needed to deactivate/uninstall this DU components.
private Collection<Object[]> uninstallActions = new ArrayList<Object[]>();
// A flag indicating wether this DU is installed
private boolean isInstalled = false;
/**
* Constructor.
* @param duDeploymentInfo this DU deployment info.
* @param deploymentManager the DeploymentManager in charge of this DU.
* @throws Exception
*/
public DeployableUnit(DeployableUnitWrapper du) throws Exception
{
this.diShortName = du.getFileName();
this.diURL = du.getUrl();
// First action for the DU is always install.
installActions.add(new Object[] { "install", diURL.toString() });
// Parse the deploy-config.xml to obtain post-install/pre-uninstall actions
parseDeployConfig();
}
/**
* Adder method for a Deployable Component.
* @param dc the deployable component object.
*/
public void addComponent(DeployableComponent dc) {
if (logger.isTraceEnabled())
logger.trace("Adding Component " + dc.getComponentKey());
// Add the component ..
components.add(dc);
// .. the key ..
componentIDs.add(dc.getComponentKey());
// .. the dependencies ..
dependencies.addAll(dc.getDependencies());
// .. the install actions to be taken ..
installActions.addAll(dc.getInstallActions());
// .. post-install actions (if any) ..
Collection<Object[]> postInstallActionsStrings = postInstallActions
.remove(dc.getComponentKey());
if (postInstallActionsStrings != null
&& postInstallActionsStrings.size() > 0) {
installActions.addAll(postInstallActionsStrings);
} else if (dc.getComponentType() == DeployableComponent.RA_COMPONENT) {
ComponentID cid = dc.getComponentID();
String raID = dc.getComponentKey();
logger
.warn("\r\n------------------------------------------------------------"
+ "\r\nNo RA Entity and Link config for "
+ raID
+ " found. Using default values!"
+ "\r\n------------------------------------------------------------");
String raName = cid.getName();
// Add the default Create and Activate RA Entity actions to the Install Actions
installActions.add(new Object[] { "createResourceAdaptorEntity",
cid, raName, new ConfigProperties() });
installActions.add(new Object[] { "activateResourceAdaptorEntity",
raName });
// Create default link
installActions.add(new Object[] { "bindLinkName", raName, raName });
// Remove default link
uninstallActions.add(new Object[] { "unbindLinkName", raName });
// Add the default Deactivate and Remove RA Entity actions to the Uninstall Actions
uninstallActions.add(new Object[] {
"deactivateResourceAdaptorEntity", raName });
uninstallActions.add(new Object[] { "removeResourceAdaptorEntity",
raName });
}
// .. pre-uninstall actions (if any) ..
Collection<Object[]> preUninstallActionsStrings = preUninstallActions
.remove(dc.getComponentKey());
if (preUninstallActionsStrings != null)
uninstallActions.addAll(preUninstallActionsStrings);
// .. and finally the uninstall actions to the DU.
uninstallActions.addAll(dc.getUninstallActions());
}
/**
* Method for checking if DU is self-sufficient.
* @return true if the DU has no external dependencies.
*/
public boolean isSelfSufficient() {
// All dependencies in the DU components?
return componentIDs.containsAll(dependencies);
}
/**
* Method for obtaining the external dependencies for this DU, if any.
* @return a Collection of external dependencies identifiers.
*/
public Collection<String> getExternalDependencies() {
// Take all dependencies...
Collection<String> externalDependencies = new HashSet<String>(dependencies);
// Remove those which are contained in this DU
externalDependencies.removeAll(componentIDs);
// Return what's left.
return externalDependencies;
}
/**
* Method for checking if the DU has all the dependencies needed to be deployed.
* @param showMissing param to set whether to show or not missing dependencies.
* @return true if all the dependencies are satisfied.
*/
public boolean hasDependenciesSatisfied(boolean showMissing) {
// First of all check if it is self-sufficient
if (isSelfSufficient())
return true;
// If not self-sufficient, get the remaining dependencies
Collection<String> externalDependencies = getExternalDependencies();
// Remove those that are already installed...
externalDependencies.removeAll(DeploymentManager.INSTANCE.getDeployedComponents());
// Some remaining?
if (externalDependencies.size() > 0) {
if (showMissing) {
// List them to the user...
String missingDepList = "";
for (String missingDep : externalDependencies)
missingDepList += "\r\n +-- " + missingDep;
logger.info("Missing dependencies for " + this.diShortName
+ ":" + missingDepList);
}
// Return dependencies not satified.
return false;
}
// OK, dependencies satisfied!
return true;
}
/**
* Method for checking if this DU contains any component that is already deployed.
* @return true if there's a component that is already deployed.
*/
public boolean hasDuplicates() {
ArrayList<String> duplicates = new ArrayList<String>();
// For each component in the DU ..
for (String componentId : componentIDs) {
// Check if it is already deployed
if (DeploymentManager.INSTANCE.getDeployedComponents().contains(componentId)) {
duplicates.add(componentId);
}
}
if (duplicates.size() > 0) {
logger
.warn("The deployable unit '"
+ this.diShortName
+ "' contains components that are already deployed. The following are already installed:");
for (String dupComponent : duplicates)
logger.warn(" - " + dupComponent);
return true;
}
// If we got here, there's no dups.
return false;
}
/**
* Method for doing all the checking to make sure it is ready to be installed.
* @param showMissing param to set whether to show or not missing dependencies.
* @return true if all the pre-reqs are met.
*/
public boolean isReadyToInstall(boolean showMissing) {
// Check if the deps are satisfied and there are no dups.
return hasDependenciesSatisfied(showMissing) && !hasDuplicates();
}
/**
* Getter for the Install Actions.
* @return a Collection of actions.
*/
public Collection<Object[]> getInstallActions() {
ArrayList<Object[]> iActions = new ArrayList<Object[]>();
iActions.addAll(installActions);
// Let's check if we have some remaining install actions
if (postInstallActions.values().size() > 0) {
for (String componentId : postInstallActions.keySet()) {
iActions.addAll(postInstallActions.get(componentId));
}
}
return iActions;
}
/**
* Getter for the Uninstall Actions.
* @return a Collection of actions.
*/
public Collection<Object[]> getUninstallActions() {
Collection<Object[]> uActions = new ArrayList(uninstallActions);
// Let's check if we have some remaining install actions
if (preUninstallActions.values().size() > 0) {
for (String componentId : preUninstallActions.keySet()) {
uActions.addAll(preUninstallActions.get(componentId));
}
}
// To make sure uninstall is the last action, we add it just when we return them.
uActions.add(new Object[] { "uninstall", diURL.toString() });
return uActions;
}
/**
* Getter for this DU components.
* @return a Collection of component identifiers.
*/
public Collection<String> getComponents() {
return componentIDs;
}
/**
* Method for doing all the checking to make sure it is ready to be uninstalled.
* @return true if all the pre-reqs are met.
* @throws Exception
*/
public boolean isReadyToUninstall() throws Exception {
// Check DU for readiness ..
if (isInstalled && !hasReferringDU()) {
// Check each DU for it's readiness also
for (DeployableComponent dc : components) {
if (!dc.isUndeployable(this))
return false;
}
} else {
return false;
}
// It's good to go.
return true;
}
/**
* Method for checking if this DU components are referred by any others.
* @return true if there are other DUs installed referring this.
* @throws Exception
*/
private boolean hasReferringDU() throws Exception {
// Check if its safe to remove the deployable unit.
// Get SleeContainer instance from JNDI
SleeContainer sC = SleeContainer.lookupFromJndi();
for (String componentIdString : this.getComponents())
{
ComponentIDPropertyEditor cidpe = new ComponentIDPropertyEditor();
cidpe.setAsText( componentIdString );
ComponentID componentId = (ComponentID) cidpe.getValue();
for (ComponentID referringComponentId : sC.getComponentRepositoryImpl().getReferringComponents(componentId))
{
ComponentIDPropertyEditor rcidpe = new ComponentIDPropertyEditor();
rcidpe.setValue( referringComponentId );
String referringComponentIdString = rcidpe.getAsText();
if (!this.getComponents().contains( referringComponentIdString ))
{
return true;
}
}
}
return false;
}
/**
* Getter for the DeploymentInfo short name
* @return a String containing the filename
*/
public String getDeploymentInfoShortName() {
return this.diShortName;
}
/**
* Setter for the isInstalled flag.
* @return a boolean indicating if the DU is already installed.
*/
public boolean isInstalled() {
return isInstalled;
}
/**
* Setter for the isInstalled flag.
* @param isInstalled the isInstalled flag indicating that the DU is already installed.
*/
public void setInstalled(boolean isInstalled) {
this.isInstalled = isInstalled;
}
/**
* Parser for the deployment config xml.
* @throws Exception
*/
private void parseDeployConfig() throws Exception {
JarFile componentJarFile = null;
InputStream is = null;
try {
// Create a JarFile object
componentJarFile = new JarFile(diURL.getFile());
// Get the JarEntry for the deploy-config.xml
JarEntry deployInfoXML = componentJarFile
.getJarEntry("META-INF/deploy-config.xml");
// If it exists, set an Input Stream on it
is = deployInfoXML != null ? componentJarFile
.getInputStream(deployInfoXML) : null;
if (is != null) {
// Read the file into a Document
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(is);
// By now we only care about <ra-entitu> nodes
NodeList raEntities = doc.getElementsByTagName("ra-entity");
// The RA identifier
String raId = null;
// The collection of Post-Install Actions
Collection<Object[]> cPostInstallActions = new ArrayList<Object[]>();
// The collection of Pre-Uninstall Actions
Collection<Object[]> cPreUninstallActions = new ArrayList<Object[]>();
// Iterate through each ra-entity node
for (int i = 0; i < raEntities.getLength(); i++) {
Element raEntity = (Element) raEntities.item(i);
// Get the component ID
ComponentIDPropertyEditor cidpe = new ComponentIDPropertyEditor();
cidpe.setAsText(raEntity.getAttribute("resource-adaptor-id"));
raId = cidpe.getValue().toString();
// The RA Entity Name
String entityName = raEntity.getAttribute("entity-name");
// Select the properties node
NodeList propsNodeList = raEntity.getElementsByTagName("properties");
if (propsNodeList.getLength() > 1)
logger
.warn("Invalid ra-entity element, has more than one properties child. Reading only first.");
// The properties for this RA
ConfigProperties props = new ConfigProperties();
// FIXME: Alexandre: Add properties handling.
/*Element propsNode = (Element) propsNodeList.item(0);
// Do we have any properties at all?
if (propsNode != null) {
String propsFilename;
// Do we have a properties file to load?
if ((propsFilename = ((Element) propsNode)
.getAttribute("file")) != null
&& !propsFilename.equals("")) {
// Get the entry from the jar
JarEntry propsFile = componentJarFile
.getJarEntry("META-INF/" + propsFilename);
// Load it.
props.load(componentJarFile
.getInputStream(propsFile));
}
// Select the property elements
NodeList propsList = propsNode
.getElementsByTagName("property");
// For each element, add it to the Properties object
for (int j = 0; j < propsList.getLength(); j++) {
Element property = (Element) propsList.item(j);
// If the property already exists, it will be overwritten.
props.put(property.getAttribute("name"), property
.getAttribute("value"));
}
}*/
// Create the Resource Adaptor ID
// ComponentIDPropertyEditor cidpe = new ComponentIDPropertyEditor();
cidpe.setAsText(raEntity.getAttribute("resource-adaptor-id"));
ResourceAdaptorID componentID = (ResourceAdaptorID) cidpe.getValue();
// Add the Create and Activate RA Entity actions to the Post-Install Actions
cPostInstallActions.add(new Object[] {
"createResourceAdaptorEntity", componentID,
entityName, props });
cPostInstallActions.add(new Object[] {
"activateResourceAdaptorEntity", entityName });
// Each RA might have zero or more links.. get them
NodeList links = raEntity.getElementsByTagName("ra-link");
for (int j = 0; j < links.getLength(); j++) {
String linkName = ((Element) links.item(j))
.getAttribute("name");
cPostInstallActions.add(new Object[] { "bindLinkName",
entityName, linkName });
cPreUninstallActions.add(new Object[] {
"unbindLinkName", linkName });
}
// Add the Deactivate and Remove RA Entity actions to the Pre-Uninstall Actions
cPreUninstallActions.add(new Object[] {
"deactivateResourceAdaptorEntity", entityName });
cPreUninstallActions.add(new Object[] {
"removeResourceAdaptorEntity", entityName });
// Finally add the actions to the respective hashmap.
if (raId != null) {
// We need to check if we are updating or adding new ones.
if (postInstallActions.containsKey(raId))
postInstallActions.get(raId).addAll(
cPostInstallActions);
else
postInstallActions.put(raId, cPostInstallActions);
// Same here...
if (preUninstallActions.containsKey(raId))
preUninstallActions.get(raId).addAll(
cPreUninstallActions);
else
preUninstallActions.put(raId, cPreUninstallActions);
}
// Now we clean the lists for the next round (might come a new RA ID)...
cPostInstallActions = new ArrayList<Object[]>();
cPreUninstallActions = new ArrayList<Object[]>();
raId = null;
}
}
} finally {
// Clean depoy-config.xml inputstream
try {
if (is != null)
is.close();
} finally {
is = null;
}
// Clean jar input streams
try {
if (componentJarFile != null)
componentJarFile.close();
} finally {
componentJarFile = null;
}
}
}
public URL getURL() {
return diURL;
}
}