@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;
  }
  /**
   * @param manifestPlatformRfcs
   * @param userId
   */
  private void processPlatformRfcs(ManifestRfcContainer manifestPlatformRfcs, String userId) {

    long t1 = System.currentTimeMillis();
    /** *** Handle root RFC and relations ***** */
    CmsRfcCI rootRfc = null;
    if (manifestPlatformRfcs.getRootRfcRelTouple().getRfcCI() != null) {
      rootRfc =
          rfcProcessor.createAndfetchRfcCINoCheck(
              manifestPlatformRfcs.getRootRfcRelTouple().getRfcCI(), userId);
      if (rootRfc.getCiState() == null) {
        rootRfc.setCiState("default");
      }
    } else {
      rootRfc = manifestPlatformRfcs.getManifestPlatformRfc();
    }

    for (CmsRfcRelation toRfcRelation :
        manifestPlatformRfcs.getRootRfcRelTouple().getToRfcRelation()) {
      toRfcRelation.setToCiId(rootRfc.getCiId());
      rfcProcessor.createRfcRelationNoCheck(toRfcRelation, userId);
    }

    for (CmsRfcRelation fromRfcRelation :
        manifestPlatformRfcs.getRootRfcRelTouple().getFromRfcRelation()) {
      fromRfcRelation.setFromCiId(rootRfc.getCiId());
      rfcProcessor.createRfcRelationNoCheck(fromRfcRelation, userId);
    }

    /** Handle DependsOn and other pack relations ** */
    for (ManifestRfcRelationTriplet rfcRelTriplet : manifestPlatformRfcs.getRfcRelTripletList()) {

      CmsRfcRelation rfcRelation = rfcRelTriplet.getRfcRelation();
      if (rfcRelation.getRfcAction() == null) {
        rfcRelation.setRfcAction("add");
      }

      CmsRfcCI toRfcCI = rfcRelTriplet.getToRfcCI();
      if (toRfcCI != null) {
        if (toRfcCI.getRfcAction() == null) {
          toRfcCI.setRfcAction("add");
        }
        manifestRfcProcessor.setCiId(toRfcCI);
        if (toRfcCI.getRfcId() == 0 && toRfcCI.getCiId() == 0) {
          toRfcCI = rfcProcessor.createAndfetchRfcCINoCheck(toRfcCI, userId);
        }
        rfcRelation.setToCiId(toRfcCI.getCiId());
      }

      CmsRfcCI fromRfcCI = rfcRelTriplet.getFromRfcCI();
      if (fromRfcCI != null) {
        if (fromRfcCI.getRfcAction() == null) {
          fromRfcCI.setRfcAction("add");
        }
        manifestRfcProcessor.setCiId(fromRfcCI);
        if (fromRfcCI.getRfcId() == 0 && fromRfcCI.getCiId() == 0) {
          fromRfcCI = rfcProcessor.createAndfetchRfcCINoCheck(fromRfcCI, userId);
        }
        rfcRelation.setFromCiId(fromRfcCI.getCiId());
      }

      if ("manifest.Entrypoint".equals(rfcRelation.getRelationName())) {
        rfcRelation.setFromCiId(rootRfc.getCiId());
      }

      manifestRfcProcessor.setCiRelationId(rfcRelation);
      rfcProcessor.createRfcRelationNoCheck(rfcRelation, userId);
    }

    /** Handle Requires relations ** */
    for (ManifestRootRfcContainer rfcRelTouple : manifestPlatformRfcs.getRfcRelToupleList()) {
      CmsRfcCI newRfc;
      if (rfcRelTouple.getRfcCI().getRfcId() == 0) {
        newRfc = rfcProcessor.createAndfetchRfcCINoCheck(rfcRelTouple.getRfcCI(), userId);
      } else {
        newRfc = rfcRelTouple.getRfcCI();
      }
      for (CmsRfcRelation rfcRel : rfcRelTouple.getToRfcRelation()) {
        if (rfcRel.getRfcAction() == null) {
          rfcRel.setRfcAction("add");
        }

        if ("manifest.Requires".equals(rfcRel.getRelationName()) && rfcRel.getFromCiId() == 0) {
          rfcRel.setFromCiId(rootRfc.getCiId());
        }
        rfcRel.setToCiId(newRfc.getCiId());
        rfcProcessor.createRfcRelationNoCheck(rfcRel, userId);
      }
      for (CmsRfcRelation rfcRel : rfcRelTouple.getFromRfcRelation()) {
        if (rfcRel.getRfcAction() == null) {
          rfcRel.setRfcAction("add");
        }

        if (rfcRel.getToCiId() == 0) {
          rfcRel.setToCiId(rootRfc.getCiId());
        }
        rfcRel.setFromCiId(newRfc.getCiId());
        rfcProcessor.createRfcRelationNoCheck(rfcRel, userId);
      }
    }

    for (CmsRfcCI rfc : manifestPlatformRfcs.getRfcList()) {
      rfcProcessor.createRfcCINoCheck(rfc, userId);
    }

    for (CmsRfcRelation rfcRelation : manifestPlatformRfcs.getRfcRelationList()) {
      if (rfcRelation.getFromCiId() == 0) {
        if ("base.Consumes".equals(rfcRelation.getRelationName())) {
          rfcRelation.setFromCiId(rootRfc.getCiId());
        } else {
          rfcRelation.setFromCiId(rootRfc.getCiId());
        }
      }
      if (rfcRelation.getRfcAction() == null) {
        rfcRelation.setRfcAction("add");
      }

      rfcProcessor.createRfcRelationNoCheck(rfcRelation, userId);
    }

    for (Long delCiId : manifestPlatformRfcs.getDeleteCiIdList()) {
      cmRfcMrgProcessor.requestCiDeleteCascadeNoRelsRfcs(delCiId, userId, 0);
    }

    for (CmsRfcRelation delRelation : manifestPlatformRfcs.getRfcDeleteRelationList()) {
      cmRfcMrgProcessor.requestRelationDelete(delRelation.getCiRelationId(), userId);
    }
    long t2 = System.currentTimeMillis();
    logger.info(
        " processPlatformRfcs  "
            + manifestPlatformRfcs.getManifestPlatformRfc().getNsPath()
            + " completed in  "
            + (t2 - t1));
  }