FileRemoveTask(PatchingTaskDescription description, File target, File backup) { this.target = target; this.backup = backup; this.description = description; this.item = description.getContentItem(MiscContentItem.class); }
/** * Get the original modification. Tasks like module remove can override this and fix the hashes based on the created content. * * @param targetHash the new target hash code (current content) * @param itemHash the new content item hash (backup content) * @return the original modification */ protected ContentModification getOriginalModification(byte[] targetHash, byte[] itemHash) { return description.getModification(); }
static PatchingTaskDescription create(final PatchingTasks.ContentTaskDefinition definition, final PatchContentLoader loader) { final ContentModification modification = resolveDefinition(definition); // Check if we already have the new content final ContentItem item = definition.getTarget().getItem(); final byte[] currentHash = definition.getLatest().getTargetHash(); final byte[] newContentHash = item.getContentHash(); boolean skipIfExists = Arrays.equals(currentHash, newContentHash); return new PatchingTaskDescription(definition.getTarget().getPatchId(), modification, loader, definition.hasConflicts(), skipIfExists, definition.isRollback()); }
@Override public boolean prepare(final PatchingTaskContext context) throws IOException { // Backup backupHash = backup(context); // If the content is already present just resolve any conflict automatically final byte[] contentHash = contentItem.getContentHash(); if(Arrays.equals(backupHash, contentHash)) { return true; } // See if the content matches our expected target final byte[] expected = description.getModification().getTargetHash(); if(Arrays.equals(backupHash, expected)) { // Don't resolve conflicts from the history return ! description.hasConflicts(); } // System.out.println("ModuleUpdateTask.prepare " + description.getModificationType() + " backup " + (backupHash == IoUtils.NO_CONTENT)); // the problem here appears for compact CPs when a module at some point was added then removed and then re-added // re-adding will be MODIFY because the removed module will exist on the FS but will be marked as absent in its module.xml // so applying re-add (MODIFY) to the version where the module didn't exist will fail return false; }
@Test public void testSimple() throws Exception { // content-item 'two' replacing 'one' final RollbackInfo patch01 = createRollbackInfo("patch01", two, one); // content-item 'three' replacing 'two' final RollbackInfo patch02 = createRollbackInfo("patch02", three, two); // [patch-two, patch-one] final ContentTaskDefinitions defs = process(patch02, patch01); Assert.assertEquals(1, defs.size()); final PatchingTasks.ContentTaskDefinition def = defs.get(new Location(new MiscContentItem(name, path, one))); Assert.assertNotNull(def); Assert.assertFalse(def.hasConflicts()); // We want to restore one (from the backup) Assert.assertEquals(one, def.getTarget().getItem().getContentHash()); // The original target was two Assert.assertEquals(two, def.getTarget().getTargetHash()); // The current content however is three Assert.assertEquals(three, def.getLatest().getTargetHash()); // And originally replaced two Assert.assertEquals(two, def.getLatest().getItem().getContentHash()); // The resulting operation should replace 'three' with 'one' final ContentModification modification = PatchingTaskDescription.resolveDefinition(def); Assert.assertEquals(one, modification.getItem().getContentHash()); Assert.assertEquals(three, modification.getTargetHash()); }
static PatchingTask create(final PatchingTaskDescription description, final IdentityPatchContext.PatchEntry context) { final ContentItem item = description.getContentItem(); switch (item.getContentType()) { case BUNDLE: return createBundleTask(description); case MISC: return createMiscTask(description, (MiscContentItem) item, context); case MODULE: return createModuleTask(description, context.getCurrentMode() == Mode.ROLLBACK || description.isRolledback()); default: throw new IllegalStateException(); } }
@Override protected byte[] notFound(ModuleItem contentItem) throws IOException { // Maybe just don't record the original ADD as part of the history? if (description.getModificationType() == ModificationType.REMOVE) { return contentItem.getContentHash(); } return super.notFound(contentItem); }
/** * Create the patching task based on the definition. * * @param definition the task description * @param provider the content provider * @param context the task context * @return the created task */ static PatchingTask createTask(final PatchingTasks.ContentTaskDefinition definition, final PatchContentProvider provider, final IdentityPatchContext.PatchEntry context) { final PatchContentLoader contentLoader = provider.getLoader(definition.getTarget().getPatchId()); final PatchingTaskDescription description = PatchingTaskDescription.create(definition, contentLoader); return PatchingTask.Factory.create(description, context); }
@Override public void execute(final PatchingTaskContext context) throws IOException { if (ignoreApply) { return; } final PatchContentLoader contentLoader = description.getLoader(); final boolean skip = skipExecution | context.isExcluded(contentItem); final byte[] contentHash; if(skip) { contentHash = backupHash; // Reuse the backup hash } else { contentHash = apply(context, contentLoader); // Copy the content } // Add the rollback action final ContentModification original = getOriginalModification(contentHash, backupHash); final ContentModification rollbackAction = createRollbackEntry(original, contentHash, backupHash); context.recordChange(original, rollbackAction); // Fail after adding the undo action if (! Arrays.equals(contentHash, contentItem.getContentHash()) && failOnContentMismatch(context)) { throw PatchLogger.ROOT_LOGGER.wrongCopiedContent(contentItem); } }
/** * Undo changes for a single patch entry. * * @param entry the patch entry * @param loader the content loader */ static void undoChanges(final PatchEntry entry, final PatchContentLoader loader) { final List<ContentModification> modifications = new ArrayList<ContentModification>(entry.rollbackActions); for (final ContentModification modification : modifications) { final ContentItem item = modification.getItem(); if (item.getContentType() != ContentType.MISC) { // Skip modules and bundles they should be removed as part of the {@link FinalizeCallback} continue; } final PatchingTaskDescription description = new PatchingTaskDescription(entry.applyPatchId, modification, loader, false, false, false); try { final PatchingTask task = PatchingTask.Factory.create(description, entry); task.execute(entry); } catch (Exception e) { PatchLogger.ROOT_LOGGER.failedToUndoChange(item.toString()); } } }
static PatchingTaskDescription create(final PatchingTasks.ContentTaskDefinition definition, final PatchContentLoader loader) { final ContentModification modification = resolveDefinition(definition); // Check if we already have the new content final ContentItem item = definition.getTarget().getItem(); final byte[] currentHash = definition.getLatest().getTargetHash(); final byte[] newContentHash = item.getContentHash(); boolean skipIfExists = Arrays.equals(currentHash, newContentHash); return new PatchingTaskDescription(definition.getTarget().getPatchId(), modification, loader, definition.hasConflicts(), skipIfExists, definition.isRollback()); }
@Override public boolean prepare(final PatchingTaskContext context) throws IOException { // Backup backupHash = backup(context); // If the content is already present just resolve any conflict automatically final byte[] contentHash = contentItem.getContentHash(); if(Arrays.equals(backupHash, contentHash)) { return true; } // See if the content matches our expected target final byte[] expected = description.getModification().getTargetHash(); if(Arrays.equals(backupHash, expected)) { // Don't resolve conflicts from the history return ! description.hasConflicts(); } // System.out.println("ModuleUpdateTask.prepare " + description.getModificationType() + " backup " + (backupHash == IoUtils.NO_CONTENT)); // the problem here appears for compact CPs when a module at some point was added then removed and then re-added // re-adding will be MODIFY because the removed module will exist on the FS but will be marked as absent in its module.xml // so applying re-add (MODIFY) to the version where the module didn't exist will fail return false; }
@Test public void testPreserveExisting() throws Exception { // content-item 'two' replacing 'one', but kept 'four' final RollbackInfo patch01 = createRollbackInfo("patch01", two, one, four, four); // content-item 'three' replacing 'two' final RollbackInfo patch02 = createRollbackInfo("patch02", three, four); // [patch-two, patch-one] final ContentTaskDefinitions defs = process(patch02, patch01); Assert.assertEquals(1, defs.size()); final PatchingTasks.ContentTaskDefinition def = defs.get(new Location(new MiscContentItem(name, path, one))); Assert.assertNotNull(def); Assert.assertTrue(def.hasConflicts()); // We want to got back to four Assert.assertEquals(four, def.getTarget().getItem().getContentHash()); // The recorded action was preserving four Assert.assertEquals(four, def.getTarget().getTargetHash()); // The current content however is three Assert.assertEquals(three, def.getLatest().getTargetHash()); // And originally replaced four Assert.assertEquals(four, def.getLatest().getItem().getContentHash()); // The resulting operation should replace 'three' with 'four' final ContentModification modification = PatchingTaskDescription.resolveDefinition(def); Assert.assertEquals(four, modification.getItem().getContentHash()); Assert.assertEquals(three, modification.getTargetHash()); }
static PatchingTask create(final PatchingTaskDescription description, final IdentityPatchContext.PatchEntry context) { final ContentItem item = description.getContentItem(); switch (item.getContentType()) { case BUNDLE: return createBundleTask(description); case MISC: return createMiscTask(description, (MiscContentItem) item, context); case MODULE: return createModuleTask(description, context.getCurrentMode() == Mode.ROLLBACK || description.isRolledback()); default: throw new IllegalStateException(); } }
@Override protected byte[] notFound(ModuleItem contentItem) throws IOException { // Maybe just don't record the original ADD as part of the history? if (description.getModificationType() == ModificationType.REMOVE) { return contentItem.getContentHash(); } return super.notFound(contentItem); }
/** * Create the patching task based on the definition. * * @param definition the task description * @param provider the content provider * @param context the task context * @return the created task */ static PatchingTask createTask(final PatchingTasks.ContentTaskDefinition definition, final PatchContentProvider provider, final IdentityPatchContext.PatchEntry context) { final PatchContentLoader contentLoader = provider.getLoader(definition.getTarget().getPatchId()); final PatchingTaskDescription description = PatchingTaskDescription.create(definition, contentLoader); return PatchingTask.Factory.create(description, context); }
@Override public void execute(final PatchingTaskContext context) throws IOException { if (ignoreApply) { return; } final PatchContentLoader contentLoader = description.getLoader(); final boolean skip = skipExecution | context.isExcluded(contentItem); final byte[] contentHash; if(skip) { contentHash = backupHash; // Reuse the backup hash } else { contentHash = apply(context, contentLoader); // Copy the content } // Add the rollback action final ContentModification original = getOriginalModification(contentHash, backupHash); final ContentModification rollbackAction = createRollbackEntry(original, contentHash, backupHash); context.recordChange(original, rollbackAction); // Fail after adding the undo action if (! Arrays.equals(contentHash, contentItem.getContentHash()) && failOnContentMismatch(context)) { throw PatchLogger.ROOT_LOGGER.wrongCopiedContent(contentItem); } }
/** * Undo changes for a single patch entry. * * @param entry the patch entry * @param loader the content loader */ static void undoChanges(final PatchEntry entry, final PatchContentLoader loader) { final List<ContentModification> modifications = new ArrayList<ContentModification>(entry.rollbackActions); for (final ContentModification modification : modifications) { final ContentItem item = modification.getItem(); if (item.getContentType() != ContentType.MISC) { // Skip modules and bundles they should be removed as part of the {@link FinalizeCallback} continue; } final PatchingTaskDescription description = new PatchingTaskDescription(entry.applyPatchId, modification, loader, false, false, false); try { final PatchingTask task = PatchingTask.Factory.create(description, entry); task.execute(entry); } catch (Exception e) { PatchLogger.ROOT_LOGGER.failedToUndoChange(item.toString()); } } }
@Override public boolean prepare(final PatchingTaskContext context) throws IOException { // Backup backupHash = backup(context); // If the content is already present just resolve any conflict automatically final byte[] contentHash = contentItem.getContentHash(); if(Arrays.equals(backupHash, contentHash)) { // Skip execute for misc items only skipExecution = contentItem.getContentType() == ContentType.MISC && backupHash != NO_CONTENT; return true; } // See if the content matches our expected target final byte[] expected = description.getModification().getTargetHash(); if(Arrays.equals(backupHash, expected)) { // Don't resolve conflicts from the history return ! description.hasConflicts(); } return false; }
@Test public void testOverrideExisting() throws Exception { // content-item 'two' replacing 'four', originally targeting 'one' final RollbackInfo patch01 = createRollbackInfo("patch01", two, one, four, two); // content-item 'three' replacing 'two' final RollbackInfo patch02 = createRollbackInfo("patch02", three, two); // [patch-two, patch-one] final ContentTaskDefinitions defs = process(patch02, patch01); Assert.assertEquals(1, defs.size()); final PatchingTasks.ContentTaskDefinition def = defs.get(new Location(new MiscContentItem(name, path, one))); Assert.assertNotNull(def); Assert.assertTrue(def.hasConflicts()); // We want to restore four (from the backup) Assert.assertEquals(four, def.getTarget().getItem().getContentHash()); // The original target was two Assert.assertEquals(two, def.getTarget().getTargetHash()); // The current content however is three Assert.assertEquals(three, def.getLatest().getTargetHash()); // And originally replaced two Assert.assertEquals(two, def.getLatest().getItem().getContentHash()); // The resulting operation should replace 'three' with 'four' final ContentModification modification = PatchingTaskDescription.resolveDefinition(def); Assert.assertEquals(four, modification.getItem().getContentHash()); Assert.assertEquals(three, modification.getTargetHash()); }