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-2016 ForgeRock AS. 015 */ 016package org.opends.server.tools.upgrade; 017 018import static javax.security.auth.callback.ConfirmationCallback.NO; 019import static javax.security.auth.callback.ConfirmationCallback.YES; 020import static javax.security.auth.callback.TextOutputCallback.*; 021 022import static org.opends.messages.ToolMessages.*; 023import static org.opends.server.tools.upgrade.FileManager.copy; 024import static org.opends.server.tools.upgrade.Installation.CURRENT_CONFIG_FILE_NAME; 025import static org.opends.server.tools.upgrade.UpgradeUtils.*; 026import static org.opends.server.util.StaticUtils.isClassAvailable; 027 028import java.io.File; 029import java.io.IOException; 030import java.util.Arrays; 031import java.util.Collections; 032import java.util.HashSet; 033import java.util.LinkedList; 034import java.util.List; 035import java.util.Map; 036import java.util.Set; 037import java.util.TreeMap; 038import java.util.TreeSet; 039 040import javax.security.auth.callback.TextOutputCallback; 041 042import org.forgerock.i18n.LocalizableMessage; 043import org.forgerock.i18n.slf4j.LocalizedLogger; 044import org.forgerock.opendj.ldap.DN; 045import org.forgerock.opendj.ldap.Entry; 046import org.forgerock.opendj.ldap.Filter; 047import org.forgerock.opendj.ldap.SearchScope; 048import org.forgerock.opendj.ldap.requests.Requests; 049import org.forgerock.opendj.ldap.requests.SearchRequest; 050import org.forgerock.opendj.ldif.EntryReader; 051import org.forgerock.util.Utils; 052import org.opends.server.backends.pluggable.spi.TreeName; 053import org.opends.server.tools.JavaPropertiesTool; 054import org.opends.server.tools.RebuildIndex; 055import org.opends.server.util.BuildVersion; 056import org.opends.server.util.ChangeOperationType; 057import org.opends.server.util.StaticUtils; 058 059import com.forgerock.opendj.cli.ClientException; 060import com.forgerock.opendj.cli.ReturnCode; 061import com.sleepycat.je.DatabaseException; 062import com.sleepycat.je.Environment; 063import com.sleepycat.je.EnvironmentConfig; 064import com.sleepycat.je.Transaction; 065import com.sleepycat.je.TransactionConfig; 066 067/** Factory methods for create new upgrade tasks. */ 068public final class UpgradeTasks 069{ 070 /** Logger for the upgrade. */ 071 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 072 073 /** An errors counter in case of ignore errors mode. */ 074 static int countErrors; 075 076 /** Contains all the indexes to rebuild. */ 077 static Set<String> indexesToRebuild = new HashSet<>(); 078 079 /** A flag to avoid rebuild single indexes if 'rebuild all' is selected. */ 080 static boolean isRebuildAllIndexesIsPresent; 081 082 /** A flag for marking 'rebuild all' task accepted by user. */ 083 static boolean isRebuildAllIndexesTaskAccepted; 084 085 private static final List<String> SUPPORTED_LOCALES_FOR_3_0_0 = Arrays.asList( 086 "ca_ES", "de", "es", "fr", "ja", "ko", "pl", "zh_CN", "zh_TW"); 087 088 /** 089 * Returns a new upgrade task which adds a config entry to the underlying 090 * config file. 091 * 092 * @param summary 093 * The summary of this upgrade task. 094 * @param ldif 095 * The LDIF record which will be applied to matching entries. 096 * @return A new upgrade task which applies an LDIF record to all 097 * configuration entries matching the provided filter. 098 */ 099 public static UpgradeTask addConfigEntry(final LocalizableMessage summary, 100 final String... ldif) 101 { 102 return updateConfigEntry(summary, null, ChangeOperationType.ADD, ldif); 103 } 104 105 /** 106 * This task copies the file placed in parameter within the config / schema 107 * folder. If the file already exists, it's overwritten. 108 * 109 * @param fileName 110 * The name of the file which need to be copied. 111 * @return A task which copy the the file placed in parameter within the 112 * config / schema folder. If the file already exists, it's 113 * overwritten. 114 */ 115 public static UpgradeTask copySchemaFile(final String fileName) 116 { 117 return new AbstractUpgradeTask() 118 { 119 @Override 120 public void perform(final UpgradeContext context) throws ClientException 121 { 122 final LocalizableMessage msg = INFO_UPGRADE_TASK_REPLACE_SCHEMA_FILE.get(fileName); 123 logger.debug(msg); 124 125 final ProgressNotificationCallback pnc = new ProgressNotificationCallback(INFORMATION, msg, 0); 126 127 final File schemaFileTemplate = 128 new File(templateConfigSchemaDirectory, fileName); 129 130 try 131 { 132 context.notifyProgress(pnc.setProgress(20)); 133 if (!schemaFileTemplate.exists() || schemaFileTemplate.length() == 0) 134 { 135 throw new IOException(ERR_UPGRADE_CORRUPTED_TEMPLATE 136 .get(schemaFileTemplate.getPath()).toString()); 137 } 138 copy(schemaFileTemplate, configSchemaDirectory, true); 139 context.notifyProgress(pnc.setProgress(100)); 140 } 141 catch (final IOException e) 142 { 143 manageTaskException(context, ERR_UPGRADE_COPYSCHEMA_FAILS.get( 144 schemaFileTemplate.getName(), e.getMessage()), pnc); 145 } 146 } 147 148 @Override 149 public String toString() 150 { 151 return INFO_UPGRADE_TASK_REPLACE_SCHEMA_FILE.get(fileName).toString(); 152 } 153 }; 154 } 155 156 /** 157 * This task copies the file placed in parameter within the config folder. If 158 * the file already exists, it's overwritten. 159 * 160 * @param fileName 161 * The name of the file which need to be copied. 162 * @return A task which copy the the file placed in parameter within the 163 * config folder. If the file already exists, it's overwritten. 164 */ 165 public static UpgradeTask addConfigFile(final String fileName) 166 { 167 return new AbstractUpgradeTask() 168 { 169 @Override 170 public void perform(final UpgradeContext context) throws ClientException 171 { 172 final LocalizableMessage msg = INFO_UPGRADE_TASK_ADD_CONFIG_FILE.get(fileName); 173 logger.debug(msg); 174 175 final ProgressNotificationCallback pnc = new ProgressNotificationCallback(INFORMATION, msg, 0); 176 177 final File configFile = new File(templateConfigDirectory, fileName); 178 179 try 180 { 181 context.notifyProgress(pnc.setProgress(20)); 182 183 copy(configFile, configDirectory, true); 184 context.notifyProgress(pnc.setProgress(100)); 185 } 186 catch (final IOException e) 187 { 188 manageTaskException(context, ERR_UPGRADE_ADD_CONFIG_FILE_FAILS.get( 189 configFile.getName(), e.getMessage()), pnc); 190 } 191 } 192 193 @Override 194 public String toString() 195 { 196 return INFO_UPGRADE_TASK_ADD_CONFIG_FILE.get(fileName).toString(); 197 } 198 }; 199 } 200 201 /** 202 * Returns a new upgrade task which deletes a config entry from the underlying config file. 203 * 204 * @param summary 205 * The summary of this upgrade task. 206 * @param dnsInLDIF 207 * The dns to delete in the form of LDIF. 208 * @return A new upgrade task which applies an LDIF record to all configuration entries matching 209 * the provided filter. 210 */ 211 public static UpgradeTask deleteConfigEntry(final LocalizableMessage summary, final String... dnsInLDIF) 212 { 213 return updateConfigEntry(summary, null, ChangeOperationType.DELETE, dnsInLDIF); 214 } 215 216 /** 217 * Returns a new upgrade task which applies an LDIF record to all 218 * configuration entries matching the provided filter. 219 * 220 * @param summary 221 * The summary of this upgrade task. 222 * @param filter 223 * The LDAP filter which configuration entries must match. 224 * @param ldif 225 * The LDIF record which will be applied to matching entries. 226 * @return A new upgrade task which applies an LDIF record to all 227 * configuration entries matching the provided filter. 228 */ 229 public static UpgradeTask modifyConfigEntry(final LocalizableMessage summary, 230 final String filter, final String... ldif) 231 { 232 return updateConfigEntry(summary, filter, ChangeOperationType.MODIFY, ldif); 233 } 234 235 /** 236 * This task adds or updates an attribute type (must exist in the original file) 237 * to the file specified in {@code fileName}. The destination must be a file 238 * contained in the config/schema folder. The attribute type is updated if an 239 * attribute with the same OID exists. 240 * 241 * e.g : This example adds a new attribute type named 'etag' in the 00-core.ldif. 242 * The 'etag' attribute already exists in the 00-core.ldif template schema file. 243 * 244 * <pre> 245 * register("2.5.0", 246 * newAttributeTypes(LocalizableMessage.raw("New attribute etag"), 247 * false, "00-core.ldif", 248 * "1.3.6.1.4.1.36733.2.1.1.59")); 249 * </pre> 250 * 251 * @param summary 252 * The summary of the task. 253 * @param fileName 254 * The file where to add the new definitions. This file must be 255 * contained in the configuration/schema folder. 256 * @param attributeOids 257 * The OIDs of the attributes to add or update. 258 * @return An upgrade task which adds or updates attribute types, defined 259 * previously in the configuration template files, reads the 260 * definition and adds it onto the file specified in {@code fileName} 261 */ 262 public static UpgradeTask newAttributeTypes(final LocalizableMessage summary, 263 final String fileName, final String... attributeOids) 264 { 265 return new AbstractUpgradeTask() 266 { 267 @Override 268 public void perform(final UpgradeContext context) throws ClientException 269 { 270 logger.debug(summary); 271 272 final ProgressNotificationCallback pnc = new ProgressNotificationCallback(INFORMATION, summary, 20); 273 context.notifyProgress(pnc); 274 275 final File schemaFileTemplate = 276 new File(templateConfigSchemaDirectory, fileName); 277 278 final File pathDestination = new File(configSchemaDirectory, fileName); 279 try 280 { 281 final int changeCount = 282 updateSchemaFile(schemaFileTemplate, pathDestination, 283 attributeOids, null); 284 285 displayChangeCount(pathDestination.getPath(), changeCount); 286 287 context.notifyProgress(pnc.setProgress(100)); 288 } 289 catch (final IOException | IllegalStateException e) 290 { 291 manageTaskException(context, ERR_UPGRADE_ADDATTRIBUTE_FAILS.get( 292 schemaFileTemplate.getName(), e.getMessage()), pnc); 293 } 294 } 295 296 @Override 297 public String toString() 298 { 299 return String.valueOf(summary); 300 } 301 }; 302 } 303 304 /** 305 * This task adds or updates an object class (must exist in the original file) 306 * to the file specified in {@code fileName}. The destination must be a file 307 * contained in the config/schema folder. The object class will be updated if 308 * a definition with the same OID exists, and added otherwise. 309 * 310 * @param summary 311 * The summary of the task. 312 * @param fileName 313 * The file where to add the new definitions. This file must be 314 * contained in the configuration/schema folder. 315 * @param objectClassesOids 316 * The OIDs of the object classes to add or update. 317 * @return An upgrade task which adds or updates object classes, defined 318 * previously in the configuration template files, reads the 319 * definition and adds it onto the file specified in {@code fileName} 320 */ 321 public static UpgradeTask newObjectClasses(final LocalizableMessage summary, 322 final String fileName, final String... objectClassesOids) 323 { 324 return new AbstractUpgradeTask() 325 { 326 @Override 327 public void perform(final UpgradeContext context) throws ClientException 328 { 329 logger.debug(summary); 330 331 final ProgressNotificationCallback pnc = new ProgressNotificationCallback(INFORMATION, summary, 20); 332 context.notifyProgress(pnc); 333 334 final File schemaFileTemplate = 335 new File(templateConfigSchemaDirectory, fileName); 336 337 final File pathDestination = new File(configSchemaDirectory, fileName); 338 339 context.notifyProgress(pnc.setProgress(20)); 340 341 try 342 { 343 final int changeCount = 344 updateSchemaFile(schemaFileTemplate, pathDestination, 345 null, objectClassesOids); 346 347 displayChangeCount(pathDestination.getPath(), changeCount); 348 349 context.notifyProgress(pnc.setProgress(100)); 350 } 351 catch (final IOException e) 352 { 353 manageTaskException(context, ERR_UPGRADE_ADDOBJECTCLASS_FAILS.get( 354 schemaFileTemplate.getName(), e.getMessage()), pnc); 355 } 356 catch (final IllegalStateException e) 357 { 358 manageTaskException(context, ERR_UPGRADE_ADDATTRIBUTE_FAILS.get( 359 schemaFileTemplate.getName(), e.getMessage()), pnc); 360 } 361 } 362 363 @Override 364 public String toString() 365 { 366 return String.valueOf(summary); 367 } 368 }; 369 } 370 371 /** 372 * Re-run the dsjavaproperties tool to rewrite the set-java-home script/batch file. 373 * 374 * @param summary 375 * The summary of the task. 376 * @return An upgrade task which runs dsjavaproperties. 377 */ 378 public static UpgradeTask rerunJavaPropertiesTool(final LocalizableMessage summary) 379 { 380 return new AbstractUpgradeTask() 381 { 382 @Override 383 public void perform(UpgradeContext context) throws ClientException 384 { 385 logger.debug(summary); 386 387 final ProgressNotificationCallback pnc = new ProgressNotificationCallback(INFORMATION, summary, 50); 388 context.notifyProgress(pnc); 389 390 int returnValue = JavaPropertiesTool.mainCLI("--quiet"); 391 context.notifyProgress(pnc.setProgress(100)); 392 393 if (JavaPropertiesTool.ErrorReturnCode.SUCCESSFUL.getReturnCode() != returnValue && 394 JavaPropertiesTool.ErrorReturnCode.SUCCESSFUL_NOP.getReturnCode() != returnValue) { 395 throw new ClientException(ReturnCode.ERROR_UNEXPECTED, ERR_UPGRADE_DSJAVAPROPERTIES_FAILED.get()); 396 } 397 } 398 399 @Override 400 public String toString() 401 { 402 return String.valueOf(summary); 403 } 404 }; 405 } 406 407 /** 408 * Creates a group of tasks which will only be invoked if the current version 409 * is more recent than the provided version. This may be useful in cases where 410 * a regression was introduced in version X and resolved in a later version Y. 411 * In this case, the provided upgrade tasks will only be invoked if the 412 * current version is between X (inclusive) and Y (exclusive). 413 * 414 * @param versionString 415 * The lower bound version. The upgrade tasks will not be applied if 416 * the current version is older than this version. 417 * @param tasks 418 * The group of tasks to invoke if the current version is equal to or 419 * more recent than {@code versionString}. 420 * @return An upgrade task which will only be invoked if the current version 421 * is more recent than the provided version. 422 */ 423 public static UpgradeTask regressionInVersion(final String versionString, final UpgradeTask... tasks) 424 { 425 final BuildVersion version = BuildVersion.valueOf(versionString); 426 return conditionalUpgradeTasks(new UpgradeCondition() 427 { 428 @Override 429 public boolean shouldPerformUpgradeTasks(final UpgradeContext context) throws ClientException 430 { 431 return context.getFromVersion().compareTo(version) >= 0; 432 } 433 434 @Override 435 public String toString() 436 { 437 return "Regression in version \"" + versionString + "\""; 438 } 439 }, tasks); 440 } 441 442 /** 443 * Creates a group of tasks which will only be invoked if the user confirms agreement. This may be 444 * useful in cases where a feature is deprecated and the upgrade is capable of migrating the 445 * configuration to the new replacement feature. 446 * 447 * @param message 448 * The confirmation message. 449 * @param tasks 450 * The group of tasks to invoke if the user agrees. 451 * @return An upgrade task which will only be invoked if the user confirms agreement. 452 */ 453 static UpgradeTask requireConfirmation( 454 final LocalizableMessage message, final int defaultResponse, final UpgradeTask... tasks) 455 { 456 return conditionalUpgradeTasks(new UpgradeCondition() 457 { 458 @Override 459 public boolean shouldPerformUpgradeTasks(final UpgradeContext context) throws ClientException 460 { 461 return context.confirmYN(INFO_UPGRADE_TASK_NEEDS_USER_CONFIRM.get(message), defaultResponse) == YES; 462 } 463 464 @Override 465 public String toString() 466 { 467 return INFO_UPGRADE_TASK_NEEDS_USER_CONFIRM.get(message).toString(); 468 } 469 }, tasks); 470 } 471 472 /** Determines whether conditional tasks should be performed. */ 473 interface UpgradeCondition 474 { 475 boolean shouldPerformUpgradeTasks(UpgradeContext context) throws ClientException; 476 } 477 478 static UpgradeTask conditionalUpgradeTasks(final UpgradeCondition condition, final UpgradeTask... tasks) 479 { 480 return new AbstractUpgradeTask() 481 { 482 private boolean shouldPerformUpgradeTasks = true; 483 484 @Override 485 public void prepare(final UpgradeContext context) throws ClientException 486 { 487 shouldPerformUpgradeTasks = condition.shouldPerformUpgradeTasks(context); 488 if (shouldPerformUpgradeTasks) 489 { 490 for (UpgradeTask task : tasks) 491 { 492 task.prepare(context); 493 } 494 } 495 } 496 497 @Override 498 public void perform(final UpgradeContext context) throws ClientException 499 { 500 if (shouldPerformUpgradeTasks) 501 { 502 for (UpgradeTask task : tasks) 503 { 504 task.perform(context); 505 } 506 } 507 } 508 509 @Override 510 public void postUpgrade(UpgradeContext context) throws ClientException 511 { 512 if (shouldPerformUpgradeTasks) 513 { 514 boolean isOk = true; 515 for (final UpgradeTask task : tasks) 516 { 517 if (isOk) 518 { 519 try 520 { 521 task.postUpgrade(context); 522 } 523 catch (ClientException e) 524 { 525 logger.error(LocalizableMessage.raw(e.getMessage())); 526 isOk = false; 527 } 528 } 529 else 530 { 531 task.postponePostUpgrade(context); 532 } 533 } 534 } 535 } 536 537 @Override 538 public String toString() 539 { 540 final StringBuilder sb = new StringBuilder(); 541 sb.append(condition).append(" = ").append(shouldPerformUpgradeTasks).append('\n'); 542 sb.append('['); 543 Utils.joinAsString(sb, "\n", (Object[]) tasks); 544 sb.append(']'); 545 return sb.toString(); 546 } 547 }; 548 } 549 550 /** 551 * Creates a rebuild all indexes task. 552 * 553 * @param summary 554 * The summary of this upgrade task. 555 * @return An Upgrade task which rebuild all the indexes. 556 */ 557 public static UpgradeTask rebuildAllIndexes(final LocalizableMessage summary) 558 { 559 return new AbstractUpgradeTask() 560 { 561 private boolean isATaskToPerform; 562 563 @Override 564 public void prepare(UpgradeContext context) throws ClientException 565 { 566 Upgrade.needToRunPostUpgradePhase(); 567 // Requires answer from the user. 568 isATaskToPerform = context.confirmYN(summary, NO) == YES; 569 isRebuildAllIndexesIsPresent = true; 570 isRebuildAllIndexesTaskAccepted = isATaskToPerform; 571 } 572 573 @Override 574 public void postUpgrade(final UpgradeContext context) throws ClientException 575 { 576 if (!isATaskToPerform) 577 { 578 postponePostUpgrade(context); 579 } 580 } 581 582 @Override 583 public void postponePostUpgrade(UpgradeContext context) throws ClientException 584 { 585 context.notify(INFO_UPGRADE_ALL_REBUILD_INDEX_DECLINED.get(), TextOutputCallback.WARNING); 586 } 587 588 @Override 589 public String toString() 590 { 591 return String.valueOf(summary); 592 } 593 }; 594 } 595 596 /** 597 * Creates a rebuild index task for a given single index. As this task is 598 * possibly lengthy, it's considered as a post upgrade task. This task is not 599 * mandatory; e.g not require user interaction, but could be required to get a 600 * fully functional server. <br /> 601 * The post upgrade task just register the task. The rebuild indexes tasks are 602 * completed at the end of the upgrade process. 603 * 604 * @param summary 605 * A message describing why the index needs to be rebuilt and asking 606 * them whether or not they wish to perform this task after the 607 * upgrade. 608 * @param indexNames 609 * The indexes to rebuild. 610 * @return The rebuild index task. 611 */ 612 public static UpgradeTask rebuildIndexesNamed(final LocalizableMessage summary, final String... indexNames) 613 { 614 return new AbstractUpgradeTask() 615 { 616 private boolean isATaskToPerform; 617 618 @Override 619 public void prepare(UpgradeContext context) throws ClientException 620 { 621 Upgrade.needToRunPostUpgradePhase(); 622 // Requires answer from the user. 623 isATaskToPerform = context.confirmYN(summary, NO) == YES; 624 } 625 626 @Override 627 public void postUpgrade(final UpgradeContext context) throws ClientException 628 { 629 if (isATaskToPerform) 630 { 631 Collections.addAll(indexesToRebuild, indexNames); 632 } 633 else 634 { 635 postponePostUpgrade(context); 636 } 637 } 638 639 @Override 640 public void postponePostUpgrade(UpgradeContext context) throws ClientException 641 { 642 if (!isRebuildAllIndexesIsPresent) 643 { 644 context.notify(INFO_UPGRADE_REBUILD_INDEXES_DECLINED.get(indexNames), TextOutputCallback.WARNING); 645 } 646 } 647 648 @Override 649 public String toString() 650 { 651 return String.valueOf(summary); 652 } 653 }; 654 } 655 656 /** 657 * This task is processed at the end of the upgrade, rebuilding indexes. If a 658 * rebuild all indexes has been registered before, it takes the flag 659 * relatively to single rebuild index. 660 * 661 * @return The post upgrade rebuild indexes task. 662 */ 663 public static UpgradeTask postUpgradeRebuildIndexes() 664 { 665 return new AbstractUpgradeTask() 666 { 667 @Override 668 public void postUpgrade(final UpgradeContext context) throws ClientException 669 { 670 LocalizableMessage message = null; 671 final List<String> args = new LinkedList<>(); 672 673 if (isRebuildAllIndexesIsPresent && isRebuildAllIndexesTaskAccepted) 674 { 675 args.add("--rebuildAll"); 676 message = INFO_UPGRADE_REBUILD_ALL.get(); 677 } 678 else if (!indexesToRebuild.isEmpty() 679 && !isRebuildAllIndexesTaskAccepted) 680 { 681 message = INFO_UPGRADE_REBUILD_INDEX_STARTS.get(indexesToRebuild); 682 683 // Adding all requested indexes. 684 for (final String indexToRebuild : indexesToRebuild) 685 { 686 args.add("-i"); 687 args.add(indexToRebuild); 688 } 689 } 690 else 691 { 692 return; 693 } 694 // Startup message. 695 ProgressNotificationCallback pnc = new ProgressNotificationCallback(INFORMATION, message, 25); 696 logger.debug(message); 697 context.notifyProgress(pnc); 698 699 // Sets the arguments like the rebuild index command line. 700 args.addAll(Arrays.asList("-f", getConfigLdifFile().getAbsolutePath())); 701 702 /* 703 * Index(es) could be contained in several backends or none, If none, 704 * the post upgrade tasks succeed and a message is printed in the 705 * upgrade log file. 706 */ 707 final List<String> backends = UpgradeUtils.getIndexedBackendsFromConfig(); 708 if (backends.isEmpty()) 709 { 710 logger.debug(INFO_UPGRADE_REBUILD_INDEX_NO_BACKEND_FOUND); 711 logger.debug(INFO_UPGRADE_REBUILD_INDEXES_DECLINED, indexesToRebuild); 712 context.notifyProgress(pnc.setProgress(100)); 713 return; 714 } 715 716 for (final String be : backends) 717 { 718 args.add("-b"); 719 args.add(be); 720 } 721 722 // Displays info about command line args for log only. 723 logger.debug(INFO_UPGRADE_REBUILD_INDEX_ARGUMENTS, args); 724 725 /* 726 * The rebuild-index process just display a status ok / fails. The 727 * logger stream contains all the log linked to this process. The 728 * complete process is not displayed in the upgrade console. 729 */ 730 final String[] commandLineArgs = args.toArray(new String[args.size()]); 731 final int result = new RebuildIndex().rebuildIndexesWithinMultipleBackends( 732 true, UpgradeLog.getPrintStream(), commandLineArgs); 733 734 if (result == 0) 735 { 736 logger.debug(INFO_UPGRADE_REBUILD_INDEX_ENDS); 737 context.notifyProgress(pnc.setProgress(100)); 738 } 739 else 740 { 741 final LocalizableMessage msg = ERR_UPGRADE_PERFORMING_POST_TASKS_FAIL.get(); 742 context.notifyProgress(pnc.setProgress(-100)); 743 throw new ClientException(ReturnCode.ERROR_UNEXPECTED, msg); 744 } 745 } 746 747 @Override 748 public String toString() 749 { 750 return "Post upgrade rebuild indexes task"; 751 } 752 }; 753 } 754 755 /** 756 * Creates a file object representing config/upgrade/schema.ldif.current which 757 * the server creates the first time it starts if there are schema 758 * customizations. 759 * 760 * @return An upgrade task which upgrade the config/upgrade folder, creating a 761 * new schema.ldif.rev which is needed after schema customization for 762 * starting correctly the server. 763 */ 764 public static UpgradeTask updateConfigUpgradeFolder() 765 { 766 return new AbstractUpgradeTask() 767 { 768 @Override 769 public void perform(final UpgradeContext context) throws ClientException 770 { 771 final LocalizableMessage msg = INFO_UPGRADE_TASK_REFRESH_UPGRADE_DIRECTORY.get(); 772 logger.debug(msg); 773 774 final ProgressNotificationCallback pnc = new ProgressNotificationCallback(INFORMATION, msg, 20); 775 context.notifyProgress(pnc); 776 777 try 778 { 779 String toRevision = context.getToVersion().getRevision(); 780 updateConfigUpgradeSchemaFile(configSchemaDirectory, toRevision); 781 782 context.notifyProgress(pnc.setProgress(100)); 783 } 784 catch (final Exception ex) 785 { 786 manageTaskException(context, ERR_UPGRADE_CONFIG_ERROR_UPGRADE_FOLDER.get(ex.getMessage()), pnc); 787 } 788 } 789 790 @Override 791 public String toString() 792 { 793 return INFO_UPGRADE_TASK_REFRESH_UPGRADE_DIRECTORY.get().toString(); 794 } 795 }; 796 } 797 798 /** 799 * Renames the SNMP security config file if it exists. Since 2.5.0.7466 this 800 * file has been renamed. 801 * 802 * @param summary 803 * The summary of this upgrade task. 804 * @return An upgrade task which renames the old SNMP security config file if 805 * it exists. 806 */ 807 public static UpgradeTask renameSnmpSecurityConfig(final LocalizableMessage summary) 808 { 809 return new AbstractUpgradeTask() 810 { 811 @Override 812 public void perform(final UpgradeContext context) throws ClientException 813 { 814 /* 815 * Snmp config file contains old name in old version(like 2.4.5), in 816 * order to make sure the process will still work after upgrade, we need 817 * to rename it - only if it exists. 818 */ 819 final File snmpDir = UpgradeUtils.configSnmpSecurityDirectory; 820 if (snmpDir.exists()) 821 { 822 ProgressNotificationCallback pnc = new ProgressNotificationCallback(INFORMATION, summary, 0); 823 try 824 { 825 final File oldSnmpConfig = new File(snmpDir, "opends-snmp.security"); 826 if (oldSnmpConfig.exists()) 827 { 828 context.notifyProgress(pnc.setProgress(20)); 829 logger.debug(summary); 830 831 final File snmpConfig = new File(snmpDir, "opendj-snmp.security"); 832 FileManager.rename(oldSnmpConfig, snmpConfig); 833 834 context.notifyProgress(pnc.setProgress(100)); 835 } 836 } 837 catch (final Exception ex) 838 { 839 LocalizableMessage msg = ERR_UPGRADE_RENAME_SNMP_SECURITY_CONFIG_FILE.get(ex.getMessage()); 840 manageTaskException(context, msg, pnc); 841 } 842 } 843 } 844 845 @Override 846 public String toString() 847 { 848 return String.valueOf(summary); 849 } 850 }; 851 } 852 853 /** 854 * Removes the specified file from the file-system. 855 * 856 * @param file 857 * The file to be removed. 858 * @return An upgrade task which removes the specified file from the file-system. 859 */ 860 public static UpgradeTask deleteFile(final File file) 861 { 862 return new AbstractUpgradeTask() 863 { 864 @Override 865 public void perform(UpgradeContext context) throws ClientException 866 { 867 LocalizableMessage msg = INFO_UPGRADE_TASK_DELETE_FILE.get(file); 868 ProgressNotificationCallback pnc = new ProgressNotificationCallback(INFORMATION, msg, 0); 869 context.notifyProgress(pnc); 870 try 871 { 872 FileManager.deleteRecursively(file); 873 context.notifyProgress(pnc.setProgress(100)); 874 } 875 catch (Exception e) 876 { 877 manageTaskException(context, LocalizableMessage.raw(e.getMessage()), pnc); 878 } 879 } 880 881 @Override 882 public String toString() 883 { 884 return INFO_UPGRADE_TASK_DELETE_FILE.get(file).toString(); 885 } 886 }; 887 } 888 889 /** 890 * Creates an upgrade task which is responsible for preparing local-db backend JE databases for a full rebuild once 891 * they have been converted to pluggable JE backends. 892 * 893 * @return An upgrade task which is responsible for preparing local-db backend JE databases. 894 */ 895 public static UpgradeTask migrateLocalDBBackendsToJEBackends() { 896 return new AbstractUpgradeTask() { 897 /** Properties of JE backends to be migrated. */ 898 class Backend { 899 final String id; 900 final boolean isEnabled; 901 final Set<DN> baseDNs; 902 final File envDir; 903 final Map<String, String> renamedDbs = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); 904 905 private Backend(Entry config) { 906 id = config.parseAttribute("ds-cfg-backend-id").asString(); 907 isEnabled = config.parseAttribute("ds-cfg-enabled").asBoolean(false); 908 baseDNs = config.parseAttribute("ds-cfg-base-dn").asSetOfDN(); 909 String dbDirectory = config.parseAttribute("ds-cfg-db-directory").asString(); 910 File backendParentDirectory = new File(dbDirectory); 911 if (!backendParentDirectory.isAbsolute()) { 912 backendParentDirectory = new File(getInstancePath(), dbDirectory); 913 } 914 envDir = new File(backendParentDirectory, id); 915 for (String db : Arrays.asList("compressed_attributes", "compressed_object_classes")) { 916 renamedDbs.put(db, new TreeName("compressed_schema", db).toString()); 917 } 918 for (DN baseDN : baseDNs) { 919 renamedDbs.put(oldName(baseDN), newName(baseDN)); 920 } 921 } 922 } 923 924 private final List<Backend> backends = new LinkedList<>(); 925 926 /** 927 * Finds all the existing JE backends and determines if they can be migrated or not. It will not be possible to 928 * migrate a JE backend if the id2entry database name cannot easily be determined, which may happen because 929 * matching rules have changed significantly in 3.0.0. 930 */ 931 @Override 932 public void prepare(final UpgradeContext context) throws ClientException { 933 // Requires answer from the user. 934 if (context.confirmYN(INFO_UPGRADE_TASK_MIGRATE_JE_DESCRIPTION.get(), NO) != YES) { 935 throw new ClientException(ReturnCode.ERROR_USER_CANCELLED, 936 INFO_UPGRADE_TASK_MIGRATE_JE_CANCELLED.get()); 937 } 938 939 final SearchRequest sr = Requests.newSearchRequest("", SearchScope.WHOLE_SUBTREE, 940 "(objectclass=ds-cfg-local-db-backend)"); 941 try (final EntryReader entryReader = searchConfigFile(sr)) { 942 // Abort the upgrade if there are JE backends but no JE library. 943 if (entryReader.hasNext() && !isJeLibraryAvailable()) { 944 throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, INFO_UPGRADE_TASK_MIGRATE_JE_NO_JE_LIB.get()); 945 } 946 while (entryReader.hasNext()) { 947 Backend backend = new Backend(entryReader.readEntry()); 948 if (backend.isEnabled) { 949 abortIfBackendCannotBeMigrated(backend); 950 } 951 backends.add(backend); 952 } 953 } catch (IOException e) { 954 throw new ClientException(ReturnCode.APPLICATION_ERROR, INFO_UPGRADE_TASK_MIGRATE_CONFIG_READ_FAIL.get(), e); 955 } 956 } 957 958 private void abortIfBackendCannotBeMigrated(final Backend backend) throws ClientException { 959 Set<String> existingDatabases = JEHelper.listDatabases(backend.envDir); 960 for (DN baseDN : backend.baseDNs) { 961 final String oldName = oldName(baseDN); 962 if (!existingDatabases.contains(oldName)) { 963 LocalizableMessage msg = INFO_UPGRADE_TASK_MIGRATE_JE_UGLY_DN.get(backend.id, baseDN); 964 throw new ClientException(ReturnCode.CONSTRAINT_VIOLATION, msg); 965 } 966 } 967 } 968 969 /** 970 * Renames the compressed schema indexes and id2entry in a 2.x environment to 971 * the naming scheme used in 3.0.0. Before 3.0.0 JE databases were named as follows: 972 * 973 * 1) normalize the base DN 974 * 2) replace all non-alphanumeric characters with '_' 975 * 3) append '_' 976 * 4) append the index name. 977 * 978 * For example, id2entry in the base DN dc=white space,dc=com would be named 979 * dc_white_space_dc_com_id2entry. In 3.0.0 JE databases are named as follows: 980 * 981 * 1) normalize the base DN and URL encode it (' ' are converted to %20) 982 * 2) format as '/' + URL encoded base DN + '/' + index name. 983 * 984 * The matching rules in 3.0.0 are not compatible with previous versions, so we need 985 * to do a best effort attempt to figure out the old database name from a given base DN. 986 */ 987 @Override 988 public void perform(final UpgradeContext context) throws ClientException { 989 if (!isJeLibraryAvailable()) { 990 return; 991 } 992 993 for (Backend backend : backends) { 994 if (backend.isEnabled) { 995 ProgressNotificationCallback pnc = new ProgressNotificationCallback( 996 INFORMATION, INFO_UPGRADE_TASK_MIGRATE_JE_SUMMARY_1.get(backend.id), 0); 997 context.notifyProgress(pnc); 998 try { 999 JEHelper.migrateDatabases(backend.envDir, backend.renamedDbs); 1000 context.notifyProgress(pnc.setProgress(100)); 1001 } catch (ClientException e) { 1002 manageTaskException(context, e.getMessageObject(), pnc); 1003 } 1004 } else { 1005 // Skip backends which have been disabled. 1006 final ProgressNotificationCallback pnc = new ProgressNotificationCallback( 1007 INFORMATION, INFO_UPGRADE_TASK_MIGRATE_JE_SUMMARY_5.get(backend.id), 0); 1008 context.notifyProgress(pnc); 1009 context.notifyProgress(pnc.setProgress(100)); 1010 } 1011 } 1012 } 1013 1014 private boolean isJeLibraryAvailable() { 1015 return isClassAvailable("com.sleepycat.je.Environment"); 1016 } 1017 1018 private String newName(final DN baseDN) { 1019 return new TreeName(baseDN.toNormalizedUrlSafeString(), "id2entry").toString(); 1020 } 1021 1022 private String oldName(final DN baseDN) { 1023 String s = baseDN.toString(); 1024 StringBuilder builder = new StringBuilder(); 1025 for (int i = 0; i < s.length(); i++) { 1026 char c = s.charAt(i); 1027 builder.append(Character.isLetterOrDigit(c) ? c : '_'); 1028 } 1029 builder.append("_id2entry"); 1030 return builder.toString(); 1031 } 1032 1033 @Override 1034 public String toString() 1035 { 1036 return INFO_UPGRADE_TASK_MIGRATE_JE_SUMMARY_1.get("%s").toString(); 1037 } 1038 }; 1039 } 1040 1041 /** 1042 * Creates backups of the local DB backends directories by renaming adding them a ".bak" suffix. 1043 * e.g "userRoot" would become "userRoot.bak" 1044 */ 1045 static UpgradeTask renameLocalDBBackendDirectories() 1046 { 1047 return new AbstractUpgradeTask() 1048 { 1049 private boolean reimportRequired; 1050 1051 @Override 1052 public void perform(UpgradeContext context) throws ClientException 1053 { 1054 try 1055 { 1056 Filter filter = Filter.equality("objectclass", "ds-cfg-local-db-backend"); 1057 SearchRequest findLocalDBBackends = Requests.newSearchRequest(DN.rootDN(), SearchScope.WHOLE_SUBTREE, filter); 1058 try (final EntryReader jeBackends = searchConfigFile(findLocalDBBackends)) 1059 { 1060 while (jeBackends.hasNext()) 1061 { 1062 Upgrade.needToRunPostUpgradePhase(); 1063 reimportRequired = true; 1064 1065 Entry jeBackend = jeBackends.readEntry(); 1066 File dbParent = UpgradeUtils.getFileForPath(jeBackend.parseAttribute("ds-cfg-db-directory").asString()); 1067 String id = jeBackend.parseAttribute("ds-cfg-backend-id").asString(); 1068 1069 // Use canonical paths so that the progress message is more readable. 1070 File dbDirectory = new File(dbParent, id).getCanonicalFile(); 1071 File dbDirectoryBackup = new File(dbParent, id + ".bak").getCanonicalFile(); 1072 if (dbDirectory.exists() && !dbDirectoryBackup.exists()) 1073 { 1074 LocalizableMessage msg = INFO_UPGRADE_TASK_RENAME_JE_DB_DIR.get(dbDirectory, dbDirectoryBackup); 1075 ProgressNotificationCallback pnc = new ProgressNotificationCallback(0, msg, 0); 1076 context.notifyProgress(pnc); 1077 boolean renameSucceeded = dbDirectory.renameTo(dbDirectoryBackup); 1078 context.notifyProgress(pnc.setProgress(renameSucceeded ? 100 : -1)); 1079 } 1080 } 1081 } 1082 } 1083 catch (Exception e) 1084 { 1085 logger.error(LocalizableMessage.raw(e.getMessage())); 1086 } 1087 } 1088 1089 @Override 1090 public void postUpgrade(UpgradeContext context) throws ClientException 1091 { 1092 postponePostUpgrade(context); 1093 } 1094 1095 @Override 1096 public void postponePostUpgrade(UpgradeContext context) throws ClientException 1097 { 1098 if (reimportRequired) 1099 { 1100 context.notify(INFO_UPGRADE_TASK_RENAME_JE_DB_DIR_WARNING.get(), TextOutputCallback.WARNING); 1101 } 1102 } 1103 1104 @Override 1105 public String toString() 1106 { 1107 return INFO_UPGRADE_TASK_RENAME_JE_DB_DIR.get("%s", "%s").toString(); 1108 } 1109 }; 1110 } 1111 1112 /** This inner classes causes JE to be lazily linked and prevents runtime errors if JE is not in the classpath. */ 1113 static final class JEHelper { 1114 private static ClientException clientException(final File backendDirectory, final DatabaseException e) { 1115 logger.error(LocalizableMessage.raw(StaticUtils.stackTraceToString(e))); 1116 return new ClientException(ReturnCode.CONSTRAINT_VIOLATION, 1117 INFO_UPGRADE_TASK_MIGRATE_JE_ENV_UNREADABLE.get(backendDirectory), e); 1118 } 1119 1120 static Set<String> listDatabases(final File backendDirectory) throws ClientException { 1121 try (Environment je = new Environment(backendDirectory, null)) { 1122 Set<String> databases = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); 1123 databases.addAll(je.getDatabaseNames()); 1124 return databases; 1125 } catch (DatabaseException e) { 1126 throw clientException(backendDirectory, e); 1127 } 1128 } 1129 1130 static void migrateDatabases(final File envDir, final Map<String, String> renamedDbs) throws ClientException { 1131 EnvironmentConfig config = new EnvironmentConfig().setTransactional(true); 1132 try (Environment je = new Environment(envDir, config)) { 1133 final Transaction txn = je.beginTransaction(null, new TransactionConfig()); 1134 try { 1135 for (String dbName : je.getDatabaseNames()) { 1136 String newDbName = renamedDbs.get(dbName); 1137 if (newDbName != null) { 1138 // id2entry or compressed schema should be kept 1139 je.renameDatabase(txn, dbName, newDbName); 1140 } else { 1141 // This index will need rebuilding 1142 je.removeDatabase(txn, dbName); 1143 } 1144 } 1145 txn.commit(); 1146 } finally { 1147 txn.abort(); 1148 } 1149 } catch (DatabaseException e) { 1150 throw JEHelper.clientException(envDir, e); 1151 } 1152 } 1153 } 1154 1155 private static void displayChangeCount(final String fileName, 1156 final int changeCount) 1157 { 1158 if (changeCount != 0) 1159 { 1160 logger.debug(INFO_UPGRADE_CHANGE_DONE_IN_SPECIFIC_FILE, fileName, changeCount); 1161 } 1162 else 1163 { 1164 logger.debug(INFO_UPGRADE_NO_CHANGE_DONE_IN_SPECIFIC_FILE, fileName); 1165 } 1166 } 1167 1168 private static void displayTaskLogInformation(final LocalizableMessage summary, 1169 final String filter, final String... ldif) 1170 { 1171 logger.debug(summary); 1172 if (filter != null) 1173 { 1174 logger.debug(LocalizableMessage.raw(filter)); 1175 } 1176 if (ldif != null) 1177 { 1178 logger.debug(LocalizableMessage.raw(Arrays.toString(ldif))); 1179 } 1180 } 1181 1182 private static void manageTaskException(final UpgradeContext context, 1183 final LocalizableMessage message, final ProgressNotificationCallback pnc) 1184 throws ClientException 1185 { 1186 countErrors++; 1187 context.notifyProgress(pnc.setProgress(-100)); 1188 logger.error(message); 1189 if (!context.isIgnoreErrorsMode()) 1190 { 1191 throw new ClientException(ReturnCode.ERROR_UNEXPECTED, message); 1192 } 1193 } 1194 1195 private static UpgradeTask updateConfigEntry(final LocalizableMessage summary, final String filter, 1196 final ChangeOperationType changeOperationType, final String... ldif) 1197 { 1198 return new AbstractUpgradeTask() 1199 { 1200 @Override 1201 public void perform(final UpgradeContext context) throws ClientException 1202 { 1203 performConfigFileUpdate(summary, filter, changeOperationType, context, ldif); 1204 } 1205 1206 @Override 1207 public String toString() 1208 { 1209 return String.valueOf(summary); 1210 } 1211 }; 1212 } 1213 1214 private static void performConfigFileUpdate(final LocalizableMessage summary, final String filter, 1215 final ChangeOperationType changeOperationType, final UpgradeContext context, final String... ldif) 1216 throws ClientException 1217 { 1218 displayTaskLogInformation(summary, filter, ldif); 1219 1220 final ProgressNotificationCallback pnc = new ProgressNotificationCallback(INFORMATION, summary, 20); 1221 context.notifyProgress(pnc); 1222 1223 try 1224 { 1225 final File configFile = getConfigLdifFile(); 1226 1227 final Filter filterVal = filter != null ? Filter.valueOf(filter) : null; 1228 final int changeCount = updateConfigFile( 1229 configFile.getPath(), filterVal, changeOperationType, ldif); 1230 1231 displayChangeCount(configFile.getPath(), changeCount); 1232 1233 context.notifyProgress(pnc.setProgress(100)); 1234 } 1235 catch (final Exception e) 1236 { 1237 manageTaskException(context, LocalizableMessage.raw(e.getMessage()), pnc); 1238 } 1239 } 1240 1241 private static File getConfigLdifFile() 1242 { 1243 return new File(configDirectory, CURRENT_CONFIG_FILE_NAME); 1244 } 1245 1246 static UpgradeTask clearReplicationDbDirectory() 1247 { 1248 return new AbstractUpgradeTask() 1249 { 1250 private File replicationDbDir; 1251 1252 @Override 1253 public void prepare(final UpgradeContext context) throws ClientException 1254 { 1255 String replDbDir = readReplicationDbDirFromConfig(); 1256 if (replDbDir != null 1257 && context.confirmYN(INFO_UPGRADE_TASK_MIGRATE_CHANGELOG_DESCRIPTION.get(), NO) == YES) 1258 { 1259 replicationDbDir = new File(getInstancePath(), replDbDir).getAbsoluteFile(); 1260 } 1261 // if replDbDir == null, then this is not an RS, there is no changelog DB to clear 1262 } 1263 1264 private String readReplicationDbDirFromConfig() throws ClientException 1265 { 1266 final SearchRequest sr = Requests.newSearchRequest( 1267 DN.valueOf("cn=replication server,cn=Multimaster Synchronization,cn=Synchronization Providers,cn=config"), 1268 SearchScope.BASE_OBJECT, Filter.alwaysTrue()); 1269 try (final EntryReader entryReader = searchConfigFile(sr)) 1270 { 1271 if (entryReader.hasNext()) 1272 { 1273 final Entry replServerCfg = entryReader.readEntry(); 1274 return replServerCfg.parseAttribute("ds-cfg-replication-db-directory").asString(); 1275 } 1276 return null; 1277 } 1278 catch (IOException e) 1279 { 1280 LocalizableMessage msg = INFO_UPGRADE_TASK_MIGRATE_CONFIG_READ_FAIL.get(); 1281 throw new ClientException(ReturnCode.APPLICATION_ERROR, msg, e); 1282 } 1283 } 1284 1285 @Override 1286 public void perform(final UpgradeContext context) throws ClientException 1287 { 1288 if (replicationDbDir == null) 1289 { 1290 // there is no changelog DB to clear 1291 return; 1292 } 1293 1294 LocalizableMessage msg = INFO_UPGRADE_TASK_DELETE_CHANGELOG_SUMMARY.get(replicationDbDir); 1295 ProgressNotificationCallback pnc = new ProgressNotificationCallback(INFORMATION, msg, 0); 1296 context.notifyProgress(pnc); 1297 try 1298 { 1299 FileManager.deleteRecursively(replicationDbDir); 1300 context.notifyProgress(pnc.setProgress(100)); 1301 } 1302 catch (ClientException e) 1303 { 1304 manageTaskException(context, e.getMessageObject(), pnc); 1305 } 1306 catch (Exception e) 1307 { 1308 manageTaskException(context, LocalizableMessage.raw(e.getLocalizedMessage()), pnc); 1309 } 1310 } 1311 1312 @Override 1313 public String toString() 1314 { 1315 return INFO_UPGRADE_TASK_DELETE_CHANGELOG_SUMMARY.get(replicationDbDir).toString(); 1316 } 1317 }; 1318 } 1319 1320 /** Removes server and localized jars from previous version since names have changed. */ 1321 static UpgradeTask removeOldJarFiles() 1322 { 1323 return new AbstractUpgradeTask() 1324 { 1325 1326 @Override 1327 public void perform(final UpgradeContext context) throws ClientException 1328 { 1329 final ProgressNotificationCallback pnc = new ProgressNotificationCallback( 1330 INFORMATION, INFO_UPGRADE_TASK_REMOVE_OLD_JARS.get(), 0); 1331 context.notifyProgress(pnc); 1332 1333 final boolean fileSystemIsCaseSensitive = fileSystemIsCaseSensitive(context); 1334 1335 deleteJarFilesIfFileSystemIsCaseSensitive(fileSystemIsCaseSensitive, "OpenDJ"); 1336 for (final String locale : SUPPORTED_LOCALES_FOR_3_0_0) 1337 { 1338 deleteJarFiles("OpenDJ-" + locale); 1339 deleteJarFilesIfFileSystemIsCaseSensitive(fileSystemIsCaseSensitive, "OpenDJ_" + locale); 1340 } 1341 // Jar files from 2.6.x 1342 deleteJarFiles("jackson-core-asl", "jackson-mapper-asl", "json-fluent", "json-resource-servlet", 1343 "mail", "opendj-ldap-sdk", "opendj-rest2ldap-servlet", "opendj-server2x-adapter"); 1344 context.notifyProgress(pnc.setProgress(100)); 1345 } 1346 1347 private void deleteJarFilesIfFileSystemIsCaseSensitive( 1348 final boolean fileSystemIsCaseSensitive, final String... jarFileNames) 1349 { 1350 if (fileSystemIsCaseSensitive) 1351 { 1352 deleteJarFiles(jarFileNames); 1353 } 1354 } 1355 1356 private void deleteJarFiles(final String... jarFileNames) 1357 { 1358 for (final String jarFileName : jarFileNames) 1359 { 1360 final File f = new File(libDirectory, jarFileName + ".jar"); 1361 if (f.exists()) 1362 { 1363 f.delete(); 1364 } 1365 } 1366 } 1367 1368 /** Used to know if we have to remove old "camel case" OpenDJ[_]*.jar(see OPENDJ-2692). */ 1369 private boolean fileSystemIsCaseSensitive(final UpgradeContext context) throws ClientException 1370 { 1371 final File openDJCamelCaseJar = new File(libDirectory, "OpenDJ.jar"); 1372 try 1373 { 1374 // getCanonicalPath() will return the new "opendj.jar" on case insensitive file systems 1375 return openDJCamelCaseJar.getCanonicalPath().equals(openDJCamelCaseJar.getAbsolutePath()); 1376 } 1377 catch (final IOException unlikely) 1378 { 1379 // Warn the user that he may have some old camel case jars to remove 1380 context.notify(INFO_UPGRADE_TASK_UNABLE_TO_REMOVE_OLD_JARS.get()); 1381 Upgrade.needToExitWithErrorCode(); 1382 return false; 1383 } 1384 } 1385 }; 1386 1387 } 1388 1389 /** Prevent instantiation. */ 1390 private UpgradeTasks() 1391 { 1392 // Do nothing. 1393 } 1394}