001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Portions Copyright 2013-2017 ForgeRock AS.
015 */
016package org.opends.server.tools.upgrade;
017
018import java.io.File;
019import java.io.FileWriter;
020import java.io.IOException;
021import java.util.Arrays;
022import java.util.LinkedList;
023import java.util.List;
024import java.util.NavigableMap;
025import java.util.TreeMap;
026
027import org.forgerock.i18n.LocalizableMessage;
028import org.forgerock.i18n.slf4j.LocalizedLogger;
029import org.opends.server.core.LockFileManager;
030import org.opends.server.tools.upgrade.UpgradeTasks.UpgradeCondition;
031import org.opends.server.util.BuildVersion;
032
033import com.forgerock.opendj.cli.ClientException;
034import com.forgerock.opendj.cli.ReturnCode;
035
036import static com.forgerock.opendj.cli.Utils.*;
037import static javax.security.auth.callback.ConfirmationCallback.*;
038import static javax.security.auth.callback.TextOutputCallback.WARNING;
039
040import static org.opends.messages.ToolMessages.*;
041import static org.opends.server.schema.SchemaConstants.*;
042import static org.opends.server.tools.upgrade.FormattedNotificationCallback.*;
043import static org.opends.server.tools.upgrade.LicenseFile.*;
044import static org.opends.server.tools.upgrade.UpgradeTasks.*;
045import static org.opends.server.tools.upgrade.UpgradeUtils.batDirectory;
046import static org.opends.server.tools.upgrade.UpgradeUtils.binDirectory;
047import static org.opends.server.tools.upgrade.UpgradeUtils.instanceContainsJeBackends;
048import static org.opends.server.tools.upgrade.UpgradeUtils.libDirectory;
049import static org.opends.server.util.StaticUtils.*;
050
051/**
052 * This class contains the table of upgrade tasks that need performing when
053 * upgrading from one version to another.
054 */
055public final class Upgrade
056{
057  /** Upgrade's logger. */
058  private static LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
059
060  /** Upgrade supports version from 2.4.5. */
061  private static BuildVersion UPGRADE_SUPPORTS_VERSION_FROM = BuildVersion.valueOf("2.4.5.0000");
062
063  /** The success exit code value. */
064  static final int EXIT_CODE_SUCCESS = 0;
065  /** The error exit code value. */
066  static final int EXIT_CODE_ERROR = 1;
067
068  private static final String LOCAL_DB_BACKEND_OBJECT_CLASS = "ds-cfg-local-db-backend";
069  private static final String JE_BACKEND_OBJECT_CLASS = "ds-cfg-je-backend";
070
071  /** If the upgrade contains some post upgrade tasks to do. */
072  private static boolean hasPostUpgradeTask;
073
074  /** If the upgrade script should exit with error code (useful for warnings) */
075  private static boolean exitWithErrorCode;
076
077  /** Developers should register upgrade tasks below. */
078  private static final NavigableMap<BuildVersion, List<UpgradeTask>> TASKS = new TreeMap<>();
079  private static final List<UpgradeTask> MANDATORY_TASKS = new LinkedList<>();
080
081  static
082  {
083    // @formatter:off
084    /** See OPENDJ-2716 */
085    register("2.5.0",
086        newAttributeTypes(INFO_UPGRADE_TASK_2716_1_SUMMARY.get(),
087        "03-changelog.ldif", "1.3.6.1.4.1.36733.2.1.1.6"), // includedAttributes
088        newObjectClasses(INFO_UPGRADE_TASK_2716_2_SUMMARY.get(),
089        "03-changelog.ldif", "2.16.840.1.113730.3.2.1")); // changeLogEntry
090
091    register("2.5.0",
092        modifyConfigEntry(INFO_UPGRADE_TASK_6869_SUMMARY.get(),
093        "(objectClass=ds-cfg-collation-matching-rule)",
094        "add: ds-cfg-collation",
095        "ds-cfg-collation: de:1.3.6.1.4.1.42.2.27.9.4.28.1",
096        "ds-cfg-collation: de-DE:1.3.6.1.4.1.42.2.27.9.4.28.1",
097        "-",
098        "delete: ds-cfg-collation",
099        "ds-cfg-collation: de:1.3.6.1.4.1.142.2.27.9.4.28.1",
100        "ds-cfg-collation: de-DE:1.3.6.1.4.1.142.2.27.9.4.28.1"));
101
102    register("2.5.0",
103        modifyConfigEntry(INFO_UPGRADE_TASK_7192_SUMMARY.get(),
104        "(objectClass=ds-cfg-password-policy)",
105        "add: objectClass",
106        "objectClass: ds-cfg-authentication-policy",
107        "-",
108        "add: ds-cfg-java-class",
109        "ds-cfg-java-class: org.opends.server.core.PasswordPolicyFactory"));
110
111    register("2.5.0",
112        modifyConfigEntry(INFO_UPGRADE_TASK_7364_SUMMARY.get(),
113        "(ds-cfg-java-class=org.opends.server.loggers.TextAuditLogPublisher)",
114        "add: objectClass",
115        "objectClass: ds-cfg-file-based-audit-log-publisher",
116        "-",
117        "delete: objectClass",
118        "objectClass: ds-cfg-file-based-access-log-publisher"));
119
120    register("2.5.0",
121        renameSnmpSecurityConfig(INFO_UPGRADE_TASK_7466_SUMMARY.get()));
122
123    register("2.5.0",
124        newAttributeTypes(INFO_UPGRADE_TASK_7748_1_SUMMARY.get(),
125        "00-core.ldif", "1.3.6.1.4.1.36733.2.1.1.59"), //etag
126        addConfigEntry(INFO_UPGRADE_TASK_7748_2_SUMMARY.get(),
127        "dn: cn=etag,cn=Virtual Attributes,cn=config",
128        "changetype: add",
129        "objectClass: top",
130        "objectClass: ds-cfg-virtual-attribute",
131        "objectClass: ds-cfg-entity-tag-virtual-attribute",
132        "cn: etag",
133        "ds-cfg-java-class: org.opends.server.extensions."
134            + "EntityTagVirtualAttributeProvider",
135        "ds-cfg-enabled: true",
136        "ds-cfg-attribute-type: etag",
137        "ds-cfg-conflict-behavior: real-overrides-virtual",
138        "ds-cfg-checksum-algorithm: adler-32",
139        "ds-cfg-excluded-attribute: ds-sync-hist"));
140
141    register("2.5.0",
142        addConfigEntry(INFO_UPGRADE_TASK_7834_SUMMARY.get(),
143        "dn: cn=Password Expiration Time,cn=Virtual Attributes,cn=config",
144        "changetype: add",
145        "objectClass: top",
146        "objectClass: ds-cfg-virtual-attribute",
147        "objectClass: ds-cfg-password-expiration-time-virtual-attribute",
148        "cn: Password Expiration Time",
149        "ds-cfg-java-class: org.opends.server.extensions."
150            + "PasswordExpirationTimeVirtualAttributeProvider",
151        "ds-cfg-enabled: true",
152        "ds-cfg-attribute-type: ds-pwp-password-expiration-time",
153        "ds-cfg-conflict-behavior: virtual-overrides-real"));
154
155    register("2.5.0",
156        modifyConfigEntry(INFO_UPGRADE_TASK_7979_SUMMARY.get(),
157        "(ds-cfg-java-class=org.opends.server.schema.CertificateSyntax)",
158        "add: objectClass",
159        "objectClass: ds-cfg-certificate-attribute-syntax",
160        "-",
161        "add: ds-cfg-strict-format",
162        "ds-cfg-strict-format: false"));
163
164    register("2.6.0",
165        modifyConfigEntry(INFO_UPGRADE_TASK_8124_SUMMARY.get(),
166        "(ds-cfg-java-class=org.opends.server.schema.JPEGSyntax)",
167        "add: objectClass",
168        "objectClass: ds-cfg-jpeg-attribute-syntax",
169        "-",
170        "add: ds-cfg-strict-format",
171        "ds-cfg-strict-format: false"));
172
173    register("2.6.0",
174        modifyConfigEntry(INFO_UPGRADE_TASK_8133_SUMMARY.get(),
175        "(ds-cfg-java-class=org.opends.server.schema.CountryStringSyntax)",
176        "add: objectClass",
177        "objectClass: ds-cfg-country-string-attribute-syntax",
178        "-",
179        "add: ds-cfg-strict-format",
180        "ds-cfg-strict-format: false"));
181
182    register("2.6.0",
183        requireConfirmation(INFO_UPGRADE_TASK_8214_DESCRIPTION.get(), YES,
184            modifyConfigEntry(INFO_UPGRADE_TASK_8214_SUMMARY.get(),
185                "(ds-cfg-java-class=org.opends.server.extensions.IsMemberOfVirtualAttributeProvider)",
186                "add: ds-cfg-filter",
187                "ds-cfg-filter: (|(objectClass=person)(objectClass=groupOfNames)"
188                    + "(objectClass=groupOfUniqueNames)(objectClass=groupOfEntries))",
189                "-",
190                "delete: ds-cfg-filter",
191                "ds-cfg-filter: (objectClass=person)")));
192
193    register("2.6.0",
194        modifyConfigEntry(INFO_UPGRADE_TASK_8387_SUMMARY.get(),
195        "(objectClass=ds-cfg-dictionary-password-validator)",
196        "add: ds-cfg-check-substrings",
197        "ds-cfg-check-substrings: false"));
198
199    register("2.6.0",
200        modifyConfigEntry(INFO_UPGRADE_TASK_8389_SUMMARY.get(),
201        "(objectClass=ds-cfg-attribute-value-password-validator)",
202        "add: ds-cfg-check-substrings",
203        "ds-cfg-check-substrings: false"));
204
205    register("2.6.0",
206        addConfigEntry(INFO_UPGRADE_TASK_8487_SUMMARY.get(),
207        "dn: cn=PBKDF2,cn=Password Storage Schemes,cn=config",
208        "changetype: add",
209        "objectClass: top",
210        "objectClass: ds-cfg-password-storage-scheme",
211        "objectClass: ds-cfg-pbkdf2-password-storage-scheme",
212        "cn: PBKDF2",
213        "ds-cfg-java-class: org.opends.server.extensions."
214            + "PBKDF2PasswordStorageScheme",
215        "ds-cfg-enabled: true"));
216
217    register("2.6.0",
218        addConfigFile("http-config.json"),
219        addConfigEntry(INFO_UPGRADE_TASK_8613_SUMMARY.get(),
220        "dn: cn=HTTP Connection Handler,cn=Connection Handlers,cn=config",
221        "changetype: add",
222        "objectClass: ds-cfg-http-connection-handler",
223        "objectClass: ds-cfg-connection-handler",
224        "objectClass: top",
225        "ds-cfg-listen-port: 8080",
226        "cn: HTTP Connection Handler",
227        "ds-cfg-max-blocked-write-time-limit: 2 minutes",
228        "ds-cfg-ssl-client-auth-policy: optional",
229        "ds-cfg-use-tcp-keep-alive: true",
230        "ds-cfg-max-request-size: 5 megabytes",
231        "ds-cfg-use-tcp-no-delay: true",
232        "ds-cfg-allow-tcp-reuse-address: true",
233        "ds-cfg-accept-backlog: 128",
234        "ds-cfg-authentication-required: true",
235        "ds-cfg-buffer-size: 4096 bytes",
236        "ds-cfg-config-file: config/http-config.json",
237        "ds-cfg-listen-address: 0.0.0.0",
238        "ds-cfg-java-class: " +
239          "org.opends.server.protocols.http.HTTPConnectionHandler",
240        "ds-cfg-keep-stats: true",
241        "ds-cfg-ssl-cert-nickname: server-cert",
242        "ds-cfg-use-ssl: false",
243        "ds-cfg-enabled: false"));
244
245    register("2.6.0",
246        addConfigEntry(INFO_UPGRADE_TASK_8832_SUMMARY.get(),
247        "dn: cn=File-Based HTTP Access Logger,cn=Loggers,cn=config",
248        "changetype: add",
249        "objectClass: ds-cfg-file-based-http-access-log-publisher",
250        "objectClass: top",
251        "objectClass: ds-cfg-http-access-log-publisher",
252        "objectClass: ds-cfg-log-publisher",
253        "cn: File-Based HTTP Access Logger",
254        "ds-cfg-java-class: " +
255          "org.opends.server.loggers.TextHTTPAccessLogPublisher",
256        "ds-cfg-asynchronous: true",
257        "ds-cfg-log-file: logs/http-access",
258        "ds-cfg-rotation-policy: " +
259          "cn=24 Hours Time Limit Rotation Policy," +
260          "cn=Log Rotation Policies,cn=config",
261        "ds-cfg-rotation-policy: " +
262          "cn=Size Limit Rotation Policy,cn=Log Rotation Policies,cn=config",
263        "ds-cfg-retention-policy: " +
264          "cn=File Count Retention Policy,cn=Log Retention Policies,cn=config",
265        "ds-cfg-log-file-permissions: 640",
266        "ds-cfg-enabled: false"));
267
268    register("2.6.0",
269        newAttributeTypes(INFO_UPGRADE_TASK_8985_1_SUMMARY.get(),
270        "00-core.ldif", "1.2.840.113549.1.9.1"), // emailAddress
271        modifyConfigEntry(INFO_UPGRADE_TASK_8985_2_SUMMARY.get(),
272        "&(ds-cfg-java-class=org.opends.server.extensions." +
273        "SubjectAttributeToUserAttributeCertificateMapper)" +
274        "(ds-cfg-subject-attribute-mapping=e:mail)",
275        "delete:ds-cfg-subject-attribute-mapping",
276        "ds-cfg-subject-attribute-mapping: e:mail",
277        "-",
278        "add:ds-cfg-subject-attribute-mapping",
279        "ds-cfg-subject-attribute-mapping: emailAddress:mail"));
280
281    /** See OPENDJ-992 */
282    register("2.6.0",
283        regressionInVersion("2.5.0",
284            rebuildIndexesNamed(INFO_UPGRADE_TASK_9013_DESCRIPTION.get(),
285                "ds-sync-hist")));
286
287    /** See OPENDJ-1284 */
288    register("2.8.0", // userCertificate OID / cACertificate OID
289        newAttributeTypes(INFO_UPGRADE_TASK_10133_1_SUMMARY.get(),
290        "00-core.ldif", "2.5.4.36", "2.5.4.37"),
291        addConfigEntry(INFO_UPGRADE_TASK_10133_2_SUMMARY.get(),
292        "dn: cn=Certificate Exact Matching Rule,cn=Matching Rules,cn=config",
293        "changetype: add",
294        "objectClass: top",
295        "objectClass: ds-cfg-matching-rule",
296        "objectClass: ds-cfg-equality-matching-rule",
297        "cn: Certificate Exact Matching Rule",
298        "ds-cfg-java-class: "
299            + "org.opends.server.schema.CertificateExactMatchingRuleFactory",
300        "ds-cfg-enabled: true"));
301
302
303    /** See OPENDJ-1295 */
304    register("2.8.0",
305        copySchemaFile("03-pwpolicyextension.ldif"));
306
307    /** See OPENDJ-1490 and OPENDJ-1454 */
308    register("2.8.0",
309        deleteConfigEntry(INFO_UPGRADE_TASK_10733_1_SUMMARY.get(),
310        "dn: ds-cfg-backend-id=replicationChanges,cn=Backends,cn=config"),
311        modifyConfigEntry(INFO_UPGRADE_TASK_10733_2_SUMMARY.get(),
312        "(objectClass=ds-cfg-dsee-compat-access-control-handler)",
313        "delete: ds-cfg-global-aci",
314        "ds-cfg-global-aci: "
315            + "(target=\"ldap:///dc=replicationchanges\")"
316            + "(targetattr=\"*\")"
317            + "(version 3.0; acl \"Replication backend access\"; "
318            + "deny (all) userdn=\"ldap:///anyone\";)"));
319
320    /** See OPENDJ-1351 */
321    register("2.8.0",
322        modifyConfigEntry(INFO_UPGRADE_TASK_10820_SUMMARY.get(),
323        "(objectClass=ds-cfg-root-dn)",
324        "add: ds-cfg-default-root-privilege-name",
325        "ds-cfg-default-root-privilege-name: changelog-read"));
326
327    /** See OPENDJ-1580 */
328    register("2.8.0",
329        addConfigEntry(INFO_UPGRADE_TASK_10908_SUMMARY.get(),
330            "dn: cn=PKCS5S2,cn=Password Storage Schemes,cn=config",
331            "changetype: add",
332            "objectClass: top",
333            "objectClass: ds-cfg-password-storage-scheme",
334            "objectClass: ds-cfg-pkcs5s2-password-storage-scheme",
335            "cn: PKCS5S2",
336            "ds-cfg-java-class: org.opends.server.extensions.PKCS5S2PasswordStorageScheme",
337            "ds-cfg-enabled: true"));
338
339    /** See OPENDJ-1322 and OPENDJ-1067 */
340    register("2.8.0",
341        rerunJavaPropertiesTool(INFO_UPGRADE_TASK_9206_SUMMARY.get()));
342
343    register("2.8.0",
344        modifyConfigEntry(INFO_UPGRADE_TASK_10214_SUMMARY.get(),
345          "(ds-cfg-java-class=org.opends.server.loggers.debug.TextDebugLogPublisher)",
346          "delete:ds-cfg-java-class",
347          "-",
348          "add:ds-cfg-java-class",
349          "ds-cfg-java-class: org.opends.server.loggers.TextDebugLogPublisher"));
350
351    register("2.8.0",
352        modifyConfigEntry(INFO_UPGRADE_TASK_10232_SUMMARY.get(),
353          "(objectclass=ds-cfg-file-based-debug-log-publisher)",
354          "delete:ds-cfg-default-debug-level"));
355
356    register("2.8.0",
357        modifyConfigEntry(INFO_UPGRADE_TASK_10329_SUMMARY.get(),
358            "&(objectclass=ds-cfg-file-based-error-log-publisher)(cn=File-Based Error Logger)",
359            "delete:ds-cfg-default-severity",
360            "ds-cfg-default-severity: severe-warning",
361            "ds-cfg-default-severity: severe-error",
362            "ds-cfg-default-severity: fatal-error",
363            "-",
364            "add:ds-cfg-default-severity",
365            "ds-cfg-default-severity: error",
366            "ds-cfg-default-severity: warning"
367            ));
368
369    register("2.8.0",
370        modifyConfigEntry(INFO_UPGRADE_TASK_10339_SUMMARY.get(),
371            "&(objectclass=ds-cfg-file-based-error-log-publisher)(cn=Replication Repair Logger)",
372            "delete:ds-cfg-override-severity",
373             "-",
374             "add:ds-cfg-override-severity",
375             "ds-cfg-override-severity: SYNC=INFO,ERROR,WARNING,NOTICE"));
376
377    /** See OPENDJ-1545 */
378    register("2.8.0",
379        deleteConfigEntry(INFO_UPGRADE_TASK_11237_1_SUMMARY.get(),
380            "dn: cn=Network Groups,cn=config"),
381        deleteConfigEntry(INFO_UPGRADE_TASK_11237_2_SUMMARY.get(),
382            "dn: cn=Workflows,cn=config"),
383        deleteConfigEntry(INFO_UPGRADE_TASK_11237_3_SUMMARY.get(),
384            "dn: cn=Workflow Elements,cn=config"));
385    register("2.8.0",
386        deleteConfigEntry(INFO_UPGRADE_TASK_11239_SUMMARY.get(),
387            "dn: cn=Network Group,cn=Plugins,cn=config"));
388    register("2.8.0",
389        deleteConfigEntry(INFO_UPGRADE_TASK_11339_SUMMARY.get(),
390            "dn: cn=Extensions,cn=config"));
391
392    /** See OPENDJ-1701 */
393    register("2.8.0",
394        deleteConfigEntry(INFO_UPGRADE_TASK_11476_SUMMARY.get(),
395            "dn: cn=File System,cn=Entry Caches,cn=config"));
396
397    /** See OPENDJ-1869 */
398    register("2.8.0",
399        modifyConfigEntry(INFO_UPGRADE_TASK_12226_SUMMARY.get(),
400            "(objectclass=ds-cfg-root-config)",
401            "delete: ds-cfg-entry-cache-preload"));
402
403    /** See OPENDJ-2054 */
404    register("2.8.0",
405        deleteFile(new File(binDirectory, "dsframework")),
406        deleteFile(new File(batDirectory, "dsframework.bat")));
407
408    /** If the upgraded version is a non OEM one, migrates local-db backends to JE Backend, see OPENDJ-2364 **/
409    register("3.0.0",
410        conditionalUpgradeTasks(
411          new UpgradeCondition() {
412              @Override
413              public boolean shouldPerformUpgradeTasks(UpgradeContext context) throws ClientException {
414                return !isOEMVersion();
415              }
416
417              @Override
418              public String toString() {
419                return "!isOEMVersion";
420              }
421          },
422          migrateLocalDBBackendsToJEBackends(),
423          modifyConfigEntry(INFO_UPGRADE_TASK_MIGRATE_JE_SUMMARY_2.get(),
424              "(objectClass=ds-cfg-local-db-backend)",
425              "replace: objectClass",
426              "objectClass: top",
427              "objectClass: ds-cfg-backend",
428              "objectClass: ds-cfg-pluggable-backend",
429              "objectClass: ds-cfg-je-backend",
430              "-",
431              "replace: ds-cfg-java-class",
432              "ds-cfg-java-class: org.opends.server.backends.jeb.JEBackend",
433              "-",
434              "delete: ds-cfg-import-thread-count",
435              "-",
436              "delete: ds-cfg-import-queue-size",
437              "-",
438              "delete: ds-cfg-subordinate-indexes-enabled",
439              "-"
440          ),
441          modifyConfigEntry(INFO_UPGRADE_TASK_MIGRATE_JE_SUMMARY_3.get(),
442              "(objectClass=ds-cfg-local-db-index)",
443              "replace: objectClass",
444              "objectClass: top",
445              "objectClass: ds-cfg-backend-index",
446              "-"
447          ),
448          modifyConfigEntry(INFO_UPGRADE_TASK_MIGRATE_JE_SUMMARY_4.get(),
449              "(objectClass=ds-cfg-local-db-vlv-index)",
450              "replace: objectClass",
451              "objectClass: top",
452              "objectClass: ds-cfg-backend-vlv-index",
453              "-",
454              "delete: ds-cfg-max-block-size",
455              "-"
456          )
457        )
458    );
459
460    /** If the upgraded version is OEM, migrates local-db backends to PDB, see OPENDJ-2364 **/
461    register("3.0.0",
462      conditionalUpgradeTasks(
463        new UpgradeCondition() {
464          @Override
465          public boolean shouldPerformUpgradeTasks(UpgradeContext context) throws ClientException {
466            return isOEMVersion();
467          }
468
469          @Override
470          public String toString() {
471            return "isOEMVersion";
472          }
473        },
474        deleteFile(new File(libDirectory, "je.jar")),
475        requireConfirmation(INFO_UPGRADE_TASK_LOCAL_DB_TO_PDB_1_SUMMARY.get("3.0.0"), NO,
476                renameLocalDBBackendDirectories(LOCAL_DB_BACKEND_OBJECT_CLASS),
477                convertJEBackendsToPDBBackends(LOCAL_DB_BACKEND_OBJECT_CLASS),
478                // Convert JE backend indexes to PDB backend indexes.
479                modifyConfigEntry(INFO_UPGRADE_TASK_LOCAL_DB_TO_PDB_3_SUMMARY.get(),
480                        "(objectclass=ds-cfg-local-db-index)",
481                        "delete: objectclass",
482                        "objectclass: ds-cfg-local-db-index",
483                        "-",
484                        "add: objectclass",
485                        "objectclass: ds-cfg-backend-index",
486                        "-"
487                ),
488                // Convert JE backend VLV indexes to PDB backend VLV indexes.
489                modifyConfigEntry(INFO_UPGRADE_TASK_LOCAL_DB_TO_PDB_4_SUMMARY.get(),
490                        "(objectclass=ds-cfg-local-db-vlv-index)",
491                        "delete: objectclass",
492                        "objectclass: ds-cfg-local-db-vlv-index",
493                        "-",
494                        "add: objectclass",
495                        "objectclass: ds-cfg-backend-vlv-index",
496                        "-",
497                        "delete: ds-cfg-max-block-size",
498                        "-"
499                )
500        )
501      )
502    );
503
504    /** Remove dbtest tool (replaced by backendstat in 3.0.0) - see OPENDJ-1791 **/
505    register("3.0.0",
506            deleteFile(new File(binDirectory, "dbtest")),
507            deleteFile(new File(batDirectory, "dbtest.bat")));
508
509    /**
510     * Rebuild all indexes when upgrading to 3.0.0.
511     *
512     * 1) matching rules have changed in 2.8.0 and again in 3.0.0- see OPENDJ-1637
513     * 2) JE backend has been migrated to pluggable architecture.
514     */
515    register("3.0.0",
516            rebuildAllIndexes(INFO_UPGRADE_TASK_11260_SUMMARY.get()));
517
518    /** See OPENDJ-1742 */
519    register("3.0.0",
520        clearReplicationDbDirectory());
521
522    /** See OPENDJ-2435 */
523    register("3.5.0",
524        addConfigEntry(INFO_UPGRADE_TASK_BCRYPT_SCHEME_SUMMARY.get(),
525            "dn: cn=Bcrypt,cn=Password Storage Schemes,cn=config",
526            "changetype: add",
527            "objectClass: top",
528            "objectClass: ds-cfg-password-storage-scheme",
529            "objectClass: ds-cfg-bcrypt-password-storage-scheme",
530            "cn: Bcrypt",
531            "ds-cfg-java-class: org.opends.server.extensions.BcryptPasswordStorageScheme",
532            "ds-cfg-enabled: true"));
533
534    /** See OPENDJ-2683 */
535    register("3.5.0",
536        deleteConfigEntry(INFO_UPGRADE_TASK_REMOVE_MATCHING_RULES.get(),
537        "cn=Auth Password Exact Equality Matching Rule,cn=Matching Rules,cn=config",
538        "cn=Bit String Equality Matching Rule,cn=Matching Rules,cn=config",
539        "cn=Boolean Equality Matching Rule,cn=Matching Rules,cn=config",
540        "cn=Case Exact Equality Matching Rule,cn=Matching Rules,cn=config",
541        "cn=Case Exact Ordering Matching Rule,cn=Matching Rules,cn=config",
542        "cn=Case Exact Substring Matching Rule,cn=Matching Rules,cn=config",
543        "cn=Case Exact IA5 Equality Matching Rule,cn=Matching Rules,cn=config",
544        "cn=Case Exact IA5 Substring Matching Rule,cn=Matching Rules,cn=config",
545        "cn=Case Ignore Equality Matching Rule,cn=Matching Rules,cn=config",
546        "cn=Case Ignore Ordering Matching Rule,cn=Matching Rules,cn=config",
547        "cn=Case Ignore Substring Matching Rule,cn=Matching Rules,cn=config",
548        "cn=Case Ignore IA5 Equality Matching Rule,cn=Matching Rules,cn=config",
549        "cn=Case Ignore IA5 Substring Matching Rule,cn=Matching Rules,cn=config",
550        "cn=Case Ignore List Equality Matching Rule,cn=Matching Rules,cn=config",
551        "cn=Case Ignore List Substring Matching Rule,cn=Matching Rules,cn=config",
552        "cn=Certificate Exact Matching Rule,cn=Matching Rules,cn=config",
553        "cn=Directory String First Component Equality Matching Rule,cn=Matching Rules,cn=config",
554        "cn=Distinguished Name Equality Matching Rule,cn=Matching Rules,cn=config",
555        "cn=Double Metaphone Approximate Matching Rule,cn=Matching Rules,cn=config",
556        "cn=Generalized Time Equality Matching Rule,cn=Matching Rules,cn=config",
557        "cn=Generalized Time Ordering Matching Rule,cn=Matching Rules,cn=config",
558        "cn=Integer Equality Matching Rule,cn=Matching Rules,cn=config",
559        "cn=Integer Ordering Matching Rule,cn=Matching Rules,cn=config",
560        "cn=Integer First Component Equality Matching Rule,cn=Matching Rules,cn=config",
561        "cn=Keyword Equality Matching Rule,cn=Matching Rules,cn=config",
562        "cn=Numeric String Equality Matching Rule,cn=Matching Rules,cn=config",
563        "cn=Numeric String Ordering Matching Rule,cn=Matching Rules,cn=config",
564        "cn=Numeric String Substring Matching Rule,cn=Matching Rules,cn=config",
565        "cn=Object Identifier Equality Matching Rule,cn=Matching Rules,cn=config",
566        "cn=Object Identifier First Component Equality Matching Rule,cn=Matching Rules,cn=config",
567        "cn=Octet String Equality Matching Rule,cn=Matching Rules,cn=config",
568        "cn=Octet String Ordering Matching Rule,cn=Matching Rules,cn=config",
569        "cn=Octet String Substring Matching Rule,cn=Matching Rules,cn=config",
570        "cn=Presentation Address Equality Matching Rule,cn=Matching Rules,cn=config",
571        "cn=Protocol Information Equality Matching Rule,cn=Matching Rules,cn=config",
572        "cn=Telephone Number Equality Matching Rule,cn=Matching Rules,cn=config",
573        "cn=Telephone Number Substring Matching Rule,cn=Matching Rules,cn=config",
574        "cn=Time Based Matching Rule,cn=Matching Rules,cn=config",
575        "cn=Unique Member Equality Matching Rule,cn=Matching Rules,cn=config",
576        "cn=User Password Exact Equality Matching Rule,cn=Matching Rules,cn=config",
577        "cn=UUID Equality Matching Rule,cn=Matching Rules,cn=config",
578        "cn=UUID Ordering Matching Rule,cn=Matching Rules,cn=config",
579        "cn=Word Equality Matching Rule,cn=Matching Rules,cn=config"));
580
581    /** see OPENDJ-2730 */
582    register("3.5.0", removeOldJarFiles());
583
584    register("3.5.0",
585        rebuildIndexesNamed(INFO_UPGRADE_REBUILD_INDEXES_DISTINGUISHED_NAME.get(),
586            "." + EMR_DN_NAME, "." + EMR_OID_NAME, "." + EMR_UNIQUE_MEMBER_NAME, "." + EMR_CERTIFICATE_EXACT_NAME));
587
588    register("3.5.0",
589        deleteConfigEntry(INFO_UPGRADE_TASK_CONFIGURATION_BACKEND_NOT_CONFIGURABLE.get(),
590            "dn: ds-cfg-backend-id=config,cn=Backends,cn=config"));
591
592    register("3.5.0",
593        restoreCsvDelimiterAttributeTypeInConcatenatedSchemaFile());
594
595    register("3.5.0",
596        requireConfirmation(INFO_UPGRADE_TASK_CONFIRM_DISABLING_HTTP_CONNECTION_HANDLER.get(), YES,
597            modifyConfigEntry(INFO_UPGRADE_TASK_DISABLING_HTTP_CONNECTION_HANDLER.get(),
598                    "(objectclass=ds-cfg-http-connection-handler)",
599                    "replace: ds-cfg-enabled",
600                    "ds-cfg-enabled: false",
601                    "-",
602                    "delete: ds-cfg-authentication-required",
603                    "-",
604                    "delete: ds-cfg-config-file",
605                    "-"
606            )
607        ),
608        addConfigEntry(INFO_UPGRADE_TASK_ADDING_DEFAULT_HTTP_ENDPOINTS_AND_AUTH.get(),
609                "dn: cn=HTTP Endpoints,cn=config",
610                "objectClass: top",
611                "objectClass: ds-cfg-branch",
612                "cn: HTTP Endpoints"
613        ),
614        addConfigEntry(
615                "dn: ds-cfg-base-path=/api,cn=HTTP Endpoints,cn=config",
616                "objectClass: top",
617                "objectClass: ds-cfg-http-endpoint",
618                "objectClass: ds-cfg-rest2ldap-endpoint",
619                "ds-cfg-enabled: true",
620                "ds-cfg-java-class: org.opends.server.protocols.http.rest2ldap.Rest2LdapEndpoint",
621                "ds-cfg-base-path: /api",
622                "ds-cfg-config-directory: config/rest2ldap/endpoints/api",
623                "ds-cfg-http-authorization-mechanism: cn=HTTP Basic,cn=HTTP Authorization Mechanisms,cn=config"
624        ),
625        addConfigEntry(
626                "dn: ds-cfg-base-path=/admin,cn=HTTP Endpoints,cn=config",
627                "objectClass: top",
628                "objectClass: ds-cfg-http-endpoint",
629                "objectClass: ds-cfg-admin-endpoint",
630                "ds-cfg-enabled: true",
631                "ds-cfg-base-path: /admin",
632                "ds-cfg-java-class: org.opends.server.protocols.http.rest2ldap.AdminEndpoint",
633                "ds-cfg-http-authorization-mechanism: cn=HTTP Basic,cn=HTTP Authorization Mechanisms,cn=config"
634        ),
635        addConfigEntry(
636                "dn: cn=HTTP Authorization Mechanisms,cn=config",
637                "objectClass: top",
638                "objectClass: ds-cfg-branch",
639                "cn: HTTP Authorizations"
640        ),
641        addConfigEntry(
642                "dn: cn=HTTP Anonymous,cn=HTTP Authorization Mechanisms,cn=config",
643                "objectClass: top",
644                "objectClass: ds-cfg-http-authorization-mechanism",
645                "objectClass: ds-cfg-http-anonymous-authorization-mechanism",
646                "cn: HTTP Anonymous",
647                "ds-cfg-enabled: true",
648                "ds-cfg-java-class: org.opends.server.protocols.http.authz.HttpAnonymousAuthorizationMechanism"
649        ),
650        addConfigEntry(
651                "dn: cn=HTTP Basic,cn=HTTP Authorization Mechanisms,cn=config",
652                "objectClass: top",
653                "objectClass: ds-cfg-http-authorization-mechanism",
654                "objectClass: ds-cfg-http-basic-authorization-mechanism",
655                "cn: HTTP Basic",
656                "ds-cfg-java-class: org.opends.server.protocols.http.authz.HttpBasicAuthorizationMechanism",
657                "ds-cfg-enabled: true",
658                "ds-cfg-http-basic-alt-authentication-enabled: true",
659                "ds-cfg-http-basic-alt-username-header: X-OpenIDM-Username",
660                "ds-cfg-http-basic-alt-password-header: X-OpenIDM-Password",
661                "ds-cfg-identity-mapper: cn=Exact Match,cn=Identity Mappers,cn=config"
662        ),
663        addConfigEntry(
664                "dn: cn=HTTP OAuth2 CTS,cn=HTTP Authorization Mechanisms,cn=config",
665                "objectClass: top",
666                "objectClass: ds-cfg-http-authorization-mechanism",
667                "objectClass: ds-cfg-http-oauth2-authorization-mechanism",
668                "objectClass: ds-cfg-http-oauth2-cts-authorization-mechanism",
669                "cn: HTTP OAuth2 CTS",
670                "ds-cfg-java-class: org.opends.server.protocols.http.authz.HttpOAuth2CtsAuthorizationMechanism",
671                "ds-cfg-enabled: false",
672                "ds-cfg-cts-base-dn: ou=famrecords,ou=openam-session,ou=tokens,dc=example,dc=com",
673                "ds-cfg-oauth2-authzid-json-pointer: userName/0",
674                "ds-cfg-identity-mapper: cn=Exact Match,cn=Identity Mappers,cn=config",
675                "ds-cfg-oauth2-required-scope: read",
676                "ds-cfg-oauth2-required-scope: write",
677                "ds-cfg-oauth2-required-scope: uid",
678                "ds-cfg-oauth2-access-token-cache-enabled: false",
679                "ds-cfg-oauth2-access-token-cache-expiration: 300s"
680        ),
681        addConfigEntry(
682                "dn: cn=HTTP OAuth2 OpenAM,cn=HTTP Authorization Mechanisms,cn=config",
683                "objectClass: top",
684                "objectClass: ds-cfg-http-authorization-mechanism",
685                "objectClass: ds-cfg-http-oauth2-authorization-mechanism",
686                "objectClass: ds-cfg-http-oauth2-openam-authorization-mechanism",
687                "cn: HTTP OAuth2 OpenAM",
688                "ds-cfg-java-class: org.opends.server.protocols.http.authz.HttpOAuth2OpenAmAuthorizationMechanism",
689                "ds-cfg-enabled: false",
690                "ds-cfg-openam-token-info-url: http://openam.example.com:8080/openam/oauth2/tokeninfo",
691                "ds-cfg-oauth2-authzid-json-pointer: uid",
692                "ds-cfg-identity-mapper: cn=Exact Match,cn=Identity Mappers,cn=config",
693                "ds-cfg-oauth2-required-scope: read",
694                "ds-cfg-oauth2-required-scope: write",
695                "ds-cfg-oauth2-required-scope: uid",
696                "ds-cfg-oauth2-access-token-cache-enabled: false",
697                "ds-cfg-oauth2-access-token-cache-expiration: 300s"
698        ),
699        addConfigEntry(
700                "dn: cn=HTTP OAuth2 Token Introspection (RFC7662),cn=HTTP Authorization Mechanisms,cn=config",
701                "objectClass: top",
702                "objectClass: ds-cfg-http-authorization-mechanism",
703                "objectClass: ds-cfg-http-oauth2-authorization-mechanism",
704                "objectClass: ds-cfg-http-oauth2-token-introspection-authorization-mechanism",
705                "cn: HTTP OAuth2 Token Introspection (RFC7662)",
706                "ds-cfg-java-class: "
707                        + "org.opends.server.protocols.http.authz.HttpOAuth2TokenIntrospectionAuthorizationMechanism",
708                "ds-cfg-enabled: false",
709                "ds-cfg-oauth2-token-introspection-url: "
710                        + "http://openam.example.com:8080/openam/oauth2/myrealm/introspect",
711                "ds-cfg-oauth2-token-introspection-client-id: directoryserver",
712                "ds-cfg-oauth2-token-introspection-client-secret: secret",
713                "ds-cfg-oauth2-authzid-json-pointer: sub",
714                "ds-cfg-identity-mapper: cn=Exact Match,cn=Identity Mappers,cn=config",
715                "ds-cfg-oauth2-required-scope: read",
716                "ds-cfg-oauth2-required-scope: write",
717                "ds-cfg-oauth2-required-scope: uid",
718                "ds-cfg-oauth2-access-token-cache-enabled: false",
719                "ds-cfg-oauth2-access-token-cache-expiration: 300s"
720        ),
721        addConfigEntry(
722                "dn: cn=HTTP OAuth2 File,cn=HTTP Authorization Mechanisms,cn=config",
723                "objectClass: top",
724                "objectClass: ds-cfg-http-authorization-mechanism",
725                "objectClass: ds-cfg-http-oauth2-authorization-mechanism",
726                "objectClass: ds-cfg-http-oauth2-file-authorization-mechanism",
727                "cn: HTTP OAuth2 File",
728                "ds-cfg-java-class: org.opends.server.protocols.http.authz.HttpOAuth2FileAuthorizationMechanism",
729                "ds-cfg-enabled: false",
730                "ds-cfg-oauth2-access-token-directory: oauth2-demo/",
731                "ds-cfg-oauth2-authzid-json-pointer: uid",
732                "ds-cfg-identity-mapper: cn=Exact Match,cn=Identity Mappers,cn=config",
733                "ds-cfg-oauth2-required-scope: read",
734                "ds-cfg-oauth2-required-scope: write",
735                "ds-cfg-oauth2-required-scope: uid",
736                "ds-cfg-oauth2-access-token-cache-enabled: false",
737                "ds-cfg-oauth2-access-token-cache-expiration: 300s"
738        ),
739        /* Recursively copies.*/
740        addConfigFile("rest2ldap")
741    );
742
743    /* See OPENDJ-3778, OPENDJ-3791 */
744    register("3.5.2",
745        deleteFile(new File(libDirectory, "je.jar"))
746    );
747
748    /* See OPENDJ-4146 */
749    register("3.5.3",
750        addConfigEntry(INFO_UPGRADE_TASK_ADDING_HISTORICAL_CSN_RANGE_MATCHING_RULE.get(),
751                "dn: cn=Historical CSN Range Matching Rule,cn=Matching Rules,cn=config",
752                "objectClass: top",
753                "objectClass: ds-cfg-matching-rule",
754                "objectClass: ds-cfg-extensible-matching-rule",
755                "ds-cfg-java-class: org.opends.server.replication.plugin.HistoricalCsnRangeMatchingRuleFactory",
756                "ds-cfg-enabled: true",
757                "cn: Historical CSN Range Matching Rule"
758             ));
759
760    /** All upgrades will refresh the server configuration schema and generate a new upgrade folder. */
761    registerLast(
762        performOEMMigrationIfNeeded(),
763        copySchemaFile("02-config.ldif"),
764        updateConfigUpgradeFolder(),
765        postUpgradeRebuildIndexes());
766
767    // @formatter:on
768  }
769
770  /** If the upgraded version is OEM, migrates local-db backends to PDB, see OPENDJ-3002 **/
771  private static UpgradeTask performOEMMigrationIfNeeded() {
772    return conditionalUpgradeTasks(
773        isOemVersionAndNewerThan3dot0(),
774        deleteFile(new File(libDirectory, "je.jar")),
775        deleteFile(new File(libDirectory, "opendj-je-backend.jar")),
776        conditionalUpgradeTasks(
777            new UpgradeCondition() {
778                @Override
779                public boolean shouldPerformUpgradeTasks(final UpgradeContext context) throws ClientException {
780                    return instanceContainsJeBackends();
781                }
782            },
783            requireConfirmation(INFO_UPGRADE_TASK_LOCAL_DB_TO_PDB_1_SUMMARY.get("3.5.0"), NO,
784                    renameLocalDBBackendDirectories(JE_BACKEND_OBJECT_CLASS),
785                    convertJEBackendsToPDBBackends(JE_BACKEND_OBJECT_CLASS))
786        )
787    );
788  }
789
790  private static UpgradeCondition isOemVersionAndNewerThan3dot0() {
791    return new UpgradeCondition() {
792        @Override
793        public boolean shouldPerformUpgradeTasks(UpgradeContext context) throws ClientException {
794            return isOEMVersion()
795                && context.getFromVersion().isNewerThan(BuildVersion.valueOf("3.0.0"));
796        }
797
798        @Override
799        public String toString() {
800            return "is OEM version and from version >= 3.0.0";
801        }
802    };
803  }
804
805  private static UpgradeTask convertJEBackendsToPDBBackends(final String objectClass) {
806    return modifyConfigEntry(INFO_UPGRADE_TASK_LOCAL_DB_TO_PDB_2_SUMMARY.get(),
807        "(objectclass=" + objectClass + ")",
808        "delete: objectclass",
809        "objectclass: " + objectClass,
810        "-",
811        "add: objectclass",
812        "objectclass: ds-cfg-pluggable-backend",
813        "objectclass: ds-cfg-pdb-backend",
814        "-",
815        "replace: ds-cfg-java-class",
816        "ds-cfg-java-class: org.opends.server.backends.pdb.PDBBackend",
817        "-",
818        "delete: ds-cfg-preload-time-limit",
819        "-",
820        "delete: ds-cfg-import-thread-count",
821        "-",
822        "delete: ds-cfg-import-queue-size",
823        "-",
824        "delete: ds-cfg-db-txn-write-no-sync",
825        "-",
826        "delete: ds-cfg-db-run-cleaner",
827        "-",
828        "delete: ds-cfg-db-cleaner-min-utilization",
829        "-",
830        "delete: ds-cfg-db-evictor-lru-only",
831        "-",
832        "delete: ds-cfg-db-evictor-core-threads",
833        "-",
834        "delete: ds-cfg-db-evictor-max-threads",
835        "-",
836        "delete: ds-cfg-db-evictor-keep-alive",
837        "-",
838        "delete: ds-cfg-db-evictor-nodes-per-scan",
839        "-",
840        "delete: ds-cfg-db-log-file-max",
841        "-",
842        "delete: ds-cfg-db-log-filecache-size",
843        "-",
844        "delete: ds-cfg-db-logging-file-handler-on",
845        "-",
846        "delete: ds-cfg-db-logging-level",
847        "-",
848        "delete: ds-cfg-db-checkpointer-bytes-interval",
849        "-",
850        "delete: ds-cfg-db-checkpointer-wakeup-interval",
851        "-",
852        "delete: ds-cfg-db-num-lock-tables",
853        "-",
854        "delete: ds-cfg-db-num-cleaner-threads",
855        "-",
856        "delete: ds-cfg-je-property",
857        "-",
858        "delete: ds-cfg-subordinate-indexes-enabled",
859        "-"
860    );
861  }
862
863  /**
864   * Returns a list containing all the tasks which are required in order to upgrade
865   * from {@code fromVersion} to {@code toVersion}.
866   *
867   * @param fromVersion
868   *          The old version.
869   * @param toVersion
870   *          The new version.
871   * @return A list containing all the tasks which are required in order to upgrade
872   *         from {@code fromVersion} to {@code toVersion}.
873   */
874  private static List<UpgradeTask> getUpgradeTasks(final BuildVersion fromVersion, final BuildVersion toVersion)
875  {
876    final List<UpgradeTask> tasks = new LinkedList<>();
877    for (final List<UpgradeTask> subList : TASKS.subMap(fromVersion, false,
878        toVersion, true).values())
879    {
880      tasks.addAll(subList);
881    }
882    tasks.addAll(MANDATORY_TASKS);
883    return tasks;
884  }
885
886  /**
887   * Upgrades the server from {@code fromVersion} to {@code toVersion} located in the upgrade context.
888   *
889   * @param context
890   *          The context of the upgrade.
891   * @throws ClientException
892   *           If an error occurred while performing the upgrade.
893   */
894  public static void upgrade(final UpgradeContext context)
895      throws ClientException
896  {
897    // Checks and validates the version number.
898    isVersionCanBeUpdated(context);
899
900    // Server must be offline.
901    checkIfServerIsRunning(context);
902
903    context.notify(INFO_UPGRADE_TITLE.get(), TITLE_CALLBACK);
904    context.notify(INFO_UPGRADE_SUMMARY.get(context.getFromVersion(), context.getToVersion()), NOTICE_CALLBACK);
905    context.notify(INFO_UPGRADE_GENERAL_SEE_FOR_DETAILS.get(UpgradeLog.getLogFilePath()), NOTICE_CALLBACK);
906
907    // Checks License.
908    checkLicence(context);
909
910    logWarnAboutPatchesFolder();
911
912    // Get the list of required upgrade tasks.
913    final List<UpgradeTask> tasks =
914        getUpgradeTasks(context.getFromVersion(), context.getToVersion());
915    if (tasks.isEmpty())
916    {
917      changeBuildInfoVersion(context);
918      return;
919    }
920
921    try
922    {
923      // Let tasks interact with the user in order to obtain user's selection.
924      context.notify(INFO_UPGRADE_REQUIREMENTS.get(), TITLE_CALLBACK);
925      for (final UpgradeTask task : tasks)
926      {
927        task.prepare(context);
928      }
929
930      // Starts upgrade
931      final int userResponse = context.confirmYN(INFO_UPGRADE_DISPLAY_CONFIRM_START.get(), YES);
932      if (userResponse == NO)
933      {
934        final LocalizableMessage message = INFO_UPGRADE_ABORTED_BY_USER.get();
935        context.notify(message, WARNING);
936        throw new ClientException(ReturnCode.ERROR_UNEXPECTED, message);
937      }
938
939      // Perform the upgrade tasks.
940      context.notify(INFO_UPGRADE_PERFORMING_TASKS.get(), TITLE_CALLBACK);
941      for (final UpgradeTask task : tasks)
942      {
943        try
944        {
945          task.perform(context);
946        }
947        catch (ClientException e)
948        {
949          handleClientException(context, e);
950        }
951      }
952
953      if (UpgradeTasks.countErrors == 0)
954      {
955        /*
956         * The end of a successful upgrade is marked up with the build info file update and the license,
957         * if present, requires the creation of an approval file.
958         */
959        changeBuildInfoVersion(context);
960
961        createFileLicenseApproved();
962      }
963      else
964      {
965        context.notify(ERR_UPGRADE_FAILS.get(UpgradeTasks.countErrors), TITLE_CALLBACK);
966      }
967
968      // Performs the post upgrade tasks.
969      if (hasPostUpgradeTask && UpgradeTasks.countErrors == 0)
970      {
971        context.notify(INFO_UPGRADE_PERFORMING_POST_TASKS.get(), TITLE_CALLBACK);
972        performPostUpgradeTasks(context, tasks);
973        context.notify(INFO_UPGRADE_POST_TASKS_COMPLETE.get(), TITLE_CALLBACK);
974      }
975    }
976    catch (final ClientException e)
977    {
978      context.notify(e.getMessageObject(), ERROR_CALLBACK);
979      throw e;
980    }
981    catch (final Exception e)
982    {
983      final LocalizableMessage message = ERR_UPGRADE_TASKS_FAIL.get(stackTraceToSingleLineString(e));
984      context.notify(message, ERROR_CALLBACK);
985      throw new ClientException(ReturnCode.ERROR_UNEXPECTED, message, e);
986    }
987    finally
988    {
989      context.notify(INFO_UPGRADE_GENERAL_SEE_FOR_DETAILS.get(UpgradeLog.getLogFilePath()), NOTICE_CALLBACK);
990      logger.info(INFO_UPGRADE_PROCESS_END);
991    }
992  }
993
994  private static void performPostUpgradeTasks(final UpgradeContext context, final List<UpgradeTask> tasks)
995      throws ClientException
996  {
997    boolean isOk = true;
998    for (final UpgradeTask task : tasks)
999    {
1000      if (isOk)
1001      {
1002        try
1003        {
1004          task.postUpgrade(context);
1005        }
1006        catch (ClientException e)
1007        {
1008          context.notify(e.getMessageObject(), WARNING);
1009          needToExitWithErrorCode();
1010          isOk = false;
1011        }
1012      }
1013      else
1014      {
1015        task.postponePostUpgrade(context);
1016      }
1017    }
1018  }
1019
1020  private static void register(final String versionString,
1021      final UpgradeTask... tasks)
1022  {
1023    final BuildVersion version = BuildVersion.valueOf(versionString);
1024    List<UpgradeTask> taskList = TASKS.get(version);
1025    if (taskList == null)
1026    {
1027      taskList = new LinkedList<>();
1028      TASKS.put(version, taskList);
1029    }
1030    taskList.addAll(Arrays.asList(tasks));
1031  }
1032
1033  private static void registerLast(final UpgradeTask... tasks)
1034  {
1035    MANDATORY_TASKS.addAll(Arrays.asList(tasks));
1036  }
1037
1038  /**
1039   * The server must be offline during the upgrade.
1040   *
1041   * @throws ClientException
1042   *           An exception is thrown if the server is currently running.
1043   */
1044  private static void checkIfServerIsRunning(final UpgradeContext context) throws ClientException
1045  {
1046    final String lockFile = LockFileManager.getServerLockFileName();
1047
1048    final StringBuilder failureReason = new StringBuilder();
1049    try
1050    {
1051      // Assume that if we cannot acquire the lock file the server is running.
1052      if (!LockFileManager.acquireExclusiveLock(lockFile, failureReason))
1053      {
1054        final LocalizableMessage message = ERR_UPGRADE_REQUIRES_SERVER_OFFLINE.get();
1055        context.notify(message, NOTICE_CALLBACK);
1056        throw new ClientException(ReturnCode.ERROR_UNEXPECTED, message);
1057      }
1058    }
1059    finally
1060    {
1061      LockFileManager.releaseLock(lockFile, failureReason);
1062    }
1063  }
1064
1065  /**
1066   * Checks if the version can be updated.
1067   *
1068   * @param context
1069   *          The current context which running the upgrade.
1070   * @throws ClientException
1071   *           If an exception occurs - stops the process.
1072   */
1073  private static void isVersionCanBeUpdated(final UpgradeContext context)
1074      throws ClientException
1075  {
1076    if (context.getFromVersion().equals(context.getToVersion()))
1077    {
1078      // If the server is already up to date then treat it as a successful upgrade so that upgrade is idempotent.
1079      final LocalizableMessage message = ERR_UPGRADE_VERSION_UP_TO_DATE.get(context.getToVersion());
1080      context.notify(message, NOTICE_CALLBACK);
1081      throw new ClientException(ReturnCode.SUCCESS, message);
1082    }
1083
1084    // The upgrade only supports version >= 2.4.5.
1085    if (context.getFromVersion().compareTo(UPGRADE_SUPPORTS_VERSION_FROM) < 0)
1086    {
1087      final LocalizableMessage message =
1088          INFO_UPGRADE_VERSION_IS_NOT_SUPPORTED.get(UPGRADE_SUPPORTS_VERSION_FROM, UPGRADE_SUPPORTS_VERSION_FROM);
1089      context.notify(message, NOTICE_CALLBACK);
1090      throw new ClientException(ReturnCode.ERROR_UNEXPECTED, message);
1091    }
1092  }
1093
1094  /**
1095   * Writes the up to date's version number within the build info file.
1096   *
1097   * @param context
1098   *          The current context which running the upgrade.
1099   * @throws ClientException
1100   *           If an exception occurs when displaying the message.
1101   */
1102  private static void changeBuildInfoVersion(final UpgradeContext context)
1103      throws ClientException
1104  {
1105    File buildInfoFile = new File(UpgradeUtils.configDirectory, Installation.BUILDINFO_RELATIVE_PATH);
1106    try (FileWriter buildInfo = new FileWriter(buildInfoFile, false))
1107    {
1108
1109      // Write the new version
1110      buildInfo.write(context.getToVersion().toString());
1111
1112      context.notify(INFO_UPGRADE_SUCCESSFUL.get(context.getFromVersion(), context.getToVersion()), TITLE_CALLBACK);
1113    }
1114    catch (IOException e)
1115    {
1116      final LocalizableMessage message = LocalizableMessage.raw(e.getMessage());
1117      context.notify(message, ERROR_CALLBACK);
1118      throw new ClientException(ReturnCode.ERROR_UNEXPECTED, message);
1119    }
1120  }
1121
1122  private static void checkLicence(final UpgradeContext context)
1123      throws ClientException
1124  {
1125    // Check license
1126    if (LicenseFile.exists() && !LicenseFile.isAlreadyApproved())
1127    {
1128      context.notify(LocalizableMessage.raw(LINE_SEPARATOR + LicenseFile.getText()));
1129      context.notify(INFO_LICENSE_DETAILS_CLI_LABEL.get());
1130      if (!context.isAcceptLicenseMode())
1131      {
1132        final int answer;
1133
1134        // The force cannot answer yes to the license's question, which is not a task even if it requires a user
1135        // interaction OR -an accept license mode to continue the process.
1136        if (context.isForceUpgradeMode())
1137        {
1138          answer = NO;
1139          context.notify(
1140              LocalizableMessage.raw(INFO_LICENSE_ACCEPT.get() + " " + INFO_PROMPT_NO_COMPLETE_ANSWER.get()));
1141        }
1142        else
1143        {
1144          answer = context.confirmYN(INFO_LICENSE_ACCEPT.get(), NO);
1145        }
1146
1147        if (answer == NO)
1148        {
1149          System.exit(EXIT_CODE_SUCCESS);
1150        }
1151        else if (answer == YES)
1152        {
1153          LicenseFile.setApproval(true);
1154        }
1155      }
1156      else
1157      {
1158        // We automatically accept the license with this option.
1159        context.notify(
1160            LocalizableMessage.raw(INFO_LICENSE_ACCEPT.get() + " " + INFO_PROMPT_YES_COMPLETE_ANSWER.get()));
1161        LicenseFile.setApproval(true);
1162      }
1163    }
1164  }
1165
1166  /**
1167   * The classes folder is renamed by the script launcher to avoid
1168   * incompatibility between patches and upgrade process. If a folder
1169   * "classes.disabled" is found, this function just displays a warning in the
1170   * log file, meaning the "classes" folder has been renamed. See upgrade.sh /
1171   * upgrade.bat scripts which hold the renaming process. (OPENDJ-1098)
1172   */
1173  private static void logWarnAboutPatchesFolder()
1174  {
1175    try
1176    {
1177      final File backup = new File(UpgradeUtils.getInstancePath(), "classes.disabled");
1178      if (backup.exists()) {
1179        final File[] files = backup.listFiles();
1180        if (files != null && files.length > 0)
1181        {
1182          logger.warn(INFO_UPGRADE_CLASSES_FOLDER_RENAMED, backup.getAbsoluteFile());
1183        }
1184      }
1185    }
1186    catch (SecurityException e)
1187    {
1188      logger.debug(LocalizableMessage.raw(e.getMessage()), e);
1189    }
1190  }
1191
1192  static void needToRunPostUpgradePhase()
1193  {
1194    Upgrade.hasPostUpgradeTask = true;
1195  }
1196
1197  /** This method should be used when the upgrade tool has issued a warning. */
1198  static void needToExitWithErrorCode()
1199  {
1200    Upgrade.exitWithErrorCode = true;
1201  }
1202
1203  /**
1204   * {@code true} if the upgrade succeeded.
1205   *
1206   * @return {@code true} if the upgrade succeeded.
1207   */
1208  static boolean isSuccess()
1209  {
1210    return !exitWithErrorCode;
1211  }
1212
1213  /** Prevent instantiation. */
1214  private Upgrade()
1215  {
1216    // Nothing to do.
1217  }
1218}