/* * Copyright (c) 2016 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.runtime.upgrade.support; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.function.Predicate; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.obiba.core.util.ComparableComparator; import org.obiba.runtime.Version; import org.obiba.runtime.upgrade.InstallStep; import org.obiba.runtime.upgrade.UpgradeException; import org.obiba.runtime.upgrade.UpgradeManager; import org.obiba.runtime.upgrade.UpgradeStep; import org.obiba.runtime.upgrade.VersionModifier; import org.obiba.runtime.upgrade.VersionProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static java.util.stream.Collectors.toList; @SuppressWarnings("UnusedDeclaration") public class DefaultUpgradeManager implements UpgradeManager { // // Constants // private static final Logger log = LoggerFactory.getLogger(DefaultUpgradeManager.class); // // Instance Variables // /** * Used to obtain and modify the current version. */ @Nonnull private VersionModifier currentVersionProvider; /** * Used to obtain the current runtime version. */ @Nonnull private VersionProvider runtimeVersionProvider; /** * Strategy that determines if the manager should go through the list of install steps instead of the list of upgrade * steps. */ @Nonnull private NewInstallationDetectionStrategy newInstallationDetectionStrategy; /** * A list of all required installation steps. */ @Nonnull private final Collection<InstallStep> installSteps = new ArrayList<>(); /** * A list of all available upgrade steps. */ @Nonnull private final Collection<UpgradeStep> upgradeSteps = new ArrayList<>(); /** * A list of listeners to be notified of step executions. */ @Nullable private List<UpgradeManagerListener> stepListeners; /** * The comparator implementation to use for comparing two versions. */ @Nonnull private Comparator<Version> versionComparator = new ComparableComparator<>(); // // Constructors // @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", justification = "XML deserialization") public DefaultUpgradeManager() { } // // UpgradeManager Methods // @SuppressWarnings("ConstantConditions") @Override public void executeUpgrade() throws UpgradeException { if(currentVersionProvider == null) throw new IllegalStateException("currentVersionProvider is required"); if(runtimeVersionProvider == null) throw new IllegalStateException("runtimeVersionProvider is required"); if(newInstallationDetectionStrategy == null) { throw new IllegalStateException("newInstallationDetectionStrategy is required"); } if(installSteps == null) throw new IllegalStateException("newInstallationDetectionStrategy is required"); if(upgradeSteps == null) throw new IllegalStateException("newInstallationDetectionStrategy is required"); // Is this a new installation, or an upgrade of an existing installation? if(newInstallationDetectionStrategy.isNewInstallation(runtimeVersionProvider)) { executeNewInstallation(); } else { upgradeExistingInstallation(); } // After completing the installation/upgrade, set the current version to the // runtime version (i.e., the version installed or upgraded to). log.info("Setting version to : {}", getRuntimeVersion()); currentVersionProvider.setVersion(getRuntimeVersion()); } private void upgradeExistingInstallation() throws UpgradeException { for(UpgradeStep step : getApplicableSteps()) { try { notifyBeforeStep(step); step.execute(getCurrentVersion()); // Update the current version. currentVersionProvider.setVersion(step.getAppliesTo()); notifyAfterStep(step); } catch(Exception e) { notifyFailedStep(step, e); throw new UpgradeException(e); } } } private void executeNewInstallation() throws UpgradeException { for(InstallStep installStep : installSteps) { try { notifyBeforeStep(installStep); installStep.execute(getCurrentVersion()); notifyAfterStep(installStep); } catch(Exception e) { notifyFailedStep(installStep, e); throw new UpgradeException(e); } } } @Override public Version getCurrentVersion() { return currentVersionProvider.getVersion(); } @Override public Version getRuntimeVersion() { return runtimeVersionProvider.getVersion(); } /** * Returns true when {@code #getCurrentVersion()} is less than {@code #getRuntimeVersion()} using {@code * #versionComparator}. */ @Override public boolean requiresUpgrade() { return versionComparator.compare(getCurrentVersion(), getRuntimeVersion()) < 0; } // // Methods // public void setCurrentVersionProvider(@Nonnull VersionModifier currentVersionProvider) { //noinspection ConstantConditions if(currentVersionProvider == null) throw new IllegalArgumentException("currentVersionProvider cannot be null"); this.currentVersionProvider = currentVersionProvider; } public void setRuntimeVersionProvider(@Nonnull VersionProvider runtimeVersionProvider) { //noinspection ConstantConditions if(runtimeVersionProvider == null) throw new IllegalArgumentException("runtimeVersionProvider cannot be null"); this.runtimeVersionProvider = runtimeVersionProvider; } public void setNewInstallationDetectionStrategy( @Nonnull NewInstallationDetectionStrategy newInstallationDetectionStrategy) { //noinspection ConstantConditions if(newInstallationDetectionStrategy == null) { throw new IllegalArgumentException("newInstallationDetectionStrategy cannot be null"); } this.newInstallationDetectionStrategy = newInstallationDetectionStrategy; } public void setInstallSteps(@Nullable Collection<InstallStep> installSteps) { if(installSteps != null) { this.installSteps.clear(); this.installSteps.addAll(installSteps); } } public void setUpgradeSteps(@Nullable Collection<UpgradeStep> upgradeSteps) { if(upgradeSteps != null) { this.upgradeSteps.clear(); this.upgradeSteps.addAll(upgradeSteps); } } public void setVersionComparator(@Nonnull Comparator<Version> versionComparator) { //noinspection ConstantConditions if(versionComparator == null) throw new IllegalArgumentException("versionComparator cannot be null"); this.versionComparator = versionComparator; } public void setStepListeners(@Nullable List<UpgradeManagerListener> stepListeners) { this.stepListeners = stepListeners; } protected void notifyBeforeStep(InstallStep step) { if(stepListeners != null) { for(UpgradeManagerListener listener : stepListeners) { listener.onBeforeStep(step); } } } protected void notifyAfterStep(InstallStep step) { if(stepListeners != null) { for(UpgradeManagerListener listener : stepListeners) { listener.onAfterStep(step); } } } protected void notifyFailedStep(InstallStep step, Exception e) { if(stepListeners != null) { for(UpgradeManagerListener listener : stepListeners) { listener.onFailedStep(step, e); } } } protected void notifyBeforeStep(UpgradeStep step) { if(stepListeners != null) { for(UpgradeManagerListener listener : stepListeners) { listener.onBeforeStep(step); } } } protected void notifyAfterStep(UpgradeStep step) { if(stepListeners != null) { for(UpgradeManagerListener listener : stepListeners) { listener.onAfterStep(step); } } } protected void notifyFailedStep(UpgradeStep step, Exception e) { if(stepListeners != null) { for(UpgradeManagerListener listener : stepListeners) { listener.onFailedStep(step, e); } } } /** * Extracts all applicable upgrade steps from the list of possible steps. An applicable step is a step instance that * has a {@code Version} that is greater than {@code Version} returned by {@code #getCurrentVersion()}, determined * using the {@code #versionComparator}. * * @return a new list containing all the applicable steps. */ @Nonnull protected List<UpgradeStep> getApplicableSteps() { Version currentVersion = getCurrentVersion(); return upgradeSteps.stream() .filter(isNewerThanCurrentVersion(currentVersion)) .filter((step) -> step.mustBeApplied(getCurrentVersion(), getRuntimeVersion())) .sorted((step1, step2) -> versionComparator.compare(step1.getAppliesTo(), step2.getAppliesTo())) .collect(toList()); } private Predicate<UpgradeStep> isNewerThanCurrentVersion(Version currentVersion) { return (step) -> versionComparator.compare(step.getAppliesTo(), currentVersion) > 0; } }