/**
   * {@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);
    }
  }
  private ProductInstance createProductInstance(
      ProductRelease productRelease, VM vm, String vdc, List<Attribute> attributes)
      throws InvalidEntityException, AlreadyExistsEntityException {

    ProductInstance instance = new ProductInstance();

    Product product = null;
    try {
      product = productDao.load(productRelease.getProduct().getName());
    } catch (EntityNotFoundException e) {
      product =
          new Product(
              productRelease.getProduct().getName(), productRelease.getProduct().getDescription());
    }
    product.setAttributes(attributes);

    productRelease.setProduct(product);

    instance.setProductRelease(productRelease);
    instance.setVm(vm);
    instance.setVdc(vdc);
    instance.setStatus(Status.UNINSTALLED);
    instance.setName(
        vm.getFqn()
            + "_"
            + productRelease.getProduct().getName()
            + "_"
            + productRelease.getVersion());

    instance = productInstanceDao.create(instance);
    return instance;
  }
  @Override
  public ProductInstance install(
      VM vm, String vdc, ProductRelease productRelease, List<Attribute> attributes)
      throws NodeExecutionException, AlreadyInstalledException,
          InvalidInstallProductRequestException, EntityNotFoundException {

    // if (INSTALATOR_CHEF.equals(product.getMapMetadata().get("installator"))) {
    //
    // }else{
    //
    // }

    // Check that there is not another product installed
    ProductInstance instance = null;
    try {

      instance =
          productInstanceDao.load(
              vm.getFqn()
                  + "_"
                  + productRelease.getProduct().getName()
                  + "_"
                  + productRelease.getVersion());

      System.out.println("intance:" + instance.getStatus());

      if (instance.getStatus().equals(Status.INSTALLED)) {
        throw new AlreadyInstalledException(instance);
      } else if (!(instance.getStatus().equals(Status.UNINSTALLED))
          && !(instance.getStatus().equals(Status.ERROR)))
        throw new InvalidInstallProductRequestException(
            "Product "
                + productRelease.getProduct().getName()
                + " "
                + productRelease.getVersion()
                + " cannot be installed in the VM "
                + vm.getFqn()
                + " strage status:  "
                + instance.getStatus());
    } catch (EntityNotFoundException e) {
      try {
        instance = createProductInstance(productRelease, vm, vdc, attributes);
      } catch (Exception e2) {
        throw new InvalidInstallProductRequestException(
            "Product "
                + productRelease.getProduct().getName()
                + " "
                + productRelease.getVersion()
                + " cannot be installed in the VM "
                + vm.getFqn()
                + " error in creating the isntance:  "
                + e2.getMessage());
      }
    }

    Status previousStatus = null;

    try {
      // makes the validations
      // instance = getProductToInstall(product, vm, vdc, attributes);
      previousStatus = instance.getStatus();
      // now we have the productInstance so can validate the operation
      validator.validateInstall(instance);

      instance.setStatus(Status.INSTALLING);
      instance.setVm(vm);
      // Id for the ProductInstance
      instance = productInstanceDao.update(instance);

      Product product = productDao.load(productRelease.getProduct().getName());

      if (INSTALATOR_CHEF.equals(product.getMapMetadata().get("installator"))) {
        chefInstallator.validateInstalatorData(vm);
        chefInstallator.callService(instance, vm, attributes, INSTALL);
      } else {
        if (INSTALATOR_PUPPET.equals(product.getMapMetadata().get("installator"))) {
          puppetInstallator.validateInstalatorData(vm);
          puppetInstallator.callService(vm, vdc, productRelease, INSTALL);
        } else {
          chefInstallator.validateInstalatorData(vm);
          chefInstallator.callService(instance, vm, attributes, INSTALL);
        }
      }

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

    } catch (InstallatorException sce) {
      restoreInstance(previousStatus, instance);
      throw new SdcRuntimeException(sce);
    } catch (RuntimeException e) {
      // by default restore the previous state when a runtime is thrown
      restoreInstance(previousStatus, instance);
      throw new SdcRuntimeException(e);
    }
  }
 /**
  * Go to previous state when a runtime exception is thrown in any method which can change the
  * status of the product instance.
  *
  * @param previousStatus the previous status
  * @param instance the product instance
  * @return the instance.
  */
 private ProductInstance restoreInstance(Status previousStatus, ProductInstance instance) {
   instance.setStatus(previousStatus);
   return update(instance);
 }
  /**
   * {@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);
    }
  }