package alien4cloud.tosca.container;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.alien4cloud.tosca.catalog.index.ICsarDependencyLoader;
import org.alien4cloud.tosca.model.CSARDependency;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import alien4cloud.utils.VersionUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@Getter
@Setter
@Slf4j
public class ToscaTypeLoader {
/** Map of type names per archives. */
private Map<CSARDependency, Set<String>> dependenciesMap = Maps.newHashMap();
/** Count the usage of a given type in the current context. */
private Map<String, Integer> typeUsagesMap = Maps.newHashMap();
private ICsarDependencyLoader csarDependencyLoader;
public ToscaTypeLoader(ICsarDependencyLoader csarDependencyLoader) {
this.csarDependencyLoader = csarDependencyLoader;
}
public Set<CSARDependency> getLoadedDependencies() {
return Sets.newHashSet(dependenciesMap.keySet());
}
/**
* Try to unload the given type
*
* @param type name of the type
*/
public void unloadType(String type) {
if (log.isDebugEnabled()) {
log.debug("Unload type [" + type + "]");
}
Integer currentUsageCount = typeUsagesMap.get(type);
if (currentUsageCount != null) {
if (currentUsageCount <= 1) {
// Not used anymore in the topology
typeUsagesMap.remove(type);
Iterator<Map.Entry<CSARDependency, Set<String>>> dependencyIterator = dependenciesMap.entrySet().iterator();
while (dependencyIterator.hasNext()) {
Map.Entry<CSARDependency, Set<String>> entry = dependencyIterator.next();
entry.getValue().remove(type);
if (entry.getValue().isEmpty()) {
if (log.isDebugEnabled()) {
log.debug("Dependency is not used anymore and will be removed [" + entry.getKey() + "] due to unload of type [" + type + "]");
log.debug("Type usage [" + typeUsagesMap + "]");
log.debug("Dependencies usage [" + dependenciesMap + "]");
}
dependencyIterator.remove();
}
}
} else {
// Still used
typeUsagesMap.put(type, currentUsageCount - 1);
}
}
if (log.isDebugEnabled()) {
log.debug("Type usage [" + typeUsagesMap + "]");
log.debug("Dependencies usage [" + dependenciesMap + "]");
}
}
private CSARDependency getDependencyWithName(String archiveName) {
Iterator<Map.Entry<CSARDependency, Set<String>>> dependencyIterator = dependenciesMap.entrySet().iterator();
while (dependencyIterator.hasNext()) {
Map.Entry<CSARDependency, Set<String>> entry = dependencyIterator.next();
if (entry.getKey().getName().equals(archiveName)) {
return entry.getKey();
}
}
return null;
}
private void addNewDependency(CSARDependency dependency, String type) {
CSARDependency currentDependency = getDependencyWithName(dependency.getName());
// New dependency that never exists before
if (currentDependency == null) {
dependenciesMap.put(dependency, Sets.newHashSet(type));
return;
}
// Dependency that already existed
// FIXME clean this, as we do not allow dependencies versions override anymore
if (VersionUtil.compare(dependency.getVersion(), currentDependency.getVersion()) > 0) {
// The new version is more recent, we will override with new version with warning
Set<String> typesLoadedByConflictingArchive = dependenciesMap.remove(currentDependency);
typesLoadedByConflictingArchive.add(type);
dependenciesMap.put(dependency, typesLoadedByConflictingArchive);
log.warn("Version conflicting for archive [" + dependency.getName() + "] override current version [" + currentDependency.getVersion() + "] with ["
+ dependency.getVersion() + "]");
} else {
log.warn("Version conflicting for archive [" + dependency.getName() + "] do not override and use current version [" + currentDependency.getVersion()
+ "] ignore old version [" + dependency.getVersion() + "]");
dependenciesMap.get(currentDependency).add(type);
}
}
/**
* Add directDependency for the given type from the given archive.
* If the directDependency is of an deprecated version ( < than found in the existing dependencies), ignore it.
* If the directDependency is of a more recent version, force the dependency of the topology to the more recent one
*
* @param directDependency the direct directDependency to load the type
* @param type name of the type
*/
public void loadType(String type, CSARDependency directDependency) {
if (log.isDebugEnabled()) {
log.debug("Load type [" + type + "] from dependency [" + directDependency + "]");
}
Set<String> typesLoadedByDependency = dependenciesMap.get(directDependency);
// Increment usage count
Integer currentUsageCount = typeUsagesMap.get(type);
if (currentUsageCount == null) {
currentUsageCount = Integer.valueOf(0);
}
typeUsagesMap.put(type, currentUsageCount + 1);
if (typesLoadedByDependency != null) {
typesLoadedByDependency.add(type);
// make sure we replace the key because the Equals on CSARDependency is only based on the name and the version
dependenciesMap.remove(directDependency);
dependenciesMap.put(directDependency, typesLoadedByDependency);
} else {
addNewDependency(directDependency, type);
}
Set<CSARDependency> transitiveDependencies = csarDependencyLoader.getDependencies(directDependency.getName(), directDependency.getVersion());
for (CSARDependency transitiveDependency : transitiveDependencies) {
Set<String> transitiveTypesLoadedByDependency = dependenciesMap.get(transitiveDependency);
if (transitiveTypesLoadedByDependency == null) {
addNewDependency(csarDependencyLoader.buildDependencyBean(transitiveDependency.getName(), transitiveDependency.getVersion()), type);
} else {
transitiveTypesLoadedByDependency.add(type);
}
}
if (log.isDebugEnabled()) {
log.debug("Type usage [" + typeUsagesMap + "]");
log.debug("Dependencies usage [" + dependenciesMap + "]");
}
}
}