/**
   * {@inheritDoc}
   *
   * @throws EntityNotFoundException
   */
  @UseCase(traceTo = "UC_001.4", status = "implemented")
  @Override
  public ProductInstance upgrade(ProductInstance productInstance, ProductRelease productRelease)
      throws NotTransitableException, NodeExecutionException, FSMViolationException,
          InstallatorException, EntityNotFoundException {
    Status previousStatus = productInstance.getStatus();
    try {
      validator.validateUpdate(productInstance, productRelease);
      // update the status
      productInstance.setStatus(Status.UPGRADING);
      productInstance = productInstanceDao.update(productInstance);
      productInstance.setProductRelease(productRelease);

      VM vm = productInstance.getVm();
      Product product = productDao.load(productInstance.getName());

      if (INSTALATOR_PUPPET.equals(product.getMapMetadata().get("installator"))) {
        throw new InstallatorException("Product not upgradeable in Puppet");
      } else {
        chefInstallator.upgrade(productInstance, vm);
      }

      productInstance.setStatus(Status.INSTALLED);
      return productInstanceDao.update(productInstance);

    } catch (RuntimeException e) { // by runtime restore the previous state
      // restore the status
      restoreInstance(previousStatus, productInstance);
      throw new SdcRuntimeException(e);
    }
  }
  @Override
  public void uninstall(ProductInstance productInstance)
      throws NodeExecutionException, FSMViolationException, EntityNotFoundException {
    Status previousStatus = productInstance.getStatus();
    try {
      validator.validateUninstall(productInstance);
      productInstance.setStatus(Status.UNINSTALLING);
      productInstance = productInstanceDao.update(productInstance);

      Product product = productDao.load(productInstance.getProductRelease().getProduct().getName());

      if (INSTALATOR_PUPPET.equals(product.getMapMetadata().get("installator"))) {
        puppetInstallator.callService(
            productInstance.getVm(),
            productInstance.getVdc(),
            productInstance.getProductRelease(),
            UNINSTALL);
      } else {
        chefInstallator.callService(productInstance, UNINSTALL);
      }

      productInstance.setStatus(Status.UNINSTALLED);
      productInstanceDao.update(productInstance);
    } catch (InstallatorException e) {
      restoreInstance(previousStatus, productInstance);
      throw new SdcRuntimeException(e);
    } catch (RuntimeException e) {
      // by default restore the previous state when a runtime is thrown
      restoreInstance(previousStatus, productInstance);
      throw new SdcRuntimeException(e);
    }
  }
  @Override
  public void callService(ProductInstance productInstance, String action, String token)
      throws InstallatorException, NodeExecutionException {
    VM vm = productInstance.getVm();
    String recipe = "";
    if ("uninstall".equals(action)) {
      recipe = recipeNamingGenerator.getUninstallRecipe(productInstance);
    } else {

    }

    assignRecipes(vm, recipe, token);
    try {
      executeRecipes(vm);
      // unassignRecipes(vm, recipe);
    } catch (NodeExecutionException e) {
      // unassignRecipes(vm, recipe);
      // even if execution fails want to unassign the recipe
      throw new NodeExecutionException(e.getMessage());
    }
  }
  /**
   * {@inheritDoc}
   *
   * @throws InstallatorException
   */
  @Override
  public ProductInstance configure(ProductInstance productInstance, List<Attribute> configuration)
      throws NodeExecutionException, FSMViolationException, InstallatorException {
    System.out.println(
        "Configuring product instance " + productInstance.getName() + " " + configuration);
    Status previousStatus = productInstance.getStatus();
    try {
      validator.validateConfigure(productInstance);
      productInstance.setStatus(Status.CONFIGURING);
      productInstance = productInstanceDao.update(productInstance);

      System.out.println("Get VM");
      VM vm = productInstance.getVm();

      System.out.println(
          "Load product " + productInstance.getProductRelease().getProduct().getName());
      Product product = productDao.load(productInstance.getProductRelease().getProduct().getName());

      if (configuration != null) {
        for (int j = 0; j < configuration.size(); j++) {
          Attribute attribute = configuration.get(j);
          product.addAttribute(attribute);
        }
      }
      System.out.println(
          "Update product " + productInstance.getProductRelease().getProduct().getName());
      productDao.update(product);

      ProductRelease productRelease = productInstance.getProductRelease();
      productRelease.setProduct(product);

      if (INSTALATOR_PUPPET.equals(product.getMapMetadata().get("installator"))) {
        throw new InstallatorException("Product not configurable in Puppet");
      } else {
        chefInstallator.callService(
            productInstance, productInstance.getVm(), configuration, CONFIGURE);
      }

      /*
       * String recipe = recipeNamingGenerator .getInstallRecipe(productInstance); callChef(
       * productInstance.getProductRelease().getProduct().getName(), recipe, productInstance.getVm(),
       * configuration); String restoreRecipe = recipeNamingGenerator .getRestoreRecipe(productInstance);
       * callChef(restoreRecipe, vm);
       */

      productInstance.setProductRelease(productRelease);
      productInstance.setStatus(Status.INSTALLED);
      return productInstanceDao.update(productInstance);

    } catch (InstallatorException e) {
      restoreInstance(previousStatus, productInstance);
      throw new SdcRuntimeException(e);
    } catch (RuntimeException e) { // by runtime restore the previous state
      // restore the status
      restoreInstance(previousStatus, productInstance);
      throw new SdcRuntimeException(e);
    } catch (NodeExecutionException e) {
      restoreInstance(Status.ERROR, productInstance);
      throw e;
    } catch (EntityNotFoundException e) {
      throw new SdcRuntimeException(e);
    }
  }