/** * Internally undo recorded changes we did so far. * * @return whether the state required undo actions */ boolean undoChanges() { final State state = stateUpdater.getAndSet(this, State.ROLLBACK_ONLY); if (state == State.COMPLETED || state == State.ROLLBACK_ONLY) { // Was actually completed already return false; } PatchingTaskContext.Mode currentMode = this.mode; mode = PatchingTaskContext.Mode.UNDO; final PatchContentLoader loader = PatchContentLoader.create(miscBackup, null, null); // Undo changes for the identity undoChanges(identityEntry, loader); // TODO maybe check if we need to do something for the layers too !? if (state == State.INVALIDATE || currentMode == PatchingTaskContext.Mode.ROLLBACK) { // For apply the state needs to be invalidate // For rollback the files are invalidated as part of the tasks final PatchingTaskContext.Mode mode = currentMode == PatchingTaskContext.Mode.APPLY ? PatchingTaskContext.Mode.ROLLBACK : PatchingTaskContext.Mode.APPLY; for (final File file : moduleInvalidations) { try { PatchModuleInvalidationUtils.processFile(file, mode); } catch (Exception e) { PatchLogger.ROOT_LOGGER.debugf(e, "failed to restore state for %s", file); } } } return true; }
/** * 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"); } } } }
@Override public void invalidateRoot(final File moduleRoot) throws IOException { final File[] files = moduleRoot.listFiles( new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".jar"); } }); if (files != null && files.length > 0) { for (final File file : files) { moduleInvalidations.add(file); if (mode == Mode.ROLLBACK) { // For rollback we need to restore the file before calculating the hash PatchModuleInvalidationUtils.processFile(file, mode); } } } }