package org.jboss.as.patching.runner; import static org.jboss.as.patching.runner.PatchingTaskContext.Mode.APPLY; import static org.jboss.as.patching.runner.PatchingTaskContext.Mode.ROLLBACK; import static org.jboss.as.patching.runner.PatchingTasks.apply; import static org.jboss.as.patching.validation.PatchHistoryValidations.validateRollbackState; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.xml.stream.XMLStreamException; import org.jboss.as.patching.Constants; import org.jboss.as.patching.DirectoryStructure; import org.jboss.as.patching.IoUtils; import org.jboss.as.patching.PatchingException; import org.jboss.as.patching.installation.InstallationManager; import org.jboss.as.patching.installation.InstalledIdentity; import org.jboss.as.patching.installation.InstalledImage; import org.jboss.as.patching.installation.PatchableTarget; import org.jboss.as.patching.logging.PatchLogger; import org.jboss.as.patching.metadata.ContentItem; import org.jboss.as.patching.metadata.ContentModification; import org.jboss.as.patching.metadata.ContentType; import org.jboss.as.patching.metadata.Identity; import org.jboss.as.patching.metadata.LayerType; import org.jboss.as.patching.metadata.ModuleItem; import org.jboss.as.patching.metadata.Patch; import org.jboss.as.patching.metadata.Patch.PatchType; import org.jboss.as.patching.metadata.PatchElement; import org.jboss.as.patching.metadata.PatchElementProvider; import org.jboss.as.patching.metadata.PatchMetadataResolver; import org.jboss.as.patching.metadata.PatchXml; import org.jboss.as.patching.metadata.RollbackPatch; import org.jboss.as.patching.metadata.UpgradeCondition; import org.jboss.as.patching.runner.IdentityPatchContext.PatchEntry; import org.jboss.as.patching.runner.PatchingTasks.ContentTaskDefinition; import org.jboss.as.patching.tool.ContentVerificationPolicy; import org.jboss.as.patching.tool.PatchingHistory; import org.jboss.as.patching.tool.PatchingHistory.Entry; import org.jboss.as.patching.tool.PatchingHistory.Iterator; import org.jboss.as.patching.tool.PatchingResult; /** * @author Emanuel Muckenhuber */ class IdentityPatchRunner implements InstallationManager.ModificationCompletionCallback { private static final String DIRECTORY_SUFFIX = "jboss-as-patch-"; private static final File TEMP_DIR = new File(SecurityActions.getSystemProperty("java.io.tmpdir")); private final InstalledImage installedImage; IdentityPatchRunner(InstalledImage installedImage) { this.installedImage = installedImage; } /** * Apply a patch. * * @param patchResolver the patch metadata resolver * @param contentProvider the patch content provider * @param contentPolicy the content verification policy * @param modification the installation modification * @throws PatchingException for any error */ public PatchingResult applyPatch(final PatchMetadataResolver patchResolver, final PatchContentProvider contentProvider, final ContentVerificationPolicy contentPolicy, final InstallationManager.InstallationModification modification) throws PatchingException { try { // Check if we can apply this patch final Patch patch = patchResolver.resolvePatch(modification.getName(), modification.getVersion()); if (patch == null) { throw PatchLogger.ROOT_LOGGER.failedToResolvePatch(modification.getName(), modification.getVersion()); } final String patchId = patch.getPatchId(); final Identity identity = patch.getIdentity(); final String appliesTo = identity.getVersion(); if (!appliesTo.equals(modification.getVersion())) { throw PatchLogger.ROOT_LOGGER.doesNotApply(appliesTo, modification.getVersion()); } // Cannot apply the same patch twice if (modification.isApplied(patchId)) { throw PatchLogger.ROOT_LOGGER.alreadyApplied(patchId); } // See if the prerequisites are met checkUpgradeConditions(identity, modification); // Apply the patch final File backup = installedImage.getPatchHistoryDir(patchId); final IdentityPatchContext context = new IdentityPatchContext(backup, contentProvider, contentPolicy, modification, APPLY, installedImage); try { return applyPatch(patchId, patch, context); } catch (Exception e) { PatchLogger.ROOT_LOGGER.debugf(e, "failed to apply patch %s", patchId); throw rethrowException(e); } finally { context.cleanup(); } } finally { contentProvider.cleanup(); } } /** * Apply a patch. * * @param patchId the patch id * @param patch the patch metadata * @param context the patch context * @throws PatchingException * @throws IOException * @throws XMLStreamException */ private PatchingResult applyPatch(final String patchId, final Patch patch, final IdentityPatchContext context) throws PatchingException, IOException, XMLStreamException { final Identity identity = patch.getIdentity(); final Patch.PatchType patchType = identity.getPatchType(); final InstallationManager.InstallationModification modification = context.getModification(); if (patchType == Patch.PatchType.CUMULATIVE) { // Invalidate all installed patches (one-off, cumulative) - we never need to invalidate the release base final List<String> invalidation = new ArrayList<String>(modification.getPatchIDs()); if (!invalidation.isEmpty()) { try { // Before rolling back the one-off patches, validate that the state until that point is consistent validateRollbackState(invalidation.get(invalidation.size() - 1), modification.getUnmodifiedInstallationState()); } catch (PatchingException e) { throw e; } catch (Exception e) { throw new PatchingException(e); } // Invalidate the installed patches first for (final String rollback : invalidation) { rollback(rollback, context); } } } // Add to installed patches list modification.addInstalledPatch(patchId); // Then apply the current patch for (final PatchElement element : patch.getElements()) { // Apply the content modifications final IdentityPatchContext.PatchEntry target = context.resolveForElement(element); final PatchElementProvider provider = element.getProvider(); final Patch.PatchType elementPatchType = provider.getPatchType(); final String elementPatchId = element.getId(); // See if we can skip this element if (target.isApplied(elementPatchId)) { // TODO if it is already applied, we can just skip the entry (maybe based ont the type of the patch) // This needs some further testing, maybe we need to compare our history with the patch if they are consistent throw PatchLogger.ROOT_LOGGER.alreadyApplied(elementPatchId); } // Check upgrade conditions checkUpgradeConditions(provider, target); apply(elementPatchId, element.getModifications(), target); target.apply(elementPatchId, elementPatchType); } // Apply the patch to the identity final IdentityPatchContext.PatchEntry identityEntry = context.getIdentityEntry(); apply(patchId, patch.getModifications(), identityEntry); identityEntry.apply(patchId, patchType); // Port forward missing module changes if (patchType == Patch.PatchType.CUMULATIVE) { portForward(patch, context); } // We need the resulting version for rollback if (patchType == Patch.PatchType.CUMULATIVE) { final Identity.IdentityUpgrade upgrade = identity.forType(Patch.PatchType.CUMULATIVE, Identity.IdentityUpgrade.class); identityEntry.setResultingVersion(upgrade.getResultingVersion()); } // Execute the tasks final IdentityApplyCallback callback = new IdentityApplyCallback(patch, identityEntry.getDirectoryStructure()); try { return executeTasks(context, callback); } catch (Exception e) { context.cancel(callback); throw rethrowException(e); } } /** * Rollback a patch. * * @param patchId the patch id * @param contentPolicy the content policy * @param rollbackTo rollback multiple one off patches * @param resetConfiguration whether to reset the configuration * @param modification the installation modification * @return the patching result * @throws PatchingException */ public PatchingResult rollbackPatch(final String patchId, final ContentVerificationPolicy contentPolicy, final boolean rollbackTo, final boolean resetConfiguration, InstallationManager.InstallationModification modification) throws PatchingException { if (Constants.BASE.equals(patchId)) { throw PatchLogger.ROOT_LOGGER.cannotRollbackPatch(patchId); } try { // Before rolling back the patch, validate that the state until that point is consistent validateRollbackState(patchId, modification.getUnmodifiedInstallationState()); } catch (PatchingException e) { throw e; } catch (Exception e) { throw new PatchingException(e); } // Figure out what to do final List<String> patches = new ArrayList<String>(); final List<String> oneOffs = modification.getPatchIDs(); final int index = oneOffs.indexOf(patchId); if (index == -1) { if (patchId.equals(modification.getCumulativePatchID())) { // Rollback all active patches.addAll(oneOffs); patches.add(modification.getCumulativePatchID()); } else { throw PatchLogger.ROOT_LOGGER.cannotRollbackPatch(patchId); } } else if (index == 0) { patches.add(patchId); } else { if (rollbackTo) { // rollback one-offs up to the given patchId for (int i = 0; i <= index; i++) { patches.add(oneOffs.get(i)); } } else { // TODO perhaps we can allow this as well? throw PatchLogger.ROOT_LOGGER.cannotRollbackPatch(patchId); } } final File historyDir = installedImage.getPatchHistoryDir(patchId); assertExists(historyDir); final File rollbackXml = new File(historyDir, Constants.ROLLBACK_XML); assertExists(rollbackXml); final File workDir = createTempDir(); final PatchContentProvider provider = PatchContentProvider.ROLLBACK_PROVIDER; final IdentityPatchContext context = new IdentityPatchContext(workDir, provider, contentPolicy, modification, ROLLBACK, installedImage); try { // Rollback patches for (final String rollback : patches) { if (!Constants.BASE.equals(rollback)) { rollback(rollback, context); modification.removeInstalledPatch(rollback); } } // Execute the tasks final IdentityPatchContext.PatchEntry identity = context.getIdentityEntry(); final IdentityRollbackCallback callback = new IdentityRollbackCallback(patchId, patches, resetConfiguration, identity.getDirectoryStructure()); try { return executeTasks(context, callback); } catch (Exception e) { context.cancel(callback); PatchLogger.ROOT_LOGGER.debugf(e, "failed to rollback patch %s", patchId); throw rethrowException(e); } } finally { if (workDir != null && !IoUtils.recursiveDelete(workDir)) { PatchLogger.ROOT_LOGGER.cannotDeleteFile(workDir.getAbsolutePath()); } context.cleanup(); } } /** * Rollback the last applied patch. * * @param contentPolicy the content policy * @param resetConfiguration whether to reset the configuration * @param modification the installation modification * @return the patching result * @throws PatchingException */ public PatchingResult rollbackLast(final ContentVerificationPolicy contentPolicy, final boolean resetConfiguration, InstallationManager.InstallationModification modification) throws PatchingException { // Determine the patch id to rollback String patchId; final List<String> oneOffs = modification.getPatchIDs(); if (oneOffs.isEmpty()) { patchId = modification.getCumulativePatchID(); if (patchId == null || Constants.NOT_PATCHED.equals(patchId)) { throw PatchLogger.ROOT_LOGGER.noPatchesApplied(); } } else { patchId = oneOffs.get(0);//oneOffs.get(oneOffs.size() - 1); } return rollbackPatch(patchId, contentPolicy, false, resetConfiguration, modification); } @Override public void completed() { // nothing here } @Override public void canceled() { // nothing here } /** * Rollback a patch. * * @param patchID the patch id * @param context the patch context * @throws PatchingException */ private void rollback(final String patchID, final IdentityPatchContext context) throws PatchingException { try { // Load the patch history final PatchingTaskContext.Mode mode = context.getMode(); final Patch originalPatch = loadPatchInformation(patchID, installedImage); final RollbackPatch rollbackPatch = loadRollbackInformation(patchID, installedImage); final Patch.PatchType patchType = rollbackPatch.getIdentity().getPatchType(); final InstalledIdentity history = rollbackPatch.getIdentityState(); // Process originals by type first final LinkedHashMap<String, PatchElement> originalLayers = new LinkedHashMap<String, PatchElement>(); final LinkedHashMap<String, PatchElement> originalAddOns = new LinkedHashMap<String, PatchElement>(); for (final PatchElement patchElement : originalPatch.getElements()) { final PatchElementProvider provider = patchElement.getProvider(); final String layerName = provider.getName(); final LayerType layerType = provider.getLayerType(); final Map<String, PatchElement> originals; switch (layerType) { case Layer: originals = originalLayers; break; case AddOn: originals = originalAddOns; break; default: throw new IllegalStateException(); } if (!originals.containsKey(layerName)) { originals.put(layerName, patchElement); } else { throw PatchLogger.ROOT_LOGGER.installationDuplicateLayer(layerType.toString(), layerName); } } // Process the rollback xml for (final PatchElement patchElement : rollbackPatch.getElements()) { final String elementPatchId = patchElement.getId(); final PatchElementProvider provider = patchElement.getProvider(); final String layerName = provider.getName(); final LayerType layerType = provider.getLayerType(); final LinkedHashMap<String, PatchElement> originals; switch (layerType) { case Layer: originals = originalLayers; break; case AddOn: originals = originalAddOns; break; default: throw new IllegalStateException(); } final PatchElement original = originals.remove(layerName); if (original == null) { throw PatchLogger.ROOT_LOGGER.noSuchLayer(layerName); } final IdentityPatchContext.PatchEntry entry = context.resolveForElement(patchElement); // Create the rollback PatchingTasks.rollback(elementPatchId, original.getModifications(), patchElement.getModifications(), entry, ContentItemFilter.ALL_BUT_MISC, mode); entry.rollback(original.getId()); // We need to restore the previous state final Patch.PatchType elementPatchType = provider.getPatchType(); final PatchableTarget.TargetInfo info; if (layerType == LayerType.AddOn) { info = history.getAddOn(layerName).loadTargetInfo(); } else { info = history.getLayer(layerName).loadTargetInfo(); } if (mode == ROLLBACK) { restoreFromHistory(entry, elementPatchId, elementPatchType, info); } } if (!originalLayers.isEmpty() || !originalAddOns.isEmpty()) { throw PatchLogger.ROOT_LOGGER.invalidRollbackInformation(); } // Rollback the patch final IdentityPatchContext.PatchEntry identity = context.getIdentityEntry(); PatchingTasks.rollback(patchID, originalPatch.getModifications(), rollbackPatch.getModifications(), identity, ContentItemFilter.MISC_ONLY, mode); identity.rollback(patchID); // Restore previous state if (mode == ROLLBACK) { final PatchableTarget.TargetInfo identityHistory = history.getIdentity().loadTargetInfo(); restoreFromHistory(identity, rollbackPatch.getPatchId(), patchType, identityHistory); if(patchType == Patch.PatchType.CUMULATIVE) { reenableNotOverridenModules(rollbackPatch, context); } } if (patchType == Patch.PatchType.CUMULATIVE) { final Identity.IdentityUpgrade upgrade = rollbackPatch.getIdentity().forType(Patch.PatchType.CUMULATIVE, Identity.IdentityUpgrade.class); identity.setResultingVersion(upgrade.getResultingVersion()); } } catch (Exception e) { throw rethrowException(e); } } /** * Restore the recorded state from the rollback xml. * * @param target the patchable target * @param rollbackPatchId the rollback patch id * @param patchType the the current patch type * @param history the recorded history * @throws PatchingException */ static void restoreFromHistory(final InstallationManager.MutablePatchingTarget target, final String rollbackPatchId, final Patch.PatchType patchType, final PatchableTarget.TargetInfo history) throws PatchingException { if (patchType == Patch.PatchType.CUMULATIVE) { assert history.getCumulativePatchID().equals(rollbackPatchId); target.apply(rollbackPatchId, patchType); // Restore one off state final List<String> oneOffs = new ArrayList<String>(history.getPatchIDs()); Collections.reverse(oneOffs); for (final String oneOff : oneOffs) { target.apply(oneOff, Patch.PatchType.ONE_OFF); } } checkState(history, history); // Just check for tests, that rollback should restore the old state } static void checkState(final PatchableTarget.TargetInfo o, final PatchableTarget.TargetInfo n) { assert n.getPatchIDs().equals(o.getPatchIDs()); assert n.getCumulativePatchID().equals(o.getCumulativePatchID()); } void reenableNotOverridenModules(final RollbackPatch patch, IdentityPatchContext context) throws PatchingException, IOException, XMLStreamException { assert patch.getIdentity().getPatchType() == Patch.PatchType.CUMULATIVE; final Iterator historyIterator = context.getHistory().iterator(patch.getIdentityState().getIdentity().loadTargetInfo()); if(!historyIterator.hasNext()) { return; } final List<PatchElement> elements = patch.getElements(); final Map<String, List<PatchElement>> layerPatches = new HashMap<String, List<PatchElement>>(elements.size()); final Map<String, List<PatchElement>> addonPatches = new HashMap<String, List<PatchElement>>(elements.size()); for(PatchElement e : elements) { if(e.getProvider().isAddOn()) { addonPatches.put(e.getProvider().getName(), Collections.<PatchElement>emptyList()); } else { layerPatches.put(e.getProvider().getName(), Collections.<PatchElement>emptyList()); } } Patch prevCP = null; while(historyIterator.hasNext()) { final Entry entry = historyIterator.next(); if(entry.getType() == PatchType.CUMULATIVE) { prevCP = entry.getMetadata(); break; } final Patch oneOff = entry.getMetadata(); for(PatchElement oneOffElement : oneOff.getElements()) { final Map<String, List<PatchElement>> providerPatches; if(oneOffElement.getProvider().isAddOn()) { providerPatches = addonPatches; } else { providerPatches = layerPatches; } List<PatchElement> patches = providerPatches.get(oneOffElement.getProvider().getName()); if(patches != null) { switch(patches.size()) { case 0: providerPatches.put(oneOffElement.getProvider().getName(), Collections.singletonList(oneOffElement)); break; case 1: patches = new ArrayList<PatchElement>(patches); providerPatches.put(oneOffElement.getProvider().getName(), patches); default: patches.add(oneOffElement); } } } } Set<ModuleItem> cpElementModules; Set<ModuleItem> reenabledModules = Collections.emptySet(); for(PatchElement e : elements) { final List<PatchElement> patches; if(e.getProvider().isAddOn()) { patches = addonPatches.get(e.getProvider().getName()); } else { patches = layerPatches.get(e.getProvider().getName()); } if(patches.isEmpty()) { continue; } cpElementModules = null; final PatchEntry rollbackEntry = context.resolveForElement(e); for(PatchElement oneOff : patches) { for(ContentModification mod : oneOff.getModifications()) { if(mod.getItem().getContentType() != ContentType.MODULE) { continue; } final ModuleItem module = (ModuleItem) mod.getItem(); if(rollbackEntry.get(new Location(module)) != null) { continue; } if(reenabledModules.contains(module)) { continue; } final File modulePath = PatchContentLoader.getModulePath(rollbackEntry.getDirectoryStructure().getModulePatchDirectory(oneOff.getId()), module); rollbackEntry.invalidateRoot(modulePath); if(reenabledModules.isEmpty()) { reenabledModules = new HashSet<ModuleItem>(); } reenabledModules.add(module); if(prevCP == null) { rollbackEntry.disableBaseModule(module); } else { if(cpElementModules == null) { for(PatchElement cpE : prevCP.getElements()) { if(cpE.getProvider().getName().equals(e.getProvider().getName()) && cpE.getProvider().getLayerType().equals(e.getProvider().getLayerType())) { cpElementModules = new HashSet<ModuleItem>(cpE.getModifications().size()); for(ContentModification cpMod : cpE.getModifications()) { if(cpMod.getItem().getContentType() == ContentType.MODULE) { cpElementModules.add(cpMod.getItem(ModuleItem.class)); } } break; } } } if(cpElementModules != null && !cpElementModules.contains(module)) { rollbackEntry.disableBaseModule(module); } } } } } } /** * Port forward missing module changes for each layer. * * @param patch the current patch * @param context the patch context * @throws PatchingException * @throws IOException * @throws XMLStreamException */ void portForward(final Patch patch, IdentityPatchContext context) throws PatchingException, IOException, XMLStreamException { assert patch.getIdentity().getPatchType() == Patch.PatchType.CUMULATIVE; final PatchingHistory history = context.getHistory(); for (final PatchElement element : patch.getElements()) { final PatchElementProvider provider = element.getProvider(); final String name = provider.getName(); final boolean addOn = provider.isAddOn(); final IdentityPatchContext.PatchEntry target = context.resolveForElement(element); final String cumulativePatchID = target.getCumulativePatchID(); if (Constants.BASE.equals(cumulativePatchID)) { reenableRolledBackInBase(target); continue; } boolean found = false; final PatchingHistory.Iterator iterator = history.iterator(); while (iterator.hasNextCP()) { final PatchingHistory.Entry entry = iterator.nextCP(); final String patchId = addOn ? entry.getAddOnPatches().get(name) : entry.getLayerPatches().get(name); if (patchId != null && patchId.equals(cumulativePatchID)) { final Patch original = loadPatchInformation(entry.getPatchId(), installedImage); for (final PatchElement originalElement : original.getElements()) { if (name.equals(originalElement.getProvider().getName()) && addOn == originalElement.getProvider().isAddOn()) { PatchingTasks.addMissingModifications(target, originalElement.getModifications(), ContentItemFilter.ALL_BUT_MISC); } } // Record a loader to have access to the current modules final DirectoryStructure structure = target.getDirectoryStructure(); final File modulesRoot = structure.getModulePatchDirectory(patchId); final File bundlesRoot = structure.getBundlesPatchDirectory(patchId); final PatchContentLoader loader = PatchContentLoader.create(null, bundlesRoot, modulesRoot); context.recordContentLoader(patchId, loader); found = true; break; } } if (!found) { throw PatchLogger.ROOT_LOGGER.patchNotFoundInHistory(cumulativePatchID); } reenableRolledBackInBase(target); } } protected void reenableRolledBackInBase(final IdentityPatchContext.PatchEntry target) throws IOException { for(ContentTaskDefinition def : target.getTaskDefinitions()) { if(def.isRollback()) { final ContentItem item = def.getTarget().getItem(); if(item.getContentType() == ContentType.MODULE) { target.reenableBaseModule((ModuleItem)item); } } } } /** * Execute all recorded tasks. * * @param context the patch context * @param callback the finalization callback * @throws Exception */ static PatchingResult executeTasks(final IdentityPatchContext context, final IdentityPatchContext.FinalizeCallback callback) throws Exception { final List<PreparedTask> tasks = new ArrayList<PreparedTask>(); final List<ContentItem> conflicts = new ArrayList<ContentItem>(); // Identity prepareTasks(context.getIdentityEntry(), context, tasks, conflicts); // Layers for (final IdentityPatchContext.PatchEntry layer : context.getLayers()) { prepareTasks(layer, context, tasks, conflicts); } // AddOns for (final IdentityPatchContext.PatchEntry addOn : context.getAddOns()) { prepareTasks(addOn, context, tasks, conflicts); } // If there were problems report them if (!conflicts.isEmpty()) { throw PatchLogger.ROOT_LOGGER.conflictsDetected(conflicts); } // Execute the tasks for (final PreparedTask task : tasks) { // Unless it's excluded by the user final ContentItem item = task.getContentItem(); if (item != null && context.isExcluded(item)) { continue; } // Run the task task.execute(); } return context.finalize(callback); } /** * Prepare all tasks. * * @param entry the patch entry * @param context the patch context * @param tasks a list for prepared tasks * @param conflicts a list for conflicting content items * @throws PatchingException */ static void prepareTasks(final IdentityPatchContext.PatchEntry entry, final IdentityPatchContext context, final List<PreparedTask> tasks, final List<ContentItem> conflicts) throws PatchingException { for (final PatchingTasks.ContentTaskDefinition definition : entry.getTaskDefinitions()) { final PatchingTask task = createTask(definition, context, entry); if(!task.isRelevant(entry)) { continue; } try { // backup and validate content if (!task.prepare(entry) || definition.hasConflicts()) { // Unless it a content item was manually ignored (or excluded) final ContentItem item = task.getContentItem(); if (!context.isIgnored(item)) { conflicts.add(item); } } tasks.add(new PreparedTask(task, entry)); } catch (IOException e) { throw new PatchingException(e); } } } /** * 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); } static class PreparedTask { private final PatchingTask task; private final IdentityPatchContext.PatchEntry entry; PreparedTask(PatchingTask task, IdentityPatchContext.PatchEntry entry) { this.task = task; this.entry = entry; } ContentItem getContentItem() { return task.getContentItem(); } protected void execute() throws IOException { task.execute(entry); } } static Patch loadPatchInformation(final String patchId, final InstalledImage installedImage) throws PatchingException, IOException, XMLStreamException { final File patchDir = installedImage.getPatchHistoryDir(patchId); final File patchXml = new File(patchDir, PatchXml.PATCH_XML); return PatchXml.parse(patchXml).resolvePatch(null, null); } static RollbackPatch loadRollbackInformation(final String patchId, final InstalledImage installedImage) throws PatchingException, IOException, XMLStreamException { final File historyDir = installedImage.getPatchHistoryDir(patchId); final File patchXml = new File(historyDir, Constants.ROLLBACK_XML); return (RollbackPatch) PatchXml.parse(patchXml).resolvePatch(null, null); } static File createTempDir() throws PatchingException { return createTempDir(TEMP_DIR); } static File createTempDir(final File parent) throws PatchingException { File workDir = null; int count = 0; while (workDir == null || workDir.exists()) { count++; workDir = new File(parent == null ? TEMP_DIR : parent, DIRECTORY_SUFFIX + count); } if (!workDir.mkdirs()) { throw new PatchingException(PatchLogger.ROOT_LOGGER.cannotCreateDirectory(workDir.getAbsolutePath())); } return workDir; } static PatchingException rethrowException(final Exception e) { if (e instanceof PatchingException) { return (PatchingException) e; } else { return new PatchingException(e); } } /** * Check whether the patch can be applied to a given target. * * @param condition the conditions * @param target the target * @throws PatchingException */ static void checkUpgradeConditions(final UpgradeCondition condition, final InstallationManager.MutablePatchingTarget target) throws PatchingException { // See if the prerequisites are met for (final String required : condition.getRequires()) { if (!target.isApplied(required)) { throw PatchLogger.ROOT_LOGGER.requiresPatch(required); } } // Check for incompatibilities for (final String incompatible : condition.getIncompatibleWith()) { if (target.isApplied(incompatible)) { throw PatchLogger.ROOT_LOGGER.incompatiblePatch(incompatible); } } } static void assertExists(final File file) throws PatchingException { if (!file.exists()) { throw new PatchingException(PatchLogger.ROOT_LOGGER.fileDoesNotExist(file.getAbsolutePath())); } } }