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
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2007-2008 Sun Microsystems, Inc.
026     */
027    
028    package org.opends.server.util;
029    import org.opends.messages.Message;
030    import org.opends.quicksetup.BuildInformation;
031    
032    import static org.opends.messages.VersionMessages.*;
033    
034    import java.util.Set;
035    import java.util.List;
036    import java.util.Collections;
037    import java.util.HashSet;
038    import java.util.ArrayList;
039    import java.util.EnumSet;
040    import java.util.Comparator;
041    import java.util.Collection;
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)
054    public 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 OpenDS codebase.
218       */
219      public enum Cause {
220        /**
221         * Incompatible changes in DN normalization. This causes dn2id and
222         * RDN / DN syntax based attribute indexes to be invalidated.
223         */
224        DN_NORMALIZATION_CHANGE_1(
225            7, // Unique ID.  See javadoc for more information.
226            INFO_3873_UPGRADE.get(),
227            INFO_3873_REVERSION.get(),
228            Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED,
229            Effect.UPGRADE_DATA_EXPORT_AND_REIMPORT_REQUIRED),
230    
231        /**
232         * Incompatible changes in the backend configuration (the db directory
233         * attribute has been modified).
234         */
235        BACKEND_CONFIGURATION_CHANGE_1(
236            6, // Unique ID.  See javadoc for more information.
237            INFO_3708_UPGRADE.get(),
238            INFO_3708_REVERSION.get(),
239            Effect.REVERSION_NOT_POSSIBLE,
240            Effect.UPGRADE_NOT_POSSIBLE),
241    
242        /**
243         * Incompatible changes in the cryptomanager and specially in the way
244         * replication works.  These changes were committed on several revisions
245         * and the flagday that has been chosen corresponds to revision 3294
246         * (opends 1.0.0 build 6 of 16/10/2007)
247         */
248        REPLICATION_SECURITY_CHANGE_1(
249                5, // Unique ID.  See javadoc for more information.
250                INFO_3294_UPGRADE.get(),
251                INFO_3294_REVERSION.get(),
252                Effect.REVERSION_NOT_POSSIBLE,
253                Effect.UPGRADE_NOT_POSSIBLE),
254    
255        /**
256         * Incompatible property name change committed on 09/05/2007
257         * and described in the SVN log for rev 2974.
258         */
259        PROPERTY_CHANGE_1(
260                4, // Unique ID.  See javadoc for more information.
261                INFO_2974_UPGRADE.get(),
262                INFO_2974_REVERSION.get(),
263                Effect.REVERSION_NOT_POSSIBLE,
264                Effect.UPGRADE_NOT_POSSIBLE),
265    
266        /**
267         * Database format change committed on 6/7/2007
268         * and described in the SVN log for rev 2049.
269         */
270        DB_FORMAT_CHANGE_2(
271                3, // Unique ID.  See javadoc for more information.
272                INFO_2049_UPGRADE.get(),
273                INFO_2049_REVERSION.get(),
274                Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED,
275                Effect.UPGRADE_SHOW_WARNING_MESSAGE),
276    
277        /**
278         * Database format change committed on 4/6/2007
279         * and described in the SVN log for rev 1582.
280         */
281        DB_FORMAT_CHANGE_1(
282                2,  // Unique ID.  See javadoc for more information.
283                INFO_1582_UPGRADE.get(),
284                INFO_1582_REVERSION.get(),
285                Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED,
286                Effect.UPGRADE_SHOW_WARNING_MESSAGE),
287    
288        /**
289         * Upgrade of Berkley DB library to 3.2.13 on
290         * 12/17/2006.
291         */
292        BERKLEY_UPGRADE_1(
293                1,  // Unique ID.  See javadoc for more information.
294                INFO_890_UPGRADE.get(),
295                INFO_890_REVERSION.get(),
296                Effect.REVERSION_DATA_EXPORT_AND_REIMPORT_REQUIRED,
297                Effect.UPGRADE_SHOW_WARNING_MESSAGE);
298    
299        /**
300         * Gets a <code>Cause</code> from its unique ID.  If no cause
301         * is associated with <code>id</code> this method returns null.
302         * @param id of a cause
303         * @return Cause with <code>id</code>
304         */
305        static Cause fromId(int id) {
306          Cause cause = null;
307          EnumSet<Cause> es = EnumSet.allOf(Cause.class);
308          for (Cause c : es) {
309            if (c.getId() == id) {
310              cause = c;
311              break;
312            }
313          }
314          return cause;
315        }
316    
317        private int id;
318        private Set<Effect> effects = new HashSet<Effect>();
319        private Message upgradeMsg;
320        private Message reversionMsg;
321    
322        /**
323         * Creates a parameterized instance.
324         *
325         * @param id of this cause.  It would get very complicated to try to
326         *        deal with releases as a graph and attempting to compare
327         *        versions to see what issues apply during an upgrade/reversion
328         *        between two releases.  Therefore IDs are used by the tools
329         *        to identify issues would have already been seen during a previous
330         *        upgrade and do not need to be rehashed.
331         *        <p>
332         *        So if an issue exists in the 1.0 branch, an upgrade from 2.0
333         *        to 3.0 will suppress the issue since it would presumably already
334         *        been dealt with when 2.0 was installed or upgraded to.  Likewise
335         *        if an issue is assocated with a particular minor version (1.1 for
336         *        instance) major upgrades (1.0 to 2.0) will avoid presenting the
337         *        issue.
338         *
339         *        <ol>
340         *        <li>IDs must be unique among different causes in all branches
341         *        of the OpenDS code.</li>
342         *
343         *        <li>Causes in different branches representing the same issue
344         *        must have identical IDs.</li>
345         *
346         *        <li>The IDs are advertised by the server when start-ds -F
347         *        is invoked.  Therefore they should be kept to as few
348         *        characters as possible.</li>
349         *        </ol>
350         *
351         * @param upgradeMessage a message to be shown to the user during an
352         *        upgrade between two different version between which this issue
353         *        lies.  This message might detail instructions for manual actions
354         *        that must be performed (when used with the
355         *        <code>UPGRADE_MANUAL_ACTION_REQUIRED</code>) or give the
356         *        user a warning message (when used with
357         *        <code>UPGRADE_SHOW_WARNING_MESSAGE</code>).  If a message is
358         *        present but no effects that would dictate how message is to
359         *        be presented <code>UPGRADE_SHOW_INFO_MESSAGE</code> is
360         *        assumed.  This parameter may also be null in which case no
361         *        action will be taken during upgrade.
362         *
363         * @param reversionMessage a message to be shown to the user during a
364         *        reversion between two different version between which this issue
365         *        lies.  This message might detail instructions for manual actions
366         *        that must be performed (when used with the
367         *        <code>REVERSION_MANUAL_ACTION_REQUIRED</code>) or give the
368         *        user a warning message (when used with
369         *        <code>REVERSION_SHOW_WARNING_MESSAGE</code>).  If a message is
370         *        present but no effects that would dictate how message is to
371         *        be presented <code>REVERSION_SHOW_INFO_MESSAGE</code> is
372         *        assumed.  This parameter may also be null in which case no
373         *        action will be taken during reversion.
374         *
375         * @param effects of this cause which cause the upgrade/reversion tools
376         *        to behave in particular ways
377         */
378        private Cause(int id, Message upgradeMessage, Message reversionMessage,
379              Effect... effects) {
380          this.id = id;
381          this.upgradeMsg = upgradeMessage;
382          this.reversionMsg = reversionMessage;
383          if (effects != null) {
384            for (Effect c : effects) {
385              this.effects.add(c);
386            }
387          }
388        }
389    
390        /**
391         * Gets the ID of this cause.
392         * @return id of this cause
393         */
394        public int getId() {
395          return this.id;
396        }
397    
398        /**
399         * Gets the set of effects that cause the upgrade/reversion
400         * tools to behave in particular ways.
401         *
402         * @return set of effects
403         */
404        public Set<Effect> getEffects() {
405          return Collections.unmodifiableSet(effects);
406        }
407    
408        /**
409         * Gets a localized message to be shown to the user during
410         * the upgrade process.
411         *
412         * @return a message to be shown to the user during an
413         *         upgrade between two different version between which this issue
414         *         lies.  This message might detail instructions for manual actions
415         *         that must be performed (when used with the
416         *         <code>UPGRADE_MANUAL_ACTION_REQUIRED</code>) or just give the
417         *         user useful information (when used with
418         *         <code>UPGRADE_SHOW_INFO_MESSAGE</code>)
419         */
420        public Message getLocalizedUpgradeMessage() {
421          return upgradeMsg;
422        }
423    
424        /**
425         * Gets a localized message to be shown to the user during
426         * the reversion process.
427         *
428         * @return a message to be shown to the user during an
429         *         upgrade between two different version between which this issue
430         *         lies.  This message might detail instructions for manual actions
431         *         that must be performed (when used with the
432         *         <code>REVERSION_MANUAL_ACTION_REQUIRED</code>) or just give the
433         *         user useful information (when used with
434         *         <code>REVERSION_SHOW_INFO_MESSAGE</code>)
435         */
436        public Message getLocalizedReversionMessage() {
437          return reversionMsg;
438        }
439    
440      }
441    
442      /**
443       * Container for registered issues.
444       */
445      static private final Set<VersionCompatibilityIssue>
446              VERSION_COMPATIBILITY_ISSUES =
447              new HashSet<VersionCompatibilityIssue>();
448    
449      //***************************************************
450      //
451      //  TO DEFINE A NEW ISSUE:
452      //
453      // STEP 2:  [scroll up]
454      //
455      // STEP 3:  Associate the cause with a particular build.
456      //
457      // DONE
458      //
459      //***************************************************
460    
461      static {
462        register(Cause.DN_NORMALIZATION_CHANGE_1, new BuildVersion(1, 0, 0, 3873));
463        register(Cause.BACKEND_CONFIGURATION_CHANGE_1,
464            new BuildVersion(1, 0, 0, 3708));
465        register(Cause.REPLICATION_SECURITY_CHANGE_1,
466            new BuildVersion(1, 0, 0, 3294));
467        register(Cause.PROPERTY_CHANGE_1, new BuildVersion(1, 0, 0, 3053));
468        register(Cause.DB_FORMAT_CHANGE_2, new BuildVersion(0, 9, 0, 2049));
469        register(Cause.DB_FORMAT_CHANGE_1, new BuildVersion(0, 1, 0, 1582));
470        register(Cause.BERKLEY_UPGRADE_1, new BuildVersion(0, 1, 0, 890));
471      }
472    
473      static private void register(Cause cause,
474                                   BuildVersion version) {
475        VERSION_COMPATIBILITY_ISSUES.add(new VersionCompatibilityIssue(cause,
476                version));
477      }
478    
479      /**
480       * Gets the list of all registered issues.
481       *
482       * @return list of issues sorted by build version in which
483       *         they appear
484       */
485      static public List<VersionCompatibilityIssue> getAllEvents() {
486        List<VersionCompatibilityIssue> issueList =
487                new ArrayList<VersionCompatibilityIssue>
488                        (VERSION_COMPATIBILITY_ISSUES);
489        Collections.sort(issueList, VERSION_COMPARATOR);
490        return Collections.unmodifiableList(issueList);
491      }
492    
493      /**
494       * Gets the list of all registered issues excluding the
495       * issues specified by <code>excludeIds</code>.
496       *
497       * @param excludeIds collection of IDs representing issues
498       *        that will not be returned in the list
499       * @param current build version
500       * @param neu build version
501       *
502       * @return list of issues sorted by build version in which
503       *         they appear
504       */
505      static public List<VersionCompatibilityIssue> getEvents(
506              Collection<Integer> excludeIds, BuildInformation current,
507              BuildInformation neu)
508      {
509        if (excludeIds == null) excludeIds = Collections.emptySet();
510        List<VersionCompatibilityIssue> issueList =
511                new ArrayList<VersionCompatibilityIssue>();
512        for (VersionCompatibilityIssue evt : VERSION_COMPATIBILITY_ISSUES) {
513          if (!excludeIds.contains(evt.getCause().getId())) {
514            boolean isUpgrade = neu.compareTo(current) >= 0;
515            BuildVersion currentVersion = new BuildVersion(
516                current.getMajorVersion(), current.getMinorVersion(),
517                current.getPointVersion(), current.getRevisionNumber());
518            if (isUpgrade)
519            {
520              // If the currentVersion is newer than the issue described, then there
521              // is no problem.  This can occur for instance when we discovered a
522              // flag day too late (and we added the flag day description to the
523              // code way after the revision).
524              if (currentVersion.compareTo(evt.getVersion()) < 0)
525              {
526                issueList.add(evt);
527              }
528            }
529            else
530            {
531              // If the newVersion in the reversion is newer than the issue
532              // described, then there is no problem.  This can occur for instance
533              // when we discovered a flag day too late (and we added the flag day
534              // description to the code way after the revision).
535              if (currentVersion.compareTo(evt.getVersion()) < 0)
536              {
537                issueList.add(evt);
538              }
539            }
540          }
541        }
542        Collections.sort(issueList, VERSION_COMPARATOR);
543        return Collections.unmodifiableList(issueList);
544      }
545    
546      /**
547       * Returns events that have happened in between the SVN revision numbers
548       * of two different builds.  Note that this method does not necessarily
549       * return all events that are pertinent.  For instance a partilar event
550       * may have happend in a branch that we don't care about for the current
551       * upgrade.  So this method should really just be used as a fall-back
552       * in the case where we are upgrading/reverting a build that was not
553       * instrumented to return the Upgrade Event IDs using start-ds -F.
554       *
555       * @param from build from which events will be returned
556       * @return List or IncompatibleVersionEvent objects
557       */
558      static public List<VersionCompatibilityIssue> getEvents(BuildVersion from) {
559        List<VersionCompatibilityIssue> issueList =
560                new ArrayList<VersionCompatibilityIssue>();
561        for (VersionCompatibilityIssue evt : VERSION_COMPATIBILITY_ISSUES) {
562          BuildVersion evtVer = evt.getVersion();
563          if (evtVer.compareTo(from) >= 0) {
564            issueList.add(evt);
565          }
566        }
567        Collections.sort(issueList, VERSION_COMPARATOR);
568        return issueList;
569      }
570    
571      /**
572       * Comparator used to sort issues by the build version for
573       * which they apply.
574       */
575      static private final Comparator<VersionCompatibilityIssue>
576              VERSION_COMPARATOR = new Comparator<VersionCompatibilityIssue>()
577      {
578        public int compare(VersionCompatibilityIssue o1,
579                           VersionCompatibilityIssue o2) {
580          return o1.getVersion().compareTo(o2.getVersion());
581        }
582      };
583    
584      private Cause cause;
585      private BuildVersion version;
586    
587      private VersionCompatibilityIssue(Cause cause, BuildVersion version) {
588        this.cause = cause;
589        this.version = version;
590      }
591    
592      /**
593       * Gets the cause of this issue.
594       * @return the cause
595       */
596      public Cause getCause() {
597        return this.cause;
598      }
599    
600      /**
601       * Gets the build version for which this issue applies.
602       * @return build version
603       */
604      public BuildVersion getVersion() {
605        return this.version;
606      }
607    
608      /**
609       * Retrieves a string representation of this version compatibility issue.
610       *
611       * @return  A string representation of this version compatibility issue.
612       */
613      public String toString() {
614        return Integer.toString(cause.getId());
615      }
616    
617    }