/**
   * Create a rollback patch based on the recorded actions.
   *
   * @param patchId the new patch id, depending on release or one-off
   * @param patchType the current patch identity
   * @return the rollback patch
   */
  protected RollbackPatch createRollbackPatch(
      final String patchId, final Patch.PatchType patchType) {
    // Process elements
    final List<PatchElement> elements = new ArrayList<PatchElement>();
    // Process layers
    for (final PatchEntry entry : getLayers()) {
      final PatchElement element = createRollbackElement(entry);
      elements.add(element);
    }
    // Process add-ons
    for (final PatchEntry entry : getAddOns()) {
      final PatchElement element = createRollbackElement(entry);
      elements.add(element);
    }

    final InstalledIdentity installedIdentity = modification.getUnmodifiedInstallationState();
    final String name = installedIdentity.getIdentity().getName();
    final IdentityImpl identity = new IdentityImpl(name, modification.getVersion());
    if (patchType == Patch.PatchType.CUMULATIVE) {
      identity.setPatchType(Patch.PatchType.CUMULATIVE);
      identity.setResultingVersion(installedIdentity.getIdentity().getVersion());
    } else if (patchType == Patch.PatchType.ONE_OFF) {
      identity.setPatchType(Patch.PatchType.ONE_OFF);
    }
    final List<ContentModification> modifications = identityEntry.rollbackActions;
    final Patch delegate =
        new PatchImpl(patchId, "rollback patch", identity, elements, modifications);
    return new PatchImpl.RollbackPatchImpl(delegate, installedIdentity);
  }
 /**
  * Complete the current operation and persist the current state to the disk. This will also
  * trigger the invalidation of outdated modules.
  *
  * @param modification the current modification
  * @param callback the completion callback
  */
 private void complete(
     final InstallationManager.InstallationModification modification,
     final FinalizeCallback callback) {
   final List<File> processed = new ArrayList<File>();
   try {
     try {
       // Update the state to invalidate and process module resources
       if (stateUpdater.compareAndSet(this, State.PREPARED, State.INVALIDATE)
           && mode == PatchingTaskContext.Mode.APPLY) {
         // Only invalidate modules when applying patches; on rollback files are immediately
         // restored
         for (final File invalidation : moduleInvalidations) {
           processed.add(invalidation);
           PatchModuleInvalidationUtils.processFile(invalidation, mode);
         }
       }
       modification.complete();
       callback.completed(this);
       state = State.COMPLETED;
     } catch (Exception e) {
       this.moduleInvalidations.clear();
       this.moduleInvalidations.addAll(processed);
       throw new RuntimeException(e);
     }
   } finally {
     if (state != State.COMPLETED) {
       try {
         modification.cancel();
       } finally {
         try {
           undoChanges();
         } finally {
           callback.operationCancelled(this);
         }
       }
     } else {
       try {
         if (checkForGarbageOnRestart) {
           final File cleanupMarker =
               new File(installedImage.getInstallationMetadata(), "cleanup-patching-dirs");
           cleanupMarker.createNewFile();
         }
       } catch (IOException e) {
         PatchLogger.ROOT_LOGGER.infof(e, "failed to create cleanup marker");
       }
     }
   }
 }
  IdentityPatchContext(
      final File backup,
      final PatchContentProvider contentProvider,
      final ContentVerificationPolicy contentPolicy,
      final InstallationManager.InstallationModification modification,
      final PatchingTaskContext.Mode mode,
      final InstalledImage installedImage) {

    this.miscTargetRoot = installedImage.getJbossHome();

    this.mode = mode;
    this.contentProvider = contentProvider;
    this.contentPolicy = contentPolicy;
    this.modification = modification;
    this.installedImage = installedImage;
    this.history =
        PatchingHistory.Factory.getHistory(modification.getUnmodifiedInstallationState());

    if (backup != null) {
      this.miscBackup = new File(backup, PatchContentLoader.MISC);
      this.configBackup = new File(backup, Constants.CONFIGURATION);
    } else {
      this.miscBackup = null; // This will trigger a failure when the root is actually needed
      this.configBackup = null;
    }
    this.identityEntry = new PatchEntry(modification, null);
  }
  /**
   * Get the target entry for a given patch element.
   *
   * @param element the patch element
   * @return the patch entry
   * @throws PatchingException
   */
  protected PatchEntry resolveForElement(final PatchElement element) throws PatchingException {
    assert state == State.NEW;
    final PatchElementProvider provider = element.getProvider();
    final String layerName = provider.getName();
    final LayerType layerType = provider.getLayerType();

    final Map<String, PatchEntry> map;
    if (layerType == LayerType.Layer) {
      map = layers;
    } else {
      map = addOns;
    }
    PatchEntry entry = map.get(layerName);
    if (entry == null) {
      final InstallationManager.MutablePatchingTarget target =
          modification.resolve(layerName, layerType);
      if (target == null) {
        throw PatchMessages.MESSAGES.noSuchLayer(layerName);
      }
      entry = new PatchEntry(target, element);
      map.put(layerName, entry);
    }
    // Maintain the most recent element
    entry.updateElement(element);
    return entry;
  }
  /**
   * Finalize the patch.
   *
   * @param callback the finalize callback
   * @return the result
   * @throws Exception
   */
  protected PatchingResult finalize(final FinalizeCallback callback) throws Exception {
    assert state == State.NEW;
    final Patch original = callback.getPatch();
    final Patch.PatchType patchType = original.getIdentity().getPatchType();
    final String patchId;
    if (patchType == Patch.PatchType.CUMULATIVE) {
      patchId = modification.getCumulativePatchID();
    } else {
      patchId = original.getPatchId();
    }
    try {
      // The processed patch, based on the recorded changes
      final Patch processedPatch = createProcessedPatch(original);
      // The rollback containing all the recorded rollback actions
      final RollbackPatch rollbackPatch = createRollbackPatch(patchId, patchType);
      callback.finishPatch(processedPatch, rollbackPatch, this);
    } catch (Exception e) {
      if (undoChanges()) {
        callback.operationCancelled(this);
      }
      throw e;
    }
    state = State.PREPARED;
    return new PatchingResult() {
      @Override
      public String getPatchId() {
        return original.getPatchId();
      }

      @Override
      public PatchInfo getPatchInfo() {
        return new PatchInfo() {
          @Override
          public String getVersion() {
            return identityEntry.getResultingVersion();
          }

          @Override
          public String getCumulativePatchID() {
            return identityEntry.delegate.getModifiedState().getCumulativePatchID();
          }

          @Override
          public List<String> getPatchIDs() {
            return identityEntry.delegate.getModifiedState().getPatchIDs();
          }
        };
      }

      @Override
      public void commit() {
        if (state == State.PREPARED) {
          complete(modification, callback);
        } else {
          undoChanges();
          throw new IllegalStateException();
        }
      }

      @Override
      public void rollback() {
        if (undoChanges()) {
          try {
            callback.operationCancelled(IdentityPatchContext.this);
          } finally {
            modification.cancel();
          }
        }
      }
    };
  }