/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2007-2009 Sun Microsystems, Inc. * Portions Copyright 2011-2012 ForgeRock AS */ package org.opends.server.util; import org.opends.messages.Message; import org.opends.quicksetup.BuildInformation; import static org.opends.messages.VersionMessages.*; import java.util.Set; import java.util.List; import java.util.Collections; import java.util.HashSet; import java.util.ArrayList; import java.util.EnumSet; import java.util.Comparator; import java.util.Collection; /** * Record for version compatibility issues (also known as 'flag days') which * are events associated with particular builds or builds between which upgrade * or reversion may required additional steps, notification of issues, or * be prohibited altogether. */ @org.opends.server.types.PublicAPI( stability=org.opends.server.types.StabilityLevel.VOLATILE, mayInstantiate=false, mayExtend=false, mayInvoke=true) public final class VersionCompatibilityIssue { //*************************************************** // // TO DEFINE A NEW ISSUE: // // Step 1: Select (or add to) effects from the list // below that will cause the upgrade or // reversion tools to behave in particular // ways. If you add to this list you will // likely need to update the UpgradeOracle // and ReversionOracle code. // // Step 2: [scroll down]... // //*************************************************** /** * Effects cause the upgrade and revision tools to behave * in specific ways in response to compatibility issues. */ public enum Effect { /** * Before a reversion can take place there must be a complete * data export to LDIF followed by a complete data import after * the operation has completed. Assigning this effect to an * issue will cause a detailed set of instructions to appear in * the reversion tool explaining how to perform the task. */ REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED, /** * Before an upgrade can take place there must be a complete * data export to LDIF followed by a complete data import after * the operation has completed. Assigning this effect to an * issue will cause a detailed set of instructions to appear in * the upgrade tool explaining how to perform the task. */ UPGRADE_DATA_EXPORT_AND_REIMPORT_REQUIRED, /** * Indicates that the upgrader will show an informational message to the * administrator. Use this effect when you want to have the * upgrader show the user an informational message during upgrade * but the message does not dictate that an action be performed. * For instance you might want to let the user know that due to * a data format incompatibility, it will be more difficult to * revert this build to its previous version following this upgrade. * * If you want the message to be scarier, use * <code>UPGRADE_SHOW_WARNING_MESSAGE</code> instead. */ UPGRADE_SHOW_INFO_MESSAGE, /** * Indicates that the reverter tool will show a message to the * administrator. Use this effect when you want to have the * reverter show the user an informational message during upgrade * but the message does not dictate that an action be performed. * * If you want the message to be scarier, use * <code>REVERSION_SHOW_WARNING_MESSAGE</code> instead. */ REVERSION_SHOW_INFO_MESSAGE, /** * Indicates that the upgrader will show a message to the * administrator. Use this effect when you want to have the * upgrader show the user an informational message during upgrade * but the message does not dictate that an action be performed. * For instance you might want to let the user know that due to * a data format incompatibility, it will be more difficult to * revert this build to its previous version following this upgrade. * * If you want the message to be less scary, use * <code>UPGRADE_SHOW_INFO_MESSAGE</code> instead. */ UPGRADE_SHOW_WARNING_MESSAGE, /** * Indicates that the reverter tool will show a message to the * administrator. Use this effect when you want to have the * reverter show the user an informational message during upgrade * but the message does not dictate that an action be performed. * * If you want the message to be less scary, use * <code>REVERSION_SHOW_INFO_MESSAGE</code> instead. */ REVERSION_SHOW_WARNING_MESSAGE, /** * Indicates that the user needs to perform some manual action * (for which there is not effect currently defined such as * <code>UPGRADE_DATA_EXPORT_AND_REIMPORT_REQUIRED</code>) in order for * the operation to be successful. The action itself should * be described in detail in the upgrade message. */ UPGRADE_MANUAL_ACTION_REQUIRED, /** * Indicates that the user needs to perform some manual action * (for which there is not effect currently defined such as * <code>REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED</code>) in order for * the operation to be successful. The action itself should * be described in detail in the reversion message. */ REVERSION_MANUAL_ACTION_REQUIRED, /** * Indicates that it is not possible to upgrade between to builds * between which lies a flag day. The upgrader will refuse to * operate in this case. */ UPGRADE_NOT_POSSIBLE, /** * Indicates that it is not possible to revert between to builds * between which lies a flag day. The reverter will refuse to run * in this case. */ REVERSION_NOT_POSSIBLE, /** * Indicates that for some reason the server should not be restarted * following a reversion. There might be situations where the admin * needs to perform some actions before the server restarts (such as * the database format being incompatible and the data needing an * export followed by a re-import). This effect need not be included * with <code>UPGRADE_DATA_EXPORT_AND_REIMPORT_REQUIRED</code> and * <code>REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED</code> as this * is assumed. */ NO_SERVER_RESTART_FOLLOWING_REVERSION, } //*************************************************** // // TO DEFINE A NEW ISSUE: // // STEP 1: [scroll up] // // STEP 2: Define an cause below. A cause must be a specific // event. For instance 'upgrade of the database libraries' // on 12/17/2006. A cause associates the effect you selected // in Step 1, detailed reversion and/or upgrade messages and // a unique ID. // // A single issue may be apply to multiple branches of the // code-base. For instance a single event might cause a flag // day between upgrade/reversions from 1.0 to 2.0 as well as // upgrading from 1.0 to 1.1. Therefore you must make sure // that causes that appear in multiple branches have the same // ID. Also, IDs should be unique among all causes in the // code-base. // // STEP 3: [scroll down] // //*************************************************** /** * Unique descriptor of an event that created a flag day for one * or more versions of the OpenDJ codebase. */ public enum Cause { /** * Incompatible changes in ds-sync-hist normalization. This causes * ds-sync-hist attribute indexes to be invalidated. */ DS_SYNC_HIST_NORMALIZATION_CHANGE_1( 10, // Unique ID. See javadoc for more information. INFO_7635_UPGRADE.get(), INFO_7635_REVERSION.get(), Effect.REVERSION_MANUAL_ACTION_REQUIRED, Effect.UPGRADE_MANUAL_ACTION_REQUIRED), /** * We not support the revert to the previous version. */ REVERT_NOT_SUPPORTED_1( 9, // Unique ID. See javadoc for more information. INFO_5278_REVERSION.get(), INFO_5278_REVERSION.get(), Effect.REVERSION_NOT_POSSIBLE, Effect.UPGRADE_SHOW_INFO_MESSAGE), /** * Incompatible changes in attribute value normalization. This causes * indexes to be invalidated. */ STRINGPREP_NORMALIZATION_CHANGE_1( 8, // Unique ID. See javadoc for more information. INFO_5134_UPGRADE.get(), INFO_5134_REVERSION.get(), Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED, Effect.UPGRADE_DATA_EXPORT_AND_REIMPORT_REQUIRED), /** * Incompatible changes in DN normalization. This causes dn2id and * RDN / DN syntax based attribute indexes to be invalidated. */ DN_NORMALIZATION_CHANGE_1( 7, // Unique ID. See javadoc for more information. INFO_3873_UPGRADE.get(), INFO_3873_REVERSION.get(), Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED, Effect.UPGRADE_DATA_EXPORT_AND_REIMPORT_REQUIRED), /** * Incompatible changes in the backend configuration (the db directory * attribute has been modified). */ BACKEND_CONFIGURATION_CHANGE_1( 6, // Unique ID. See javadoc for more information. INFO_3708_UPGRADE.get(), INFO_3708_REVERSION.get(), Effect.REVERSION_NOT_POSSIBLE, Effect.UPGRADE_NOT_POSSIBLE), /** * Incompatible changes in the cryptomanager and specially in the way * replication works. These changes were committed on several revisions * and the flagday that has been chosen corresponds to revision 3294 * (opends 1.0.0 build 6 of 16/10/2007) */ REPLICATION_SECURITY_CHANGE_1( 5, // Unique ID. See javadoc for more information. INFO_3294_UPGRADE.get(), INFO_3294_REVERSION.get(), Effect.REVERSION_NOT_POSSIBLE, Effect.UPGRADE_NOT_POSSIBLE), /** * Incompatible property name change committed on 09/05/2007 * and described in the SVN log for rev 2974. */ PROPERTY_CHANGE_1( 4, // Unique ID. See javadoc for more information. INFO_2974_UPGRADE.get(), INFO_2974_REVERSION.get(), Effect.REVERSION_NOT_POSSIBLE, Effect.UPGRADE_NOT_POSSIBLE), /** * Database format change committed on 6/7/2007 * and described in the SVN log for rev 2049. */ DB_FORMAT_CHANGE_2( 3, // Unique ID. See javadoc for more information. INFO_2049_UPGRADE.get(), INFO_2049_REVERSION.get(), Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED, Effect.UPGRADE_SHOW_WARNING_MESSAGE), /** * Database format change committed on 4/6/2007 * and described in the SVN log for rev 1582. */ DB_FORMAT_CHANGE_1( 2, // Unique ID. See javadoc for more information. INFO_1582_UPGRADE.get(), INFO_1582_REVERSION.get(), Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED, Effect.UPGRADE_SHOW_WARNING_MESSAGE), /** * Upgrade of Berkley DB library to 3.2.13 on * 12/17/2006. */ BERKLEY_UPGRADE_1( 1, // Unique ID. See javadoc for more information. INFO_890_UPGRADE.get(), INFO_890_REVERSION.get(), Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED, Effect.UPGRADE_SHOW_WARNING_MESSAGE); /** * Gets a <code>Cause</code> from its unique ID. If no cause * is associated with <code>id</code> this method returns null. * @param id of a cause * @return Cause with <code>id</code> */ static Cause fromId(int id) { Cause cause = null; EnumSet<Cause> es = EnumSet.allOf(Cause.class); for (Cause c : es) { if (c.getId() == id) { cause = c; break; } } return cause; } private int id; private Set<Effect> effects = new HashSet<Effect>(); private Message upgradeMsg; private Message reversionMsg; /** * Creates a parameterized instance. * * @param id of this cause. It would get very complicated to try to * deal with releases as a graph and attempting to compare * versions to see what issues apply during an upgrade/reversion * between two releases. Therefore IDs are used by the tools * to identify issues would have already been seen during a previous * upgrade and do not need to be rehashed. * <p> * So if an issue exists in the 1.0 branch, an upgrade from 2.0 * to 3.0 will suppress the issue since it would presumably already * been dealt with when 2.0 was installed or upgraded to. Likewise * if an issue is assocated with a particular minor version (1.1 for * instance) major upgrades (1.0 to 2.0) will avoid presenting the * issue. * * <ol> * <li>IDs must be unique among different causes in all branches * of the OpenDJ code.</li> * * <li>Causes in different branches representing the same issue * must have identical IDs.</li> * * <li>The IDs are advertised by the server when start-ds -F * is invoked. Therefore they should be kept to as few * characters as possible.</li> * </ol> * * @param upgradeMessage a message to be shown to the user during an * upgrade between two different version between which this issue * lies. This message might detail instructions for manual actions * that must be performed (when used with the * <code>UPGRADE_MANUAL_ACTION_REQUIRED</code>) or give the * user a warning message (when used with * <code>UPGRADE_SHOW_WARNING_MESSAGE</code>). If a message is * present but no effects that would dictate how message is to * be presented <code>UPGRADE_SHOW_INFO_MESSAGE</code> is * assumed. This parameter may also be null in which case no * action will be taken during upgrade. * * @param reversionMessage a message to be shown to the user during a * reversion between two different version between which this issue * lies. This message might detail instructions for manual actions * that must be performed (when used with the * <code>REVERSION_MANUAL_ACTION_REQUIRED</code>) or give the * user a warning message (when used with * <code>REVERSION_SHOW_WARNING_MESSAGE</code>). If a message is * present but no effects that would dictate how message is to * be presented <code>REVERSION_SHOW_INFO_MESSAGE</code> is * assumed. This parameter may also be null in which case no * action will be taken during reversion. * * @param effects of this cause which cause the upgrade/reversion tools * to behave in particular ways */ private Cause(int id, Message upgradeMessage, Message reversionMessage, Effect... effects) { this.id = id; this.upgradeMsg = upgradeMessage; this.reversionMsg = reversionMessage; if (effects != null) { for (Effect c : effects) { this.effects.add(c); } } } /** * Gets the ID of this cause. * @return id of this cause */ public int getId() { return this.id; } /** * Gets the set of effects that cause the upgrade/reversion * tools to behave in particular ways. * * @return set of effects */ public Set<Effect> getEffects() { return Collections.unmodifiableSet(effects); } /** * Gets a localized message to be shown to the user during * the upgrade process. * * @return a message to be shown to the user during an * upgrade between two different version between which this issue * lies. This message might detail instructions for manual actions * that must be performed (when used with the * <code>UPGRADE_MANUAL_ACTION_REQUIRED</code>) or just give the * user useful information (when used with * <code>UPGRADE_SHOW_INFO_MESSAGE</code>) */ public Message getLocalizedUpgradeMessage() { return upgradeMsg; } /** * Gets a localized message to be shown to the user during * the reversion process. * * @return a message to be shown to the user during an * upgrade between two different version between which this issue * lies. This message might detail instructions for manual actions * that must be performed (when used with the * <code>REVERSION_MANUAL_ACTION_REQUIRED</code>) or just give the * user useful information (when used with * <code>REVERSION_SHOW_INFO_MESSAGE</code>) */ public Message getLocalizedReversionMessage() { return reversionMsg; } } /** * Container for registered issues. */ static private final Set<VersionCompatibilityIssue> VERSION_COMPATIBILITY_ISSUES = new HashSet<VersionCompatibilityIssue>(); //*************************************************** // // TO DEFINE A NEW ISSUE: // // STEP 2: [scroll up] // // STEP 3: Associate the cause with a particular build. // // DONE // //*************************************************** static { register(Cause.DS_SYNC_HIST_NORMALIZATION_CHANGE_1, new BuildVersion(2, 4, 5, 7635)); register (Cause.REVERT_NOT_SUPPORTED_1, new BuildVersion(2,0,0,5278)); register(Cause.STRINGPREP_NORMALIZATION_CHANGE_1, new BuildVersion(1,2,0,5134)); register(Cause.DN_NORMALIZATION_CHANGE_1, new BuildVersion(1, 0, 0, 3873)); register(Cause.BACKEND_CONFIGURATION_CHANGE_1, new BuildVersion(1, 0, 0, 3708)); register(Cause.REPLICATION_SECURITY_CHANGE_1, new BuildVersion(1, 0, 0, 3294)); register(Cause.PROPERTY_CHANGE_1, new BuildVersion(1, 0, 0, 3053)); register(Cause.DB_FORMAT_CHANGE_2, new BuildVersion(0, 9, 0, 2049)); register(Cause.DB_FORMAT_CHANGE_1, new BuildVersion(0, 1, 0, 1582)); register(Cause.BERKLEY_UPGRADE_1, new BuildVersion(0, 1, 0, 890)); } static private void register(Cause cause, BuildVersion version) { VERSION_COMPATIBILITY_ISSUES.add(new VersionCompatibilityIssue(cause, version)); } /** * Gets the list of all registered issues. * * @return list of issues sorted by build version in which * they appear */ static public List<VersionCompatibilityIssue> getAllEvents() { List<VersionCompatibilityIssue> issueList = new ArrayList<VersionCompatibilityIssue> (VERSION_COMPATIBILITY_ISSUES); Collections.sort(issueList, VERSION_COMPARATOR); return Collections.unmodifiableList(issueList); } /** * Gets the list of all registered issues excluding the * issues specified by <code>excludeIds</code>. * * @param excludeIds collection of IDs representing issues * that will not be returned in the list * @param current build version * @param neu build version * * @return list of issues sorted by build version in which * they appear */ static public List<VersionCompatibilityIssue> getEvents( Collection<Integer> excludeIds, BuildInformation current, BuildInformation neu) { if (excludeIds == null) excludeIds = Collections.emptySet(); List<VersionCompatibilityIssue> issueList = new ArrayList<VersionCompatibilityIssue>(); for (VersionCompatibilityIssue evt : VERSION_COMPATIBILITY_ISSUES) { if (!excludeIds.contains(evt.getCause().getId())) { boolean isUpgrade = neu.compareTo(current) >= 0; BuildVersion currentVersion = new BuildVersion( current.getMajorVersion(), current.getMinorVersion(), current.getPointVersion(), current.getRevisionNumber()); if (isUpgrade) { // If the currentVersion is newer than the issue described, then there // is no problem. This can occur for instance when we discovered a // flag day too late (and we added the flag day description to the // code way after the revision). if (currentVersion.compareTo(evt.getVersion()) < 0) { issueList.add(evt); } } else { // If the newVersion in the reversion is newer than the issue // described, then there is no problem. This can occur for instance // when we discovered a flag day too late (and we added the flag day // description to the code way after the revision). if (currentVersion.compareTo(evt.getVersion()) < 0) { issueList.add(evt); } } } } Collections.sort(issueList, VERSION_COMPARATOR); return Collections.unmodifiableList(issueList); } /** * Returns events that have happened in between the SVN revision numbers * of two different builds. Note that this method does not necessarily * return all events that are pertinent. For instance a partilar event * may have happend in a branch that we don't care about for the current * upgrade. So this method should really just be used as a fall-back * in the case where we are upgrading/reverting a build that was not * instrumented to return the Upgrade Event IDs using start-ds -F. * * @param from build from which events will be returned * @return List or IncompatibleVersionEvent objects */ static public List<VersionCompatibilityIssue> getEvents(BuildVersion from) { List<VersionCompatibilityIssue> issueList = new ArrayList<VersionCompatibilityIssue>(); for (VersionCompatibilityIssue evt : VERSION_COMPATIBILITY_ISSUES) { BuildVersion evtVer = evt.getVersion(); if (evtVer.compareTo(from) >= 0) { issueList.add(evt); } } Collections.sort(issueList, VERSION_COMPARATOR); return issueList; } /** * Comparator used to sort issues by the build version for * which they apply. */ static private final Comparator<VersionCompatibilityIssue> VERSION_COMPARATOR = new Comparator<VersionCompatibilityIssue>() { public int compare(VersionCompatibilityIssue o1, VersionCompatibilityIssue o2) { return o1.getVersion().compareTo(o2.getVersion()); } }; private Cause cause; private BuildVersion version; private VersionCompatibilityIssue(Cause cause, BuildVersion version) { this.cause = cause; this.version = version; } /** * Gets the cause of this issue. * @return the cause */ public Cause getCause() { return this.cause; } /** * Gets the build version for which this issue applies. * @return build version */ public BuildVersion getVersion() { return this.version; } /** * Retrieves a string representation of this version compatibility issue. * * @return A string representation of this version compatibility issue. */ public String toString() { return Integer.toString(cause.getId()); } }