/*
* JBoss, Home of Professional Open Source.
* Copyright 2014, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.patching.validation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jboss.as.patching.PatchingException;
import org.jboss.as.patching.installation.InstalledIdentity;
import org.jboss.as.patching.logging.PatchLogger;
/**
* Utility class to validate the patched state and history.
*
* @author Emanuel Muckenhuber
*/
public final class PatchHistoryValidations {
private PatchHistoryValidations() {
//
}
/**
* Validate the consistency of patches to the point we rollback.
*
* @param patchID the patch id which gets rolled back
* @param identity the installed identity
* @throws PatchingException
*/
public static void validateRollbackState(final String patchID, final InstalledIdentity identity) throws PatchingException {
final Set<String> validHistory = processRollbackState(patchID, identity);
if (patchID != null && !validHistory.contains(patchID)) {
throw PatchLogger.ROOT_LOGGER.patchNotFoundInHistory(patchID);
}
}
private static Set<String> processRollbackState(final String patchID, final InstalledIdentity identity) throws PatchingException {
final Set<String> validHistory = new HashSet<String>();
final PatchHistoryIterator.Builder builder = PatchHistoryIterator.Builder.create(identity);
final HistoryProcessor processor = new HistoryProcessor() {
boolean includeCurrent = true;
boolean proceed = true;
boolean found = false;
@Override
protected boolean includeCurrent() {
return includeCurrent;
}
@Override
protected boolean canProceed() {
return proceed;
}
@Override
protected <P extends PatchingArtifact.ArtifactState, S extends PatchingArtifact.ArtifactState> boolean handleError(PatchingArtifact<P, S> artifact, S state) {
if (artifact == PatchingArtifacts.HISTORY_DIR
|| artifact == PatchingArtifacts.PATCH_XML
|| artifact == PatchingArtifacts.ROLLBACK_XML
|| artifact == PatchingArtifacts.MISC_BACKUP) {
// If parts of the history are is missing we can rollback to this patch, but no further
proceed = false;
return found;
} else {
includeCurrent = false;
proceed = false;
return false;
}
}
@Override
protected void processedPatch(String patch) {
validHistory.add(patch);
if (patch.equals(patchID)) {
// We found the patch we are rolling back, and continue to the next patch
found = true;
} else if (found) {
proceed = false;
}
}
};
processor.process(builder.iterator());
return validHistory;
}
abstract static class HistoryProcessor implements PatchingValidationErrorHandler {
private final List<String> errors = new ArrayList<String>();
/**
* Whether the current patch is valid.
*
* @return {@code true} if the current patch is valid, {@code false} otherwise
*/
protected abstract boolean includeCurrent();
/**
* Whether we can proceed processing the next patch.
*
* @return {@code true} if the next patch can be processed, {@code false} otherwise
*/
protected abstract boolean canProceed();
/**
* Handle all errors.
*
* @param artifact the processed artifact in error
* @param state the artifact state
* @param <P>
* @param <S>
* @param {@code true} if the error can be ignored, {@code false otherwise}
*/
protected abstract <P extends PatchingArtifact.ArtifactState, S extends PatchingArtifact.ArtifactState> boolean handleError(PatchingArtifact<P, S> artifact, S state);
/**
* Callback for valid patches.
*
* @param patch the patch id
*/
protected abstract void processedPatch(String patch);
@Override
public <P extends PatchingArtifact.ArtifactState, S extends PatchingArtifact.ArtifactState> void addError(PatchingArtifact<P, S> artifact, S state) {
if (!handleError(artifact, state)) {
errors.add(PatchLogger.ROOT_LOGGER.artifactInError(state));
}
}
@Override
public <P extends PatchingArtifact.ArtifactState, S extends PatchingArtifact.ArtifactState> void addInconsistent(PatchingArtifact<P, S> artifact, S current) {
if (!handleError(artifact, current)) {
errors.add(PatchLogger.ROOT_LOGGER.inconsistentArtifact(current));
}
}
@Override
public <P extends PatchingArtifact.ArtifactState, S extends PatchingArtifact.ArtifactState> void addMissing(PatchingArtifact<P, S> artifact, S state) {
if (!handleError(artifact, state)) {
errors.add(PatchLogger.ROOT_LOGGER.missingArtifact(state));
}
}
protected void process(PatchHistoryIterator iterator) throws PatchingException {
while (iterator.hasNext() && canProceed()) {
final String patch = iterator.next(this);
if (includeCurrent()) {
processedPatch(patch);
}
}
// If there are errors, fail
if (!errors.isEmpty()) {
throw new PatchingException(errors.toString());
}
}
}
static class PatchingArtifactStateHandlers {
// A map of artifact state handlers for each artifact. Generics FTW!
private final Map<PatchingArtifact<? extends PatchingArtifact.ArtifactState, ? extends PatchingArtifact.ArtifactState>, PatchingArtifactStateHandler<? extends PatchingArtifact.ArtifactState>> handlers
= new HashMap<PatchingArtifact<? extends PatchingArtifact.ArtifactState, ? extends PatchingArtifact.ArtifactState>, PatchingArtifactStateHandler<? extends PatchingArtifact.ArtifactState>>();
<P extends PatchingArtifact.ArtifactState, S extends PatchingArtifact.ArtifactState> void put(PatchingArtifact<P, S> artifact, PatchingArtifactStateHandler<S> handler) {
assert !handlers.containsKey(artifact);
handlers.put(artifact, handler);
}
<P extends PatchingArtifact.ArtifactState, S extends PatchingArtifact.ArtifactState> PatchingArtifactStateHandler<S> get(final PatchingArtifact<P, S> artifact) {
return (PatchingArtifactStateHandler<S>) handlers.get(artifact);
}
}
}