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()));
}
}
}