@Override
 public long enablePlatforms(Set<Long> platformIds, String userId) {
   long releaseId = 0;
   cloudUtil.check4missingServices(platformIds);
   for (long manifestPlatformId : platformIds) {
     releaseId = manifestRfcProcessor.enablePlatform(manifestPlatformId, userId);
   }
   return releaseId;
 }
 /** @param design2manifestPlatMap design manifest platform map. */
 private void check4MissingServices(Map<Long, CmsRfcCI> design2manifestPlatMap)
     throws TransistorException {
   // get CiIds to be checked for missing services
   Set<Long> manifestPlatformIds =
       design2manifestPlatMap
           .entrySet()
           .stream()
           .map(t -> t.getValue().getCiId())
           .collect(Collectors.toSet());
   cloudUtil.check4missingServices(manifestPlatformIds);
 }
  @Override
  public long updateEnvClouds(long envId, List<CmsCIRelation> cloudRels, String userId) {
    // for now we will handle just new clouds
    List<CmsCIRelation> existingCloudRels =
        cmProcessor.getFromCIRelationsNaked(envId, BASE_CONSUMES, ACCOUNT_CLOUD);
    Set<Long> existingCloudIds = new HashSet<Long>();
    for (CmsCIRelation rel : existingCloudRels) {
      existingCloudIds.add(rel.getToCiId());
    }

    boolean needUpdate = false;
    for (CmsCIRelation requestRel : cloudRels) {
      if (!existingCloudIds.contains(requestRel.getToCiId())) {
        // this is new cloud lets add env->cloud rel
        cmProcessor.createRelation(requestRel);
        needUpdate = true;
      } else {
        cmProcessor.updateRelation(requestRel);
        existingCloudIds.remove(requestRel.getToCiId());
      }
    }
    if (!existingCloudIds.isEmpty()) {
      // looks like we need to delete some clouds
      // first lets see if we have any open releases
      processCloudDeletions(envId, existingCloudIds);
    }

    if (needUpdate) {
      CmsCI env = getEnv(envId);
      String nsPath = env.getNsPath() + "/" + env.getCiName() + "/manifest";
      List<CmsRfcRelation> compOfRels =
          cmRfcMrgProcessor.getFromCIRelations(
              envId, MANIFEST_COMPOSED_OF, MANIFEST_PLATFORM, "dj");
      for (CmsRfcRelation compOfRel : compOfRels) {
        CmsRfcCI platform = compOfRel.getToRfcCi();
        String platNs = platform.getNsPath();
        manifestRfcProcessor.processClouds(env, platform, platNs, nsPath, userId, null, null, null);
        Set<String> missingSrvs = cloudUtil.getMissingServices(platform.getCiId());
        if (missingSrvs.size() > 0) {
          logger.info(
              ">>>>> Not all services available for platform: "
                  + platform.getCiName()
                  + ", the missing services: "
                  + missingSrvs.toString());
          manifestRfcProcessor.disablePlatform(platform.getCiId(), userId);
        }
        logger.info("Done working on platform " + platform.getCiName());
      }
      return populateParentRelease(env, nsPath);
    } else {
      return 0;
    }
  }
  @Override
  public long generateEnvManifest(long envId, String userId, Map<String, String> platModes) {
    long t1 = System.currentTimeMillis();
    String oldThreadName = Thread.currentThread().getName();
    Thread.currentThread().setName(getProcessingThreadName(oldThreadName, envId));
    List<CmsCIRelation> assemblyRels =
        cmProcessor.getToCIRelations(envId, BASE_REALIZED_IN, null, ACCOUNT_ASSEMBLY);
    CmsCI assembly = null;
    if (assemblyRels.size() > 0) {
      assembly = assemblyRels.get(0).getFromCi();
    } else {
      String error = "Can not get assembly for envId = " + envId;
      logger.error(error);
      throw new TransistorException(CmsError.TRANSISTOR_CANNOT_GET_ASSEMBLY, error);
    }

    CmsCI env = getEnv(envId);

    String nsPath = env.getNsPath() + "/" + env.getCiName() + "/manifest";

    if (hasOpenManifestRelease(nsPath)) {
      String message =
          "This environment has an open release. It needs to be discarded or committed before the design pull: "
              + env.getNsPath()
              + "/"
              + env.getCiName();
      logger.info(message);
      throw new TransistorException(CmsError.TRANSISTOR_OPEN_MANIFEST_RELEASE, message);
    }
    Long nsId = trUtil.verifyAndCreateNS(nsPath);
    logger.info("Created nsId " + nsId);
    // Long releaseId = createManifestRelease(nsPath,userId);

    List<CmsCIRelation> designPlatRels =
        cmProcessor.getFromCIRelations(assembly.getCiId(), null, "ComposedOf", CATALOG_PLATFORM);

    // we need to reset all pending deletions cis just in case there was one added back
    cmProcessor.resetDeletionsByNs(nsPath);

    // check for edge case scenario when there is new design platform with the same name as old one
    // but different pack
    long releaseId = checkPlatformPackCompliance(designPlatRels, env, nsPath, userId);
    if (releaseId > 0) {
      // stop any processing and return new release id
      return releaseId;
    }

    final CountDownLatch latch = new CountDownLatch(designPlatRels.size());
    List<Future<DesignCIManifestRfcTouple>> submittedFutureTasks =
        new ArrayList<Future<DesignCIManifestRfcTouple>>();

    Map<Long, CmsRfcCI> design2manifestPlatMap = new HashMap<Long, CmsRfcCI>();
    for (CmsCIRelation platRelation : designPlatRels) {
      String availMode = null;
      if (platModes != null) {
        availMode = platModes.get(String.valueOf(platRelation.getToCiId()));
        if (availMode != null && availMode.length() == 0) {
          availMode = "default";
        }
      }

      Future<DesignCIManifestRfcTouple> future =
          executorService.submit(
              new ManifestRfcProcessorTask(env, nsPath, userId, availMode, latch, platRelation));
      submittedFutureTasks.add(future);
    }

    boolean allPlatsProcessed = false;
    try {
      // latch.await(); //wait till all platform processing threads return
      allPlatsProcessed =
          latch.await(
              timeoutInMilliSeconds,
              TimeUnit
                  .MILLISECONDS); // wait for all platform processing threads to finish with timeout
                                  // of 10 mins
      if (!allPlatsProcessed) {
        logger.error(
            "All platforms not processed within timeout duration of " + timeoutInMilliSeconds);
        throw new TransistorException(
            CmsError.TRANSISTOR_OPEN_MANIFEST_RELEASE,
            "Failed to pull latest design for all platform within timeout duration of "
                + timeoutInMilliSeconds
                + " millis");
      }
    } catch (InterruptedException ie) {
      for (Future<DesignCIManifestRfcTouple> job : submittedFutureTasks) {
        job.cancel(true);
      }
      throw new TransistorException(
          CmsError.TRANSISTOR_OPEN_MANIFEST_RELEASE, "Design pull process interrupted. ");
    }

    for (Future<DesignCIManifestRfcTouple> task : submittedFutureTasks) {

      DesignCIManifestRfcTouple touple;
      try {
        touple = task.get();
        processPlatformRfcs(touple.manifestPlatformRfcs, userId);

        CmsRfcCI manifestPlatformRfc = touple.manifestPlatformRfcs.getManifestPlatformRfc();
        Set<String> missingSrvs = cloudUtil.getMissingServices(manifestPlatformRfc.getCiId());
        if (missingSrvs.size() > 0) {
          logger.info(
              ">>>>> Not all services available for platform: "
                  + manifestPlatformRfc.getCiName()
                  + ", the missing services: "
                  + missingSrvs.toString());
          disablePlatform(manifestPlatformRfc.getCiId(), userId);
        }
        logger.info("New release id = " + manifestPlatformRfc.getReleaseId());
        logger.info("Done working on platform " + manifestPlatformRfc.getNsPath());

        design2manifestPlatMap.put(touple.designPlatCI, manifestPlatformRfc);
      } catch (Exception e) {
        logger.error("Error in pulling latest design for all platforms ", e);
        throw new TransistorException(
            CmsError.TRANSISTOR_OPEN_MANIFEST_RELEASE,
            "Error in pulling latest design for all platforms ");
      }
    }

    // now we need to process linkedTo relations
    manifestRfcProcessor.processLinkedTo(design2manifestPlatMap, nsPath, userId);

    // now lets delete old existing plats that do not exists in new manifest
    manifestRfcProcessor.processDeletedPlatforms(
        design2manifestPlatMap.values(), env, nsPath, userId);

    // process global variables from design
    manifestRfcProcessor.processGlobalVars(assembly.getCiId(), env, nsPath, userId);
    long t2 = System.currentTimeMillis();
    long envReleaseId = populateParentRelease(env, nsPath);
    logger.info(
        "Pull design for  "
            + nsPath
            + " completed in  "
            + (t2 - t1)
            + " millis (releaseId "
            + envReleaseId
            + ")");
    return envReleaseId;
  }