package org.jboss.as.patching.installation; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; import org.jboss.as.patching.Constants; import org.jboss.as.patching.DirectoryStructure; import org.jboss.as.patching.IoUtils; import org.jboss.as.patching.metadata.Patch; import org.jboss.as.patching.runner.PatchUtils; /** * @author Emanuel Muckenhuber */ class MutableTargetImpl implements InstallationManager.MutablePatchingTarget { private final DirectoryStructure structure; private final PatchableTarget.TargetInfo current; // The mutable state private final List<String> patchIds; private final Properties properties; private String cumulativeID; private boolean modified = false; // this attribute will be null for layers and add-ons in their current implementation private String version; private Set<String> rolledback = Collections.emptySet(); MutableTargetImpl(PatchableTarget.TargetInfo current) { this(current, null); } MutableTargetImpl(PatchableTarget.TargetInfo current, String currentVersion) { this.current = current; this.structure = current.getDirectoryStructure(); this.cumulativeID = current.getCumulativePatchID(); this.patchIds = new ArrayList<String>(current.getPatchIDs()); this.properties = new Properties(current.getProperties()); this.version = currentVersion; } @Override public boolean isApplied(String patchId) { if (cumulativeID.equals(patchId)) { return true; } return patchIds.contains(patchId); } @Override public void rollback(final String patchId) { if (!patchIds.remove(patchId)) { if (patchId.equals(cumulativeID)) { cumulativeID = Constants.NOT_PATCHED; } else { throw new IllegalStateException("cannot rollback not-applied patch " + patchId); // internal wrong usage, no i18n } } else { switch(rolledback.size()) { case 0: rolledback = Collections.singleton(patchId); break; case 1: rolledback = new HashSet<String>(rolledback); default: rolledback.add(patchId); } } modified = true; } @Override public boolean isRolledback(final String patchId) { return rolledback.contains(patchId); } @Override public void apply(String patchId, Patch.PatchType patchType) { if (patchType == Patch.PatchType.CUMULATIVE) { if (!patchIds.isEmpty()) { throw new IllegalStateException("cannot apply cumulative patch if there are other patches applied " +patchIds); // internal wrong usage, no i18n } cumulativeID = patchId; } else { patchIds.add(0, patchId); } modified = true; } @Override public String getCumulativePatchID() { return current.getCumulativePatchID(); } @Override public List<String> getPatchIDs() { return current.getPatchIDs(); } @Override public Properties getProperties() { return current.getProperties(); } @Override public DirectoryStructure getDirectoryStructure() { return structure; } public String getVersion() { return version; } public void setResultingVersion(String version) { this.version = version; } protected Properties getMutableProperties() { return properties; } protected void persist() throws IOException { if (modified) { // persist the state for bundles and modules directory persist(cumulativeID, patchIds, properties); } } protected void restore() throws IOException { if (modified) { // persist the state for bundles and modules directory persist(current.getCumulativePatchID(), current.getPatchIDs(), current.getProperties()); } } protected void persist(final String cumulativeID, final List<String> patches, final Properties properties) throws IOException { assert cumulativeID != null; // Create the parent IoUtils.mkdir(structure.getInstallationInfo().getParentFile()); final List<String> consolidate = new ArrayList<String>(); consolidate.addAll(patches); if (!Constants.BASE.equals(cumulativeID)) { consolidate.add(cumulativeID); } if (structure.getModuleRoot() != null) { final File overlays = new File(structure.getModuleRoot(), Constants.OVERLAYS); final File refs = new File(overlays, Constants.OVERLAYS); PatchUtils.writeRefs(refs, consolidate); } // Update the properties properties.put(Constants.CUMULATIVE, cumulativeID); properties.put(Constants.PATCHES, PatchUtils.asString(patches)); if(version != null) { properties.put(Constants.CURRENT_VERSION, version); } // Write layer.conf PatchUtils.writeProperties(structure.getInstallationInfo(), properties); } @Override public PatchableTarget.TargetInfo getModifiedState() { if (modified) { return new LayerInfo.TargetInfoImpl(properties, cumulativeID, patchIds, structure); } else { return current; } } }