content: package alien4cloud.component;
  
  import alien4cloud.dao.IGenericSearchDAO;
  import alien4cloud.dao.model.GetMultipleDataResult;
  import alien4cloud.utils.AlienConstants;
  import alien4cloud.utils.MapUtil;
  import alien4cloud.utils.version.Version;
  import com.google.common.collect.Maps;
  import lombok.extern.slf4j.Slf4j;
  import org.alien4cloud.tosca.model.templates.Topology;
  import org.alien4cloud.tosca.model.types.NodeType;
  import org.springframework.beans.factory.annotation.Value;
  import org.springframework.core.env.Environment;
  import org.springframework.scheduling.TaskScheduler;
  import org.springframework.stereotype.Component;
  
  import javax.annotation.PostConstruct;
  import javax.annotation.Resource;
  import java.util.Date;
  import java.util.Map;
  
  import static alien4cloud.dao.FilterUtil.fromKeyValueCouples;
  
  /**
   * 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;
      @Resource
      private Environment environment;
  
      @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);
          if (environment.acceptsProfiles("security-demo")) {
              scheduler.schedule(this, new Date());
          }
      }
  
      @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, AlienConstants.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 + AlienConstants.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.buildQuery(Topology.class).setFilters(fromKeyValueCouples("nodeTemplates.value.type",
                      nodeType.getElementId(), "dependencies.name", nodeType.getArchiveName(), "dependencies.version", nodeType.getArchiveVersion())).count();
              // 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, AlienConstants.DEFAULT_ES_SEARCH_SIZE).getData()) {
              Version otherVersion = new Version(((NodeType) otherVersionNodeType).getArchiveVersion());
              if (nodeVersion.compareTo(otherVersion) < 0) {
                  return false;
              }
          }
          return true;
      }
  }
  