/* * 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); } } }