package alien4cloud.component;
import java.util.Date;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.stereotype.Component;
import alien4cloud.Constants;
import alien4cloud.dao.IGenericSearchDAO;
import alien4cloud.dao.model.GetMultipleDataResult;
import org.alien4cloud.tosca.model.types.NodeType;
import org.alien4cloud.tosca.model.templates.Topology;
import alien4cloud.utils.MapUtil;
import alien4cloud.utils.version.Version;
import com.google.common.collect.Maps;
/**
* Updates the scoring of node types based on their usage, version and default capabilities.
*/
@Slf4j
@Component
public class NodeTypeScoreService implements Runnable {
@Resource(name = "alien-es-dao")
private IGenericSearchDAO alienESDAO;
@Resource(name = "node-type-score-scheduler")
private TaskScheduler scheduler;
@Value("${components.search.boost.frequency}")
private long frequencyH = 1;
@Value("${components.search.boost.usage}")
private long usageBoost;
@Value("${components.search.boost.version}")
private long versionBoost;
@Value("${components.search.boost.default}")
private long defaultBoost;
/** Refresh boost for all indexed node types in the system. */
@PostConstruct
public void refreshBoostCompute() {
long frequencyMs = frequencyH * 1000 * 60 * 60;
Date date = new Date(System.currentTimeMillis() + frequencyMs);
log.info("Type score is scheduled with {} ms frequency", frequencyMs);
scheduler.scheduleAtFixedRate(this, date, frequencyMs);
}
@Override
public void run() {
log.info("Updating node type scores.");
// Go over all indexed node types.
GetMultipleDataResult<NodeType> getMultipleDataResult = alienESDAO.find(NodeType.class, null, 0, Constants.DEFAULT_ES_SEARCH_SIZE);
int from = getMultipleDataResult.getTo() + 1;
processNodeTypes(getMultipleDataResult.getData());
while (getMultipleDataResult.getData().length > 0 && from < getMultipleDataResult.getTotalResults()) {
getMultipleDataResult = alienESDAO.find(NodeType.class, null, from, from + Constants.DEFAULT_ES_SEARCH_SIZE);
from = getMultipleDataResult.getTo() + 1;
processNodeTypes(getMultipleDataResult.getData());
}
}
private void processNodeTypes(NodeType[] indexedNodeTypes) {
for (NodeType nodeType : indexedNodeTypes) {
if (log.isDebugEnabled()) {
log.debug("Processing node score for type {}", nodeType.getId());
}
Map<String, String[]> usedNodeFiler = Maps.newHashMap();
usedNodeFiler.put("nodeTemplates.value.type", new String[] { nodeType.getElementId() });
// count the applications that uses the node-type
long usageFactor = usageBoost * alienESDAO.count(Topology.class, null, usedNodeFiler);
// get the version factor (latest version of a node is better than previous version, snapshot versions do not get boost)
long versionFactor = isLatestVersion(nodeType) ? versionBoost : 0;
// default boost (boost node types that have a default capability)
long defaultFactor = nodeType.getDefaultCapabilities() == null || nodeType.getDefaultCapabilities().isEmpty() ? 0 : defaultBoost;
// update the score for the node type.
nodeType.setAlienScore(usageFactor + defaultFactor + versionFactor);
alienESDAO.save(nodeType);
}
}
private boolean isLatestVersion(NodeType nodeType) {
Map<String, String[]> filters = MapUtil.newHashMap(new String[] { "elementId" }, new String[][] { new String[] { nodeType.getElementId() } });
// TODO get a single element and order by version.
Version nodeVersion = new Version(((NodeType) nodeType).getArchiveVersion());
for (Object otherVersionNodeType : alienESDAO.find(NodeType.class, filters, Constants.DEFAULT_ES_SEARCH_SIZE).getData()) {
Version otherVersion = new Version(((NodeType) otherVersionNodeType).getArchiveVersion());
if (nodeVersion.compareTo(otherVersion) < 0) {
return false;
}
}
return true;
}
}