package alien4cloud.common;
import static alien4cloud.common.AlienConstants.APP_WORKSPACE_PREFIX;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.inject.Inject;
import org.alien4cloud.tosca.catalog.ArchiveDelegateType;
import org.alien4cloud.tosca.catalog.index.ArchiveIndexer;
import org.alien4cloud.tosca.catalog.index.CsarService;
import org.alien4cloud.tosca.model.CSARDependency;
import org.alien4cloud.tosca.model.Csar;
import org.alien4cloud.tosca.model.templates.AbstractTopologyVersion;
import org.alien4cloud.tosca.model.templates.Topology;
import com.google.common.collect.Lists;
import alien4cloud.dao.IGenericSearchDAO;
import alien4cloud.dao.model.GetMultipleDataResult;
import alien4cloud.exception.AlreadyExistException;
import alien4cloud.exception.NotFoundException;
import alien4cloud.exception.ReleaseReferencingSnapshotException;
import alien4cloud.utils.MapUtil;
import alien4cloud.utils.VersionUtil;
public abstract class AbtractVersionService<V extends AbstractTopologyVersion> {
@Resource(name = "alien-es-dao")
protected IGenericSearchDAO alienDAO;
@Inject
private ArchiveIndexer archiveIndexer;
@Inject
private CsarService csarService;
protected abstract V buildVersionImplem();
protected abstract V[] buildVersionImplemArray(int length);
protected abstract Class<V> getVersionImplemClass();
protected abstract ArchiveDelegateType getDelegateType();
protected abstract String getDelegatePropertyName();
/**
* Create a new version for an application/topology template based on an existing topology.
*
* @param delegateId The id of the application/topology template for which to create the version.
* @param topologyToCloneId The id of the topology to clone for the version's topology.
* @param version The new version.
* @param description The description.
*/
public V createVersion(String delegateId, String topologyToCloneId, String version, String description) {
if (isVersionNameExist(delegateId, version)) {
throw new AlreadyExistException("An version " + version + " already exists.");
}
VersionUtil.parseVersion(version);
V appVersion = buildVersionImplem();
appVersion.setDelegateId(delegateId);
appVersion.setVersion(version);
appVersion.setLatest(true);
appVersion.setSnapshot(VersionUtil.isSnapshot(version));
appVersion.setReleased(!VersionUtil.isSnapshot(version));
appVersion.setDescription(description);
// Every version of an application has a Cloud Service Archive
String delegateType = ArchiveDelegateType.APPLICATION.toString();
Csar csar = new Csar(delegateId, version);
csar.setWorkspace(APP_WORKSPACE_PREFIX + ":" + delegateId);
csar.setDelegateId(delegateId);
csar.setDelegateType(delegateType);
Topology topology;
if (topologyToCloneId != null) { // "cloning" the topology
topology = alienDAO.findById(Topology.class, topologyToCloneId);
} else {
topology = new Topology();
}
topology.setArchiveName(csar.getName());
topology.setArchiveVersion(csar.getVersion());
topology.setWorkspace(csar.getWorkspace());
// first of all, if the new version is a release, we have to ensure that all dependencies are released
if (!VersionUtil.isSnapshot(version)) {
checkTopologyReleasable(topology);
}
// Import the created archive and topology
archiveIndexer.importNewArchive(csar, topology);
// Save the version object
appVersion.setId(csar.getId());
alienDAO.save(appVersion);
return appVersion;
}
/**
* Check that the topology can be associated to a release version, actually : check that the topology doesn't reference SNAPSHOT
* dependencies.
*
* @throws @{@link ReleaseReferencingSnapshotException} if the topology references SNAPSHOT dependencies
* version.
*/
public void checkTopologyReleasable(Topology topology) {
if (topology.getDependencies() != null) {
for (CSARDependency dep : topology.getDependencies()) {
// we allow SNAPSHOTS only for tosca-normative-types (we don't expect to have a release soon !)
if (VersionUtil.isSnapshot(dep.getVersion()) && !dep.getName().equals("tosca-normative-types")) {
throw new ReleaseReferencingSnapshotException(String.format("Can not release: %s dependency is a snapshot", dep.getName()));
}
}
}
}
/**
* Get all versions for a given delegate.
*
* @param delegateId The id of the application for which to get environments.
* @return An array of the applications versions for the requested application id.
*/
public V[] getByDelegateId(String delegateId) {
GetMultipleDataResult<V> result = alienDAO.find(getVersionImplemClass(),
MapUtil.newHashMap(new String[] { getDelegatePropertyName() }, new String[][] { new String[] { delegateId } }), Integer.MAX_VALUE);
return result.getData();
}
/**
* Get the version from a topology id
*
* @param topologyId The id of the application for which to get environments.
* @return An array of the applications/topology templates versions for the requested topology id.
*/
public V getByTopologyId(String topologyId) {
GetMultipleDataResult<V> result = alienDAO.find(getVersionImplemClass(),
MapUtil.newHashMap(new String[] { "topologyId" }, new String[][] { new String[] { topologyId } }), Integer.MAX_VALUE);
return (result.getData() == null || result.getData().length != 1) ? null : result.getData()[0];
}
/**
* Get all versions snapshot for a given application/topology template.
*
* @param delegateId The id of the application/topology template for which to get versions.
* @return An array of the applications/topology templates versions snapshot for the requested application id.
*/
public V[] getSnapshotByDelegateId(String delegateId) {
GetMultipleDataResult<V> result = alienDAO.find(getVersionImplemClass(), MapUtil.newHashMap(new String[] { getDelegatePropertyName(), "isSnapshot" },
new String[][] { new String[] { delegateId }, new String[] { "true" } }), Integer.MAX_VALUE);
return result.getData();
}
private void deleteVersion(V version) {
// Call delete archive
csarService.deleteCsar(version.getId());
alienDAO.delete(getVersionImplemClass(), version.getId());
}
/**
* Delete a version and the related topologies. Fail if version doesn't exist.
*
* @param id The id of the version to delete.
*/
public void delete(String id) {
V version = this.getOrFail(id);
deleteVersion(version);
}
/**
* Delete all versions related to an application/topology template.
*
* @param delegateId The application/topology template id.
*/
public void deleteByDelegate(String delegateId) {
V[] versions = getByDelegateId(delegateId);
for (V version : versions) {
deleteVersion(version);
}
}
/**
* Check uniqueness of a version for a given application/topology template.
*
* @param delegateId
* @param versionName
* @return a boolean indicating if the version exists.
*/
public boolean isVersionNameExist(String delegateId, String versionName) {
GetMultipleDataResult<V> dataResult = alienDAO.search(getVersionImplemClass(), null, MapUtil.newHashMap(
new String[] { getDelegatePropertyName(), "version" }, new String[][] { new String[] { delegateId }, new String[] { versionName } }), 1);
if (dataResult.getData() != null && dataResult.getData().length > 0) {
return true;
}
return false;
}
/**
* Get an application/topology template version by id or fail if not found.
*
* @param id
* @return the version or throw an exception
*/
public V getOrFail(String id) {
V v = alienDAO.findById(getVersionImplemClass(), id);
if (v == null) {
throw new NotFoundException("Version with id <" + id + "> does not exist");
}
return v;
}
/**
* Get a version by id.
*
* @param id
* @return the version or null if not found
*/
public V get(String id) {
return alienDAO.findById(getVersionImplemClass(), id);
}
/**
* Sort the versions from newest to oldest.
*
* @param data
* @return a sorted array of versions
*/
public V[] sortArrayOfVersion(V[] data) {
if (data == null || data.length <= 1) {
return data;
}
List<V> sortedData = Lists.newArrayList(data);
Collections.sort(sortedData, new Comparator<V>() {
@Override
public int compare(V left, V right) {
return VersionUtil.compare(right.getVersion(), left.getVersion());
}
});
return sortedData.toArray(buildVersionImplemArray(data.length));
}
/**
* Filter to search app/tt versions only for an delegate id.
*
* @param delegateId
* @return a filter for application/tt versions
*/
public Map<String, String[]> getVersionsFilters(String delegateId, String version) {
List<String> filterKeys = Lists.newArrayList();
List<String[]> filterValues = Lists.newArrayList();
if (delegateId != null) {
filterKeys.add(getDelegatePropertyName());
filterValues.add(new String[] { delegateId });
}
if (version != null && !version.equals("")) {
filterKeys.add("version");
filterValues.add(new String[] { version });
}
return MapUtil.newHashMap(filterKeys.toArray(new String[filterKeys.size()]), filterValues.toArray(new String[filterValues.size()][]));
}
}