/*
* Copyright 2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.rioproject.cybernode.service;
import org.rioproject.deploy.DownloadRecord;
import org.rioproject.deploy.StagedData;
import org.rioproject.deploy.StagedSoftware;
import org.rioproject.deploy.SystemComponent;
import org.rioproject.impl.exec.Util;
import org.rioproject.impl.servicebean.DefaultServiceBeanContext;
import org.rioproject.opstring.ServiceElement;
import org.rioproject.impl.util.DownloadManager;
import org.rioproject.impl.util.FileUtils;
import org.rioproject.impl.system.ComputeResource;
import org.rioproject.impl.system.OperatingSystemType;
import org.rioproject.system.capability.PlatformCapability;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.*;
/**
* A utility to assist in downloading and installing data for a service.
*
* @author Dennis Reedy
*/
public class StagedDataManager {
/** Collection of PlatformCapability instances that have been installed
* by the ServiceBeanDelegateImpl */
private final Collection<PlatformCapability> installedPlatformCapabilities = new ArrayList<PlatformCapability>();
private final List<DownloadRecord> dlRecords = new ArrayList<DownloadRecord>();
private final Map<StagedData, DownloadRecord[]> downloadedArtifacts = new HashMap<StagedData, DownloadRecord[]>();
private ServiceElement sElem;
private ComputeResource computeResource;
/** Logger */
static Logger logger = LoggerFactory.getLogger("org.rioproject.cybernode");
/**
* Create a StagedDataManager
* @param computeResource The compute resource
*/
public StagedDataManager(ComputeResource computeResource) {
if(computeResource==null)
throw new IllegalArgumentException("ComputeResource is null");
this.computeResource = computeResource;
}
/**
* @param sElem The service element information
*/
public void setServiceElement(ServiceElement sElem) {
if(sElem==null)
throw new IllegalArgumentException("ServiceElement is null");
this.sElem = sElem;
}
/**
* Remove any installed PlatformCapability components
*
* @return An array of PlatformCapability instances that were removed. If
* none were removed, return a zero-length array
*/
public PlatformCapability[] removeInstalledPlatformCapabilities() {
return removeInstalledPlatformCapabilities(true);
}
/**
* Remove installed PlatformCapability components
*
* @param force Whether to remove the PlatformCapability, regardless of
* whether the remove on destroy attribute of the StatedData element is true
*
* @return An array of PlatformCapability instances that were removed. If
* none were removed, return a zero-length array
*/
public PlatformCapability[] removeInstalledPlatformCapabilities(boolean force) {
List<PlatformCapability> removed = new ArrayList<PlatformCapability>();
for (PlatformCapability pCap : installedPlatformCapabilities) {
StagedSoftware[] software = pCap.getStagedSoftware();
for(StagedSoftware sw : software) {
if(force && pCap.getUsageCount()==0) {
if(computeResource.removePlatformCapability(pCap, true))
removed.add(pCap);
} else {
if (sw.removeOnDestroy() && pCap.getUsageCount()==0) {
if(computeResource.removePlatformCapability(pCap, true))
removed.add(pCap);
}
}
}
}
return removed.toArray(new PlatformCapability[removed.size()]);
}
/**
* Remove any installed StagedData
*/
public void removeStagedData() {
for(Map.Entry<StagedData, DownloadRecord[]> entry : downloadedArtifacts.entrySet()) {
StagedData data = entry.getKey();
if(data.removeOnDestroy()) {
DownloadRecord[] records = entry.getValue();
for(DownloadRecord record : records) {
DownloadManager.remove(record);
}
}
}
}
/**
* Get any {@link org.rioproject.system.capability.PlatformCapability}
* instances that were installed
*
* @return A collection of
* {@link org.rioproject.system.capability.PlatformCapability} instances
* that were installed. If there were none installed, return a zero-length
* collection
*/
public Collection<PlatformCapability> getInstalledPlatformCapabilities() {
return installedPlatformCapabilities;
}
/**
* Get all DownloadRecords
*
* @return An array of {@link org.rioproject.deploy.DownloadRecord}
* instances that represent the download of a
* {@link org.rioproject.deploy.StagedData} instance. If nothing
* was downloaded return a zero-length array
*/
public DownloadRecord[] getDownloadRecords() {
return dlRecords.toArray(new DownloadRecord[dlRecords.size()]);
}
/**
* Get the map of {@link org.rioproject.deploy.StagedData} keys and
* {@link org.rioproject.deploy.DownloadRecord} values for each
* downloaded item.
*
* @return A map of {@link org.rioproject.deploy.StagedData} keys and
* {@link org.rioproject.deploy.DownloadRecord} values for each
* downloaded item. If nothing was downloaded, return an empty map
*/
public Map<StagedData, DownloadRecord[]> getDownloadedArtifacts() {
return downloadedArtifacts;
}
/**
* Download any artifacts the service requires
*
* @return A map of {@link org.rioproject.deploy.StagedData} keys and
* {@link org.rioproject.deploy.DownloadRecord} values for each
* downloaded item. If nothing was downloaded, return an empty map
*
* @throws Exception If errors occur downloading or creating
* {@link org.rioproject.system.capability.PlatformCapability} instances
*/
public Map<StagedData, DownloadRecord[]> download() throws Exception {
if(sElem==null)
throw new IllegalStateException("ServiceElement has not been set");
/* If there are provisionable capabilities, or data staging, perform
* the stagedData/installation */
Collection<SystemComponent> installableComponents =
sElem.getProvisionablePlatformCapabilities();
install(installableComponents);
/* Verify missing components. If there are any, go get them */
Collection<SystemComponent> missing = verifyPlatformCapabilities();
if(!missing.isEmpty()) {
install(missing);
logger.info("Missing requirements have been provisioned");
}
StagedData[] stagedData = sElem.getStagedData();
for (StagedData data : stagedData) {
DownloadRecord dlRec;
if (data.getInstallRoot().startsWith(File.separator)) {
DownloadManager dlManager = new DownloadManager(data);
dlRec = dlManager.download();
dlRecords.add(dlRec);
} else {
String provisionRoot = computeResource.getPersistentProvisioningRoot();
DownloadManager dlManager = new DownloadManager(provisionRoot, data);
dlRec = dlManager.download();
dlRecords.add(dlRec);
}
if (data.getPerms() != null) {
if (OperatingSystemType.isWindows()) {
logger.warn("Cannot apply permissions [{}] to StagedData on Windows", data.getPerms());
} else {
File toChmod;
StringBuilder perms = new StringBuilder();
if (dlRec.unarchived()) {
toChmod = new File(dlRec.getPath());
perms.append("-R ");
} else {
toChmod = new File(FileUtils.makeFileName(dlRec.getPath(), dlRec.getName()));
}
perms.append(data.getPerms());
logger.info("Applying permissions [{}] to data staged at [{}]",
perms.toString(), FileUtils.getFilePath(toChmod));
Util.chmod(toChmod, perms.toString());
}
}
downloadedArtifacts.put(data, new DownloadRecord[]{dlRec});
}
return downloadedArtifacts;
}
/**
* Verify that required platform capabilities are present
*
* @return A collection of {@code SystemComponent}s that are not found.
* If there are no missing requirements, return an empty collection
*/
public Collection<SystemComponent> verifyPlatformCapabilities() {
if(sElem==null)
throw new IllegalStateException("ServiceElement has not been set");
PlatformCapability[] platformCapabilities = computeResource.getPlatformCapabilities();
SystemComponent[] jsbRequirements =
sElem.getServiceLevelAgreements().getSystemRequirements().getSystemComponents();
ArrayList<SystemComponent> missing = new ArrayList<SystemComponent>();
/*
* If there are no PlatformCapability requirements we can return
* successfully
*/
if(jsbRequirements.length == 0)
return missing;
/*
* Check each of our PlatformCapability objects for supportability
*/
for (SystemComponent jsbRequirement : jsbRequirements) {
boolean supported = false;
/*
* Iterate through all resource PlatformCapability objects and see
* if any of them supports the current PlatformCapability. If none
* are found, then we dont have a match
*/
for (PlatformCapability pCap : platformCapabilities) {
if (pCap.supports(jsbRequirement)) {
supported = true;
/* Make sure that if the platform capability is
* provisionable that it is not being removed */
if(pCap.getType()==PlatformCapability.PROVISIONABLE &&
computeResource.removalInProcess(pCap)) {
logger.warn("Service [{}] has a requirement [{}] that " +
"is being removed as part of a previous " +
"instantiation. An attempt will be made " +
"to install the software", sElem.getName(), jsbRequirement);
missing.add(jsbRequirement);
logger.info("Wait for the removal to finish...");
do {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
} while(computeResource.removalInProcess(pCap));
}
break;
}
}
if (!supported) {
StagedSoftware data = jsbRequirement.getStagedSoftware();
if(data!=null) {
missing.add(jsbRequirement);
logger.info("Service [{}] has a missing requirement [{}]. An attempt will be made to install it.",
sElem.getName(), jsbRequirement);
} else {
logger.error("Service [{}] has a requirement for [{}], and it is not found on the Cybernode, not " +
"is it downloadable. The capability may have been administratively removed during the " +
"instantiation of the service", sElem.getName(), jsbRequirement);
}
}
}
return missing;
}
/*
* Install Platform capabilities
*/
private void install(Collection<SystemComponent> toInstall)
throws IllegalAccessException, InstantiationException,ClassNotFoundException {
for (SystemComponent sysComp : toInstall) {
String className = sysComp.getClassName();
if (className == null) {
Map<String, String> pCapMap =
computeResource.getPlatformCapabilityNameTable();
className = pCapMap.get(sysComp.getName());
}
PlatformCapability pCap = DefaultServiceBeanContext.createPlatformCapability(className,
sysComp.getAttributes());
installedPlatformCapabilities.add(pCap);
StagedSoftware staged = sysComp.getStagedSoftware();
if(staged!=null) {
DownloadRecord[] dlRecs = computeResource.provision(pCap, staged);
//downloadedArtifacts.put(sw, dlRecs);
Collections.addAll(dlRecords, dlRecs);
}
}
}
}