001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2007-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027package org.opends.server.util;
028
029import static org.opends.messages.VersionMessages.*;
030
031import org.forgerock.i18n.LocalizableMessage;
032import org.opends.quicksetup.BuildInformation;
033
034import java.util.ArrayList;
035import java.util.Collection;
036import java.util.Collections;
037import java.util.Comparator;
038import java.util.EnumSet;
039import java.util.HashSet;
040import java.util.List;
041import java.util.Set;
042
043/**
044 * Record for version compatibility issues (also known as 'flag days') which
045 * are events associated with particular builds or builds between which upgrade
046 * or reversion may required additional steps, notification of issues, or
047 * be prohibited altogether.
048 */
049@org.opends.server.types.PublicAPI(
050     stability=org.opends.server.types.StabilityLevel.VOLATILE,
051     mayInstantiate=false,
052     mayExtend=false,
053     mayInvoke=true)
054public final class VersionCompatibilityIssue {
055
056  //***************************************************
057  //
058  //  TO DEFINE A NEW ISSUE:
059  //
060  //  Step 1:  Select (or add to) effects from the list
061  //           below that will cause the upgrade or
062  //           reversion tools to behave in particular
063  //           ways.  If you add to this list you will
064  //           likely need to update the UpgradeOracle
065  //           and ReversionOracle code.
066  //
067  //  Step 2:  [scroll down]...
068  //
069  //***************************************************
070
071  /**
072   * Effects cause the upgrade and revision tools to behave
073   * in specific ways in response to compatibility issues.
074   */
075  public enum Effect {
076
077    /**
078     * Before a reversion can take place there must be a complete
079     * data export to LDIF followed by a complete data import after
080     * the operation has completed.  Assigning this effect to an
081     * issue will cause a detailed set of instructions to appear in
082     * the reversion tool explaining how to perform the task.
083     */
084    REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED,
085
086    /**
087     * Before an upgrade can take place there must be a complete
088     * data export to LDIF followed by a complete data import after
089     * the operation has completed.  Assigning this effect to an
090     * issue will cause a detailed set of instructions to appear in
091     * the upgrade tool explaining how to perform the task.
092     */
093    UPGRADE_DATA_EXPORT_AND_REIMPORT_REQUIRED,
094
095    /**
096     * Indicates that the upgrader will show an informational message to the
097     * administrator.  Use this effect when you want to have the
098     * upgrader show the user an informational message during upgrade
099     * but the message does not dictate that an action be performed.
100     * For instance you might want to let the user know that due to
101     * a data format incompatibility, it will be more difficult to
102     * revert this build to its previous version following this upgrade.
103     *
104     * If you want the message to be scarier, use
105     * <code>UPGRADE_SHOW_WARNING_MESSAGE</code> instead.
106     */
107    UPGRADE_SHOW_INFO_MESSAGE,
108
109    /**
110     * Indicates that the reverter tool will show a message to the
111     * administrator.  Use this effect when you want to have the
112     * reverter show the user an informational message during upgrade
113     * but the message does not dictate that an action be performed.
114     *
115     * If you want the message to be scarier, use
116     * <code>REVERSION_SHOW_WARNING_MESSAGE</code> instead.
117     */
118    REVERSION_SHOW_INFO_MESSAGE,
119
120    /**
121     * Indicates that the upgrader will show a message to the
122     * administrator.  Use this effect when you want to have the
123     * upgrader show the user an informational message during upgrade
124     * but the message does not dictate that an action be performed.
125     * For instance you might want to let the user know that due to
126     * a data format incompatibility, it will be more difficult to
127     * revert this build to its previous version following this upgrade.
128     *
129     * If you want the message to be less scary, use
130     * <code>UPGRADE_SHOW_INFO_MESSAGE</code> instead.
131     */
132    UPGRADE_SHOW_WARNING_MESSAGE,
133
134    /**
135     * Indicates that the reverter tool will show a message to the
136     * administrator.  Use this effect when you want to have the
137     * reverter show the user an informational message during upgrade
138     * but the message does not dictate that an action be performed.
139     *
140     * If you want the message to be less scary, use
141     * <code>REVERSION_SHOW_INFO_MESSAGE</code> instead.
142     */
143    REVERSION_SHOW_WARNING_MESSAGE,
144
145    /**
146     * Indicates that the user needs to perform some manual action
147     * (for which there is not effect currently defined such as
148     * <code>UPGRADE_DATA_EXPORT_AND_REIMPORT_REQUIRED</code>) in order for
149     * the operation to be successful.  The action itself should
150     * be described in detail in the upgrade message.
151     */
152    UPGRADE_MANUAL_ACTION_REQUIRED,
153
154    /**
155     * Indicates that the user needs to perform some manual action
156     * (for which there is not effect currently defined such as
157     * <code>REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED</code>) in order for
158     * the operation to be successful.  The action itself should
159     * be described in detail in the reversion message.
160     */
161    REVERSION_MANUAL_ACTION_REQUIRED,
162
163    /**
164     * Indicates that it is not possible to upgrade between to builds
165     * between which lies a flag day.  The upgrader will refuse to
166     * operate in this case.
167     */
168    UPGRADE_NOT_POSSIBLE,
169
170    /**
171     * Indicates that it is not possible to revert between to builds
172     * between which lies a flag day.  The reverter will refuse to run
173     * in this case.
174     */
175    REVERSION_NOT_POSSIBLE,
176
177    /**
178     * Indicates that for some reason the server should not be restarted
179     * following a reversion.  There might be situations where the admin
180     * needs to perform some actions before the server restarts (such as
181     * the database format being incompatible and the data needing an
182     * export followed by a re-import).  This effect need not be included
183     * with <code>UPGRADE_DATA_EXPORT_AND_REIMPORT_REQUIRED</code> and
184     * <code>REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED</code> as this
185     * is assumed.
186     */
187    NO_SERVER_RESTART_FOLLOWING_REVERSION,
188
189  }
190
191  //***************************************************
192  //
193  //  TO DEFINE A NEW ISSUE:
194  //
195  // STEP 1:  [scroll up]
196  //
197  // STEP 2:  Define an cause below.  A cause must be a specific
198  //          event.  For instance 'upgrade of the database libraries'
199  //          on 12/17/2006.  A cause associates the effect you selected
200  //          in Step 1, detailed reversion and/or upgrade messages and
201  //          a unique ID.
202  //
203  //          A single issue may be apply to multiple branches of the
204  //          code-base.  For instance a single event might cause a flag
205  //          day between upgrade/reversions from 1.0 to 2.0 as well as
206  //          upgrading from 1.0 to 1.1.  Therefore you must make sure
207  //          that causes that appear in multiple branches have the same
208  //          ID.  Also, IDs should be unique among all causes in the
209  //          code-base.
210  //
211  // STEP 3:  [scroll down]
212  //
213  //***************************************************
214
215  /**
216   * Unique descriptor of an event that created a flag day for one
217   * or more versions of the OpenDJ codebase.
218   */
219  public enum Cause {
220
221    /**
222     * Incompatible changes in ds-sync-hist normalization. This causes
223     * ds-sync-hist attribute indexes to be invalidated.
224     */
225    DS_SYNC_HIST_NORMALIZATION_CHANGE_1(
226        10, // Unique ID.  See javadoc for more information.
227        INFO_7635_UPGRADE.get(),
228        INFO_7635_REVERSION.get(),
229        Effect.REVERSION_MANUAL_ACTION_REQUIRED,
230        Effect.UPGRADE_MANUAL_ACTION_REQUIRED),
231
232
233    /**
234     * We not support the revert to the previous version.
235     */
236    REVERT_NOT_SUPPORTED_1(
237        9, // Unique ID.  See javadoc for more information.
238        INFO_5278_REVERSION.get(),
239        INFO_5278_REVERSION.get(),
240        Effect.REVERSION_NOT_POSSIBLE,
241        Effect.UPGRADE_SHOW_INFO_MESSAGE),
242
243
244    /**
245     * Incompatible changes in attribute value normalization. This causes
246     * indexes to be invalidated.
247     */
248    STRINGPREP_NORMALIZATION_CHANGE_1(
249        8, // Unique ID.  See javadoc for more information.
250        INFO_5134_UPGRADE.get(),
251        INFO_5134_REVERSION.get(),
252        Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED,
253        Effect.UPGRADE_DATA_EXPORT_AND_REIMPORT_REQUIRED),
254
255    /**
256     * Incompatible changes in DN normalization. This causes dn2id and
257     * RDN / DN syntax based attribute indexes to be invalidated.
258     */
259    DN_NORMALIZATION_CHANGE_1(
260        7, // Unique ID.  See javadoc for more information.
261        INFO_3873_UPGRADE.get(),
262        INFO_3873_REVERSION.get(),
263        Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED,
264        Effect.UPGRADE_DATA_EXPORT_AND_REIMPORT_REQUIRED),
265
266    /**
267     * Incompatible changes in the backend configuration (the db directory
268     * attribute has been modified).
269     */
270    BACKEND_CONFIGURATION_CHANGE_1(
271        6, // Unique ID.  See javadoc for more information.
272        INFO_3708_UPGRADE.get(),
273        INFO_3708_REVERSION.get(),
274        Effect.REVERSION_NOT_POSSIBLE,
275        Effect.UPGRADE_NOT_POSSIBLE),
276
277    /**
278     * Incompatible changes in the cryptomanager and specially in the way
279     * replication works.  These changes were committed on several revisions
280     * and the flagday that has been chosen corresponds to revision 3294
281     * (opends 1.0.0 build 6 of 16/10/2007)
282     */
283    REPLICATION_SECURITY_CHANGE_1(
284            5, // Unique ID.  See javadoc for more information.
285            INFO_3294_UPGRADE.get(),
286            INFO_3294_REVERSION.get(),
287            Effect.REVERSION_NOT_POSSIBLE,
288            Effect.UPGRADE_NOT_POSSIBLE),
289
290    /**
291     * Incompatible property name change committed on 09/05/2007
292     * and described in the SVN log for rev 2974.
293     */
294    PROPERTY_CHANGE_1(
295            4, // Unique ID.  See javadoc for more information.
296            INFO_2974_UPGRADE.get(),
297            INFO_2974_REVERSION.get(),
298            Effect.REVERSION_NOT_POSSIBLE,
299            Effect.UPGRADE_NOT_POSSIBLE),
300
301    /**
302     * Database format change committed on 6/7/2007
303     * and described in the SVN log for rev 2049.
304     */
305    DB_FORMAT_CHANGE_2(
306            3, // Unique ID.  See javadoc for more information.
307            INFO_2049_UPGRADE.get(),
308            INFO_2049_REVERSION.get(),
309            Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED,
310            Effect.UPGRADE_SHOW_WARNING_MESSAGE),
311
312    /**
313     * Database format change committed on 4/6/2007
314     * and described in the SVN log for rev 1582.
315     */
316    DB_FORMAT_CHANGE_1(
317            2,  // Unique ID.  See javadoc for more information.
318            INFO_1582_UPGRADE.get(),
319            INFO_1582_REVERSION.get(),
320            Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED,
321            Effect.UPGRADE_SHOW_WARNING_MESSAGE),
322
323    /**
324     * Upgrade of Berkley DB library to 3.2.13 on
325     * 12/17/2006.
326     */
327    BERKLEY_UPGRADE_1(
328            1,  // Unique ID.  See javadoc for more information.
329            INFO_890_UPGRADE.get(),
330            INFO_890_REVERSION.get(),
331            Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED,
332            Effect.UPGRADE_SHOW_WARNING_MESSAGE);
333
334    /**
335     * Gets a <code>Cause</code> from its unique ID.  If no cause
336     * is associated with <code>id</code> this method returns null.
337     * @param id of a cause
338     * @return Cause with <code>id</code>
339     */
340    static Cause fromId(int id) {
341      Cause cause = null;
342      EnumSet<Cause> es = EnumSet.allOf(Cause.class);
343      for (Cause c : es) {
344        if (c.getId() == id) {
345          cause = c;
346          break;
347        }
348      }
349      return cause;
350    }
351
352    private int id;
353    private Set<Effect> effects = new HashSet<>();
354    private LocalizableMessage upgradeMsg;
355    private LocalizableMessage reversionMsg;
356
357    /**
358     * Creates a parameterized instance.
359     *
360     * @param id of this cause.  It would get very complicated to try to
361     *        deal with releases as a graph and attempting to compare
362     *        versions to see what issues apply during an upgrade/reversion
363     *        between two releases.  Therefore IDs are used by the tools
364     *        to identify issues would have already been seen during a previous
365     *        upgrade and do not need to be rehashed.
366     *        <p>
367     *        So if an issue exists in the 1.0 branch, an upgrade from 2.0
368     *        to 3.0 will suppress the issue since it would presumably already
369     *        been dealt with when 2.0 was installed or upgraded to.  Likewise
370     *        if an issue is assocated with a particular minor version (1.1 for
371     *        instance) major upgrades (1.0 to 2.0) will avoid presenting the
372     *        issue.
373     *
374     *        <ol>
375     *        <li>IDs must be unique among different causes in all branches
376     *        of the OpenDJ code.</li>
377     *
378     *        <li>Causes in different branches representing the same issue
379     *        must have identical IDs.</li>
380     *
381     *        <li>The IDs are advertised by the server when start-ds -F
382     *        is invoked.  Therefore they should be kept to as few
383     *        characters as possible.</li>
384     *        </ol>
385     *
386     * @param upgradeMessage a message to be shown to the user during an
387     *        upgrade between two different version between which this issue
388     *        lies.  This message might detail instructions for manual actions
389     *        that must be performed (when used with the
390     *        <code>UPGRADE_MANUAL_ACTION_REQUIRED</code>) or give the
391     *        user a warning message (when used with
392     *        <code>UPGRADE_SHOW_WARNING_MESSAGE</code>).  If a message is
393     *        present but no effects that would dictate how message is to
394     *        be presented <code>UPGRADE_SHOW_INFO_MESSAGE</code> is
395     *        assumed.  This parameter may also be null in which case no
396     *        action will be taken during upgrade.
397     *
398     * @param reversionMessage a message to be shown to the user during a
399     *        reversion between two different version between which this issue
400     *        lies.  This message might detail instructions for manual actions
401     *        that must be performed (when used with the
402     *        <code>REVERSION_MANUAL_ACTION_REQUIRED</code>) or give the
403     *        user a warning message (when used with
404     *        <code>REVERSION_SHOW_WARNING_MESSAGE</code>).  If a message is
405     *        present but no effects that would dictate how message is to
406     *        be presented <code>REVERSION_SHOW_INFO_MESSAGE</code> is
407     *        assumed.  This parameter may also be null in which case no
408     *        action will be taken during reversion.
409     *
410     * @param effects of this cause which cause the upgrade/reversion tools
411     *        to behave in particular ways
412     */
413    private Cause(int id, LocalizableMessage upgradeMessage, LocalizableMessage reversionMessage,
414          Effect... effects) {
415      this.id = id;
416      this.upgradeMsg = upgradeMessage;
417      this.reversionMsg = reversionMessage;
418      if (effects != null) {
419        Collections.addAll(this.effects, effects);
420      }
421    }
422
423    /**
424     * Gets the ID of this cause.
425     * @return id of this cause
426     */
427    public int getId() {
428      return this.id;
429    }
430
431    /**
432     * Gets the set of effects that cause the upgrade/reversion
433     * tools to behave in particular ways.
434     *
435     * @return set of effects
436     */
437    public Set<Effect> getEffects() {
438      return Collections.unmodifiableSet(effects);
439    }
440
441    /**
442     * Gets a localized message to be shown to the user during
443     * the upgrade process.
444     *
445     * @return a message to be shown to the user during an
446     *         upgrade between two different version between which this issue
447     *         lies.  This message might detail instructions for manual actions
448     *         that must be performed (when used with the
449     *         <code>UPGRADE_MANUAL_ACTION_REQUIRED</code>) or just give the
450     *         user useful information (when used with
451     *         <code>UPGRADE_SHOW_INFO_MESSAGE</code>)
452     */
453    public LocalizableMessage getLocalizedUpgradeMessage() {
454      return upgradeMsg;
455    }
456
457    /**
458     * Gets a localized message to be shown to the user during
459     * the reversion process.
460     *
461     * @return a message to be shown to the user during an
462     *         upgrade between two different version between which this issue
463     *         lies.  This message might detail instructions for manual actions
464     *         that must be performed (when used with the
465     *         <code>REVERSION_MANUAL_ACTION_REQUIRED</code>) or just give the
466     *         user useful information (when used with
467     *         <code>REVERSION_SHOW_INFO_MESSAGE</code>)
468     */
469    public LocalizableMessage getLocalizedReversionMessage() {
470      return reversionMsg;
471    }
472
473  }
474
475  /** Container for registered issues. */
476  private static final Set<VersionCompatibilityIssue> VERSION_COMPATIBILITY_ISSUES = new HashSet<>();
477
478  //***************************************************
479  //
480  //  TO DEFINE A NEW ISSUE:
481  //
482  // STEP 2:  [scroll up]
483  //
484  // STEP 3:  Associate the cause with a particular build.
485  //
486  // DONE
487  //
488  //***************************************************
489
490  static
491  {
492    register(Cause.DS_SYNC_HIST_NORMALIZATION_CHANGE_1, new BuildVersion(2, 4,
493        5, 7635));
494    register (Cause.REVERT_NOT_SUPPORTED_1, new BuildVersion(2,0,0,5278));
495    register(Cause.STRINGPREP_NORMALIZATION_CHANGE_1,
496            new BuildVersion(1,2,0,5134));
497    register(Cause.DN_NORMALIZATION_CHANGE_1, new BuildVersion(1, 0, 0, 3873));
498    register(Cause.BACKEND_CONFIGURATION_CHANGE_1,
499        new BuildVersion(1, 0, 0, 3708));
500    register(Cause.REPLICATION_SECURITY_CHANGE_1,
501        new BuildVersion(1, 0, 0, 3294));
502    register(Cause.PROPERTY_CHANGE_1, new BuildVersion(1, 0, 0, 3053));
503    register(Cause.DB_FORMAT_CHANGE_2, new BuildVersion(0, 9, 0, 2049));
504    register(Cause.DB_FORMAT_CHANGE_1, new BuildVersion(0, 1, 0, 1582));
505    register(Cause.BERKLEY_UPGRADE_1, new BuildVersion(0, 1, 0, 890));
506  }
507
508  private static void register(Cause cause,
509                               BuildVersion version) {
510    VERSION_COMPATIBILITY_ISSUES.add(new VersionCompatibilityIssue(cause,
511            version));
512  }
513
514  /**
515   * Gets the list of all registered issues.
516   *
517   * @return list of issues sorted by build version in which
518   *         they appear
519   */
520  public static List<VersionCompatibilityIssue> getAllEvents() {
521    List<VersionCompatibilityIssue> issueList = new ArrayList<>(VERSION_COMPATIBILITY_ISSUES);
522    Collections.sort(issueList, VERSION_COMPARATOR);
523    return Collections.unmodifiableList(issueList);
524  }
525
526  /**
527   * Gets the list of all registered issues excluding the
528   * issues specified by <code>excludeIds</code>.
529   *
530   * @param excludeIds collection of IDs representing issues
531   *        that will not be returned in the list
532   * @param current build version
533   * @param neu build version
534   *
535   * @return list of issues sorted by build version in which
536   *         they appear
537   */
538  public static List<VersionCompatibilityIssue> getEvents(
539          Collection<Integer> excludeIds, BuildInformation current,
540          BuildInformation neu)
541  {
542    if (excludeIds == null)
543    {
544      excludeIds = Collections.emptySet();
545    }
546    List<VersionCompatibilityIssue> issueList = new ArrayList<>();
547    for (VersionCompatibilityIssue evt : VERSION_COMPATIBILITY_ISSUES) {
548      if (!excludeIds.contains(evt.getCause().getId())) {
549        BuildVersion currentVersion = new BuildVersion(
550            current.getMajorVersion(), current.getMinorVersion(),
551            current.getPointVersion(), current.getRevisionNumber());
552        // If the currentVersion is newer than the issue described, then there
553        // is no problem.  This can occur for instance when we discovered a
554        // flag day too late (and we added the flag day description to the
555        // code way after the revision).
556        if (currentVersion.compareTo(evt.getVersion()) < 0)
557        {
558          issueList.add(evt);
559        }
560      }
561    }
562    Collections.sort(issueList, VERSION_COMPARATOR);
563    return Collections.unmodifiableList(issueList);
564  }
565
566  /**
567   * Returns events that have happened in between the SVN revision numbers
568   * of two different builds.  Note that this method does not necessarily
569   * return all events that are pertinent.  For instance a partilar event
570   * may have happend in a branch that we don't care about for the current
571   * upgrade.  So this method should really just be used as a fall-back
572   * in the case where we are upgrading/reverting a build that was not
573   * instrumented to return the Upgrade Event IDs using start-ds -F.
574   *
575   * @param from build from which events will be returned
576   * @return List or IncompatibleVersionEvent objects
577   */
578  public static List<VersionCompatibilityIssue> getEvents(BuildVersion from) {
579    List<VersionCompatibilityIssue> issueList = new ArrayList<>();
580    for (VersionCompatibilityIssue evt : VERSION_COMPATIBILITY_ISSUES) {
581      BuildVersion evtVer = evt.getVersion();
582      if (evtVer.compareTo(from) >= 0) {
583        issueList.add(evt);
584      }
585    }
586    Collections.sort(issueList, VERSION_COMPARATOR);
587    return issueList;
588  }
589
590  /**
591   * Comparator used to sort issues by the build version for
592   * which they apply.
593   */
594  private static final Comparator<VersionCompatibilityIssue>
595          VERSION_COMPARATOR = new Comparator<VersionCompatibilityIssue>()
596  {
597    @Override
598    public int compare(VersionCompatibilityIssue o1,
599                       VersionCompatibilityIssue o2) {
600      return o1.getVersion().compareTo(o2.getVersion());
601    }
602  };
603
604  private Cause cause;
605  private BuildVersion version;
606
607  private VersionCompatibilityIssue(Cause cause, BuildVersion version) {
608    this.cause = cause;
609    this.version = version;
610  }
611
612  /**
613   * Gets the cause of this issue.
614   * @return the cause
615   */
616  public Cause getCause() {
617    return this.cause;
618  }
619
620  /**
621   * Gets the build version for which this issue applies.
622   * @return build version
623   */
624  public BuildVersion getVersion() {
625    return this.version;
626  }
627
628  /**
629   * Retrieves a string representation of this version compatibility issue.
630   *
631   * @return  A string representation of this version compatibility issue.
632   */
633  @Override
634  public String toString() {
635    return Integer.toString(cause.getId());
636  }
637
638}