001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2009 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS. 026 */ 027package org.opends.server.tools; 028 029import static org.opends.messages.ToolMessages.*; 030import static org.opends.server.config.ConfigConstants.*; 031import static org.opends.server.util.ServerConstants.*; 032import static org.opends.server.util.StaticUtils.*; 033 034import static com.forgerock.opendj.cli.ArgumentConstants.*; 035import static com.forgerock.opendj.cli.Utils.*; 036 037import java.io.File; 038import java.io.OutputStream; 039import java.io.PrintStream; 040import java.text.SimpleDateFormat; 041import java.util.ArrayList; 042import java.util.Date; 043import java.util.HashMap; 044import java.util.HashSet; 045import java.util.List; 046import java.util.TimeZone; 047 048import org.forgerock.i18n.slf4j.LocalizedLogger; 049import org.forgerock.opendj.config.server.ConfigException; 050import org.forgerock.util.Utils; 051import org.opends.server.admin.std.server.BackendCfg; 052import org.opends.server.api.Backend; 053import org.opends.server.api.Backend.BackendOperation; 054import org.opends.server.core.CoreConfigManager; 055import org.opends.server.core.DirectoryServer; 056import org.opends.server.core.LockFileManager; 057import org.opends.server.extensions.ConfigFileHandler; 058import org.opends.server.loggers.DebugLogger; 059import org.opends.server.loggers.ErrorLogPublisher; 060import org.opends.server.loggers.ErrorLogger; 061import org.opends.server.loggers.JDKLogging; 062import org.opends.server.loggers.TextErrorLogPublisher; 063import org.opends.server.loggers.TextWriter; 064import org.opends.server.protocols.ldap.LDAPAttribute; 065import org.opends.server.tasks.BackupTask; 066import org.opends.server.tools.tasks.TaskTool; 067import org.opends.server.types.BackupConfig; 068import org.opends.server.types.BackupDirectory; 069import org.opends.server.types.DN; 070import org.opends.server.types.DirectoryException; 071import org.opends.server.types.InitializationException; 072import org.opends.server.types.NullOutputStream; 073import org.opends.server.types.RawAttribute; 074import org.opends.server.util.args.LDAPConnectionArgumentParser; 075 076import com.forgerock.opendj.cli.Argument; 077import com.forgerock.opendj.cli.ArgumentException; 078import com.forgerock.opendj.cli.BooleanArgument; 079import com.forgerock.opendj.cli.ClientException; 080import com.forgerock.opendj.cli.CommonArguments; 081import com.forgerock.opendj.cli.StringArgument; 082 083/** 084 * This program provides a utility that may be used to back up a Directory 085 * Server backend in a binary form that may be quickly archived and restored. 086 * The format of the backup may vary based on the backend type and does not need 087 * to be something that can be handled by any other backend type. This will be 088 * a process that is intended to run separate from Directory Server and not 089 * internally within the server process (e.g., via the tasks interface). 090 */ 091public class BackUpDB extends TaskTool 092{ 093 094 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 095 096 /** 097 * The main method for BackUpDB tool. 098 * 099 * @param args The command-line arguments provided to this program. 100 */ 101 public static void main(String[] args) 102 { 103 int retCode = mainBackUpDB(args, true, System.out, System.err); 104 105 if(retCode != 0) 106 { 107 System.exit(filterExitCode(retCode)); 108 } 109 } 110 111 /** 112 * Processes the command-line arguments and invokes the backup process. 113 * 114 * @param args The command-line arguments provided to this program. 115 * 116 * @return The error code. 117 */ 118 public static int mainBackUpDB(String[] args) 119 { 120 return mainBackUpDB(args, true, System.out, System.err); 121 } 122 123 /** 124 * Processes the command-line arguments and invokes the backup process. 125 * 126 * @param args The command-line arguments provided to this 127 * program. 128 * @param initializeServer Indicates whether to initialize the server. 129 * @param outStream The output stream to use for standard output, or 130 * {@code null} if standard output is not needed. 131 * @param errStream The output stream to use for standard error, or 132 * {@code null} if standard error is not needed. 133 * 134 * @return The error code. 135 */ 136 public static int mainBackUpDB(String[] args, boolean initializeServer, 137 OutputStream outStream, OutputStream errStream) 138 { 139 BackUpDB tool = new BackUpDB(); 140 return tool.process(args, initializeServer, outStream, errStream); 141 } 142 143 /** Define the command-line arguments that may be used with this program. */ 144 private BooleanArgument backUpAll; 145 private BooleanArgument compress; 146 private BooleanArgument displayUsage; 147 private BooleanArgument encrypt; 148 private BooleanArgument hash; 149 private BooleanArgument incremental; 150 private BooleanArgument signHash; 151 private StringArgument backendID; 152 private StringArgument backupIDString; 153 private StringArgument configClass; 154 private StringArgument configFile; 155 private StringArgument backupDirectory; 156 private StringArgument incrementalBaseID; 157 158 private int process(String[] args, boolean initializeServer, 159 OutputStream outStream, OutputStream errStream) 160 { 161 PrintStream out = NullOutputStream.wrapOrNullStream(outStream); 162 PrintStream err = NullOutputStream.wrapOrNullStream(errStream); 163 JDKLogging.disableLogging(); 164 165 // Create the command-line argument parser for use with this program. 166 LDAPConnectionArgumentParser argParser = 167 createArgParser("org.opends.server.tools.BackUpDB", 168 INFO_BACKUPDB_TOOL_DESCRIPTION.get()); 169 argParser.setShortToolDescription(REF_SHORT_DESC_BACKUP.get()); 170 171 172 // Initialize all the command-line argument types and register them with the 173 // parser. 174 try 175 { 176 configClass = 177 new StringArgument( 178 "configclass", OPTION_SHORT_CONFIG_CLASS, 179 OPTION_LONG_CONFIG_CLASS, true, false, 180 true, INFO_CONFIGCLASS_PLACEHOLDER.get(), 181 ConfigFileHandler.class.getName(), null, 182 INFO_DESCRIPTION_CONFIG_CLASS.get()); 183 configClass.setHidden(true); 184 argParser.addArgument(configClass); 185 186 187 configFile = 188 new StringArgument( 189 "configfile", 'f', "configFile", true, false, 190 true, INFO_CONFIGFILE_PLACEHOLDER.get(), null, null, 191 INFO_DESCRIPTION_CONFIG_FILE.get()); 192 configFile.setHidden(true); 193 argParser.addArgument(configFile); 194 195 196 backendID = 197 new StringArgument( 198 "backendid", 'n', "backendID", false, true, true, 199 INFO_BACKENDNAME_PLACEHOLDER.get(), null, null, 200 INFO_BACKUPDB_DESCRIPTION_BACKEND_ID.get()); 201 argParser.addArgument(backendID); 202 203 204 backUpAll = new BooleanArgument( 205 "backupall", 'a', "backUpAll", 206 INFO_BACKUPDB_DESCRIPTION_BACKUP_ALL.get()); 207 argParser.addArgument(backUpAll); 208 209 210 backupIDString = 211 new StringArgument( 212 "backupid", 'I', "backupID", false, false, true, 213 INFO_BACKUPID_PLACEHOLDER.get(), null, null, 214 INFO_BACKUPDB_DESCRIPTION_BACKUP_ID.get()); 215 argParser.addArgument(backupIDString); 216 217 218 backupDirectory = 219 new StringArgument( 220 "backupdirectory", 'd', "backupDirectory", true, 221 false, true, INFO_BACKUPDIR_PLACEHOLDER.get(), null, null, 222 INFO_BACKUPDB_DESCRIPTION_BACKUP_DIR.get()); 223 argParser.addArgument(backupDirectory); 224 225 226 incremental = new BooleanArgument( 227 "incremental", 'i', "incremental", 228 INFO_BACKUPDB_DESCRIPTION_INCREMENTAL.get()); 229 argParser.addArgument(incremental); 230 231 232 incrementalBaseID = 233 new StringArgument( 234 "incrementalbaseid", 'B', "incrementalBaseID", 235 false, false, true, INFO_BACKUPID_PLACEHOLDER.get(), null, 236 null, 237 INFO_BACKUPDB_DESCRIPTION_INCREMENTAL_BASE_ID.get()); 238 argParser.addArgument(incrementalBaseID); 239 240 241 compress = new BooleanArgument( 242 "compress", OPTION_SHORT_COMPRESS, 243 OPTION_LONG_COMPRESS, 244 INFO_BACKUPDB_DESCRIPTION_COMPRESS.get()); 245 argParser.addArgument(compress); 246 247 248 encrypt = new BooleanArgument( 249 "encrypt", 'y', "encrypt", 250 INFO_BACKUPDB_DESCRIPTION_ENCRYPT.get()); 251 argParser.addArgument(encrypt); 252 253 254 hash = new BooleanArgument( 255 "hash", 'A', "hash", 256 INFO_BACKUPDB_DESCRIPTION_HASH.get()); 257 argParser.addArgument(hash); 258 259 260 signHash = 261 new BooleanArgument( 262 "signhash", 's', "signHash", 263 INFO_BACKUPDB_DESCRIPTION_SIGN_HASH.get()); 264 argParser.addArgument(signHash); 265 266 267 displayUsage = CommonArguments.getShowUsage(); 268 argParser.addArgument(displayUsage); 269 argParser.setUsageArgument(displayUsage); 270 } 271 catch (ArgumentException ae) 272 { 273 printWrappedText(err, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage())); 274 return 1; 275 } 276 277 // Init the default values so that they can appear also on the usage. 278 try 279 { 280 argParser.getArguments().initArgumentsWithConfiguration(); 281 } 282 catch (ConfigException ce) 283 { 284 // Ignore. 285 } 286 287 // Parse the command-line arguments provided to this program. 288 try 289 { 290 argParser.parseArguments(args); 291 validateTaskArgs(); 292 } 293 catch (ArgumentException ae) 294 { 295 argParser.displayMessageAndUsageReference(err, ERR_ERROR_PARSING_ARGS.get(ae.getMessage())); 296 return 1; 297 } 298 catch (ClientException ce) 299 { 300 // No need to display the usage since the problem comes with a provided value. 301 printWrappedText(err, ce.getMessageObject()); 302 return 1; 303 } 304 305 306 // If we should just display usage or version information, 307 // then print it and exit. 308 if (argParser.usageOrVersionDisplayed()) 309 { 310 return 0; 311 } 312 313 // Make sure that either the backUpAll argument was provided or at least one 314 // backend ID was given. They are mutually exclusive. 315 if (backUpAll.isPresent()) 316 { 317 if (backendID.isPresent()) 318 { 319 argParser.displayMessageAndUsageReference(err, ERR_BACKUPDB_CANNOT_MIX_BACKUP_ALL_AND_BACKEND_ID.get( 320 backUpAll.getLongIdentifier(), backendID.getLongIdentifier())); 321 return 1; 322 } 323 } 324 else if (! backendID.isPresent()) 325 { 326 argParser.displayMessageAndUsageReference(err, ERR_BACKUPDB_NEED_BACKUP_ALL_OR_BACKEND_ID.get( 327 backUpAll.getLongIdentifier(), backendID.getLongIdentifier())); 328 return 1; 329 } 330 else 331 { 332 // Check that the backendID has not been expressed twice. 333 HashSet<String> backendIDLowerCase = new HashSet<>(); 334 HashSet<String> repeatedBackendIds = new HashSet<>(); 335 for (String id : backendID.getValues()) 336 { 337 String lId = id.toLowerCase(); 338 if (!backendIDLowerCase.add(lId)) 339 { 340 repeatedBackendIds.add(lId); 341 } 342 } 343 if (!repeatedBackendIds.isEmpty()) 344 { 345 argParser.displayMessageAndUsageReference(err, 346 ERR_BACKUPDB_REPEATED_BACKEND_ID.get(Utils.joinAsString(", ", repeatedBackendIds))); 347 return 1; 348 } 349 } 350 351 // If the incremental base ID was specified, then make sure it is an 352 // incremental backup. 353 if (incrementalBaseID.isPresent() && ! incremental.isPresent()) 354 { 355 argParser.displayMessageAndUsageReference(err, ERR_BACKUPDB_INCREMENTAL_BASE_REQUIRES_INCREMENTAL.get( 356 incrementalBaseID.getLongIdentifier(), incremental.getLongIdentifier())); 357 return 1; 358 } 359 360 // Encryption or signing requires the ADS backend be available for 361 // CryptoManager access to secret key entries. If no connection arguments 362 // are present, infer an offline backup. 363 if ((encrypt.isPresent() || signHash.isPresent()) 364 && ! argParser.connectionArgumentsPresent()) { 365 argParser.displayMessageAndUsageReference(err, ERR_BACKUPDB_ENCRYPT_OR_SIGN_REQUIRES_ONLINE.get( 366 encrypt.getLongIdentifier(), signHash.getLongIdentifier())); 367 return 1; 368 } 369 370 // If the signHash option was provided, then make sure that the hash option 371 // was given. 372 if (signHash.isPresent() && !hash.isPresent()) 373 { 374 argParser.displayMessageAndUsageReference(err, 375 ERR_BACKUPDB_SIGN_REQUIRES_HASH.get(signHash.getLongIdentifier(), hash.getLongIdentifier())); 376 return 1; 377 } 378 379 380 // Checks the version - if upgrade required, the tool is unusable 381 try 382 { 383 checkVersion(); 384 } 385 catch (InitializationException e) 386 { 387 printWrappedText(err, e.getMessage()); 388 return 1; 389 } 390 391 return process(argParser, initializeServer, out, err); 392 393 } 394 395 /** {@inheritDoc} */ 396 @Override 397 public void addTaskAttributes(List<RawAttribute> attributes) 398 { 399 addIfHasValue(attributes, ATTR_TASK_BACKUP_ALL, backUpAll); 400 addIfHasValue(attributes, ATTR_TASK_BACKUP_COMPRESS, compress); 401 addIfHasValue(attributes, ATTR_TASK_BACKUP_ENCRYPT, encrypt); 402 addIfHasValue(attributes, ATTR_TASK_BACKUP_HASH, hash); 403 addIfHasValue(attributes, ATTR_TASK_BACKUP_INCREMENTAL, incremental); 404 addIfHasValue(attributes, ATTR_TASK_BACKUP_SIGN_HASH, signHash); 405 406 List<String> backendIDs = backendID.getValues(); 407 if (backendIDs != null && !backendIDs.isEmpty()) { 408 attributes.add( 409 new LDAPAttribute(ATTR_TASK_BACKUP_BACKEND_ID, backendIDs)); 410 } 411 412 addIfHasValue(attributes, ATTR_BACKUP_ID, backupIDString); 413 addIfHasValue(attributes, ATTR_BACKUP_DIRECTORY_PATH, backupDirectory); 414 addIfHasValue(attributes, ATTR_TASK_BACKUP_INCREMENTAL_BASE_ID, incrementalBaseID); 415 } 416 417 private void addIfHasValue(List<RawAttribute> attributes, String attrName, Argument arg) 418 { 419 if (hasValueDifferentThanDefaultValue(arg)) { 420 attributes.add(new LDAPAttribute(attrName, arg.getValue())); 421 } 422 } 423 424 private boolean hasValueDifferentThanDefaultValue(Argument arg) 425 { 426 return arg.getValue() != null 427 && !arg.getValue().equals(arg.getDefaultValue()); 428 } 429 430 /** {@inheritDoc} */ 431 @Override 432 public String getTaskObjectclass() { 433 return "ds-task-backup"; 434 } 435 436 /** {@inheritDoc} */ 437 @Override 438 public Class<?> getTaskClass() { 439 return BackupTask.class; 440 } 441 442 /** {@inheritDoc} */ 443 @Override 444 protected int processLocal(boolean initializeServer, 445 PrintStream out, 446 PrintStream err) { 447 448 // Make sure that the backup directory exists. If not, then create it. 449 File backupDirFile = new File(backupDirectory.getValue()); 450 if (! backupDirFile.exists()) 451 { 452 try 453 { 454 backupDirFile.mkdirs(); 455 } 456 catch (Exception e) 457 { 458 printWrappedText( 459 err, ERR_BACKUPDB_CANNOT_CREATE_BACKUP_DIR.get(backupDirectory.getValue(), getExceptionMessage(e))); 460 return 1; 461 } 462 } 463 464 // If no backup ID was provided, then create one with the current timestamp. 465 String backupID; 466 if (backupIDString.isPresent()) 467 { 468 backupID = backupIDString.getValue(); 469 } 470 else 471 { 472 SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME); 473 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 474 backupID = dateFormat.format(new Date()); 475 } 476 477 // If the incremental base ID was specified, then make sure it is an 478 // incremental backup. 479 String incrementalBase; 480 if (incrementalBaseID.isPresent()) 481 { 482 incrementalBase = incrementalBaseID.getValue(); 483 } 484 else 485 { 486 incrementalBase = null; 487 } 488 489 // Perform the initial bootstrap of the Directory Server and process the 490 // configuration. 491 DirectoryServer directoryServer = DirectoryServer.getInstance(); 492 if (initializeServer) 493 { 494 try 495 { 496 DirectoryServer.bootstrapClient(); 497 DirectoryServer.initializeJMX(); 498 } 499 catch (Exception e) 500 { 501 printWrappedText(err, ERR_SERVER_BOOTSTRAP_ERROR.get(getExceptionMessage(e))); 502 return 1; 503 } 504 505 try 506 { 507 directoryServer.initializeConfiguration(configClass.getValue(), 508 configFile.getValue()); 509 } 510 catch (InitializationException ie) 511 { 512 printWrappedText(err, ERR_CANNOT_LOAD_CONFIG.get(ie.getMessage())); 513 return 1; 514 } 515 catch (Exception e) 516 { 517 printWrappedText(err, ERR_CANNOT_LOAD_CONFIG.get(getExceptionMessage(e))); 518 return 1; 519 } 520 521 522 523 // Initialize the Directory Server schema elements. 524 try 525 { 526 directoryServer.initializeSchema(); 527 } 528 catch (ConfigException | InitializationException e) 529 { 530 printWrappedText(err, ERR_CANNOT_LOAD_SCHEMA.get(e.getMessage())); 531 return 1; 532 } 533 catch (Exception e) 534 { 535 printWrappedText(err, ERR_CANNOT_LOAD_SCHEMA.get(getExceptionMessage(e))); 536 return 1; 537 } 538 539 540 // Initialize the Directory Server core configuration. 541 try 542 { 543 CoreConfigManager coreConfigManager = new CoreConfigManager(directoryServer.getServerContext()); 544 coreConfigManager.initializeCoreConfig(); 545 } 546 catch (ConfigException | InitializationException e) 547 { 548 printWrappedText(err, ERR_CANNOT_INITIALIZE_CORE_CONFIG.get(e.getMessage())); 549 return 1; 550 } 551 catch (Exception e) 552 { 553 printWrappedText(err, ERR_CANNOT_INITIALIZE_CORE_CONFIG.get(getExceptionMessage(e))); 554 return 1; 555 } 556 557 558 // Initialize the Directory Server crypto manager. 559 try 560 { 561 directoryServer.initializeCryptoManager(); 562 } 563 catch (ConfigException | InitializationException e) 564 { 565 printWrappedText(err, ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get(e.getMessage())); 566 return 1; 567 } 568 catch (Exception e) 569 { 570 printWrappedText(err, ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get(getExceptionMessage(e))); 571 return 1; 572 } 573 574 try 575 { 576 ErrorLogPublisher errorLogPublisher = 577 TextErrorLogPublisher.getToolStartupTextErrorPublisher( 578 new TextWriter.STREAM(out)); 579 ErrorLogger.getInstance().addLogPublisher(errorLogPublisher); 580 DebugLogger.getInstance().addPublisherIfRequired(new TextWriter.STREAM(out)); 581 } 582 catch(Exception e) 583 { 584 err.println("Error installing the custom error logger: " + 585 stackTraceToSingleLineString(e)); 586 } 587 } 588 589 590 // Get information about the backends defined in the server, and determine 591 // whether we are backing up multiple backends or a single backend. 592 ArrayList<Backend> backendList = new ArrayList<>(); 593 ArrayList<BackendCfg> entryList = new ArrayList<>(); 594 ArrayList<List<DN>> dnList = new ArrayList<>(); 595 BackendToolUtils.getBackends(backendList, entryList, dnList); 596 int numBackends = backendList.size(); 597 598 boolean multiple; 599 ArrayList<Backend<?>> backendsToArchive = new ArrayList<>(numBackends); 600 HashMap<String,BackendCfg> configEntries = new HashMap<>(numBackends); 601 if (backUpAll.isPresent()) 602 { 603 for (int i=0; i < numBackends; i++) 604 { 605 Backend<?> b = backendList.get(i); 606 if (b.supports(BackendOperation.BACKUP)) 607 { 608 backendsToArchive.add(b); 609 configEntries.put(b.getBackendID(), entryList.get(i)); 610 } 611 } 612 613 // We'll proceed as if we're backing up multiple backends in this case 614 // even if there's just one. 615 multiple = true; 616 } 617 else 618 { 619 // Iterate through the set of backends and pick out those that were requested. 620 HashSet<String> requestedBackends = new HashSet<>(backendID.getValues()); 621 for (int i=0; i < numBackends; i++) 622 { 623 Backend<?> b = backendList.get(i); 624 if (requestedBackends.contains(b.getBackendID())) 625 { 626 if (!b.supports(BackendOperation.BACKUP)) 627 { 628 logger.warn(WARN_BACKUPDB_BACKUP_NOT_SUPPORTED, b.getBackendID()); 629 } 630 else 631 { 632 backendsToArchive.add(b); 633 configEntries.put(b.getBackendID(), entryList.get(i)); 634 requestedBackends.remove(b.getBackendID()); 635 } 636 } 637 } 638 639 if (! requestedBackends.isEmpty()) 640 { 641 for (String id : requestedBackends) 642 { 643 logger.error(ERR_BACKUPDB_NO_BACKENDS_FOR_ID, id); 644 } 645 646 return 1; 647 } 648 649 650 // See if there are multiple backends to archive. 651 multiple = backendsToArchive.size() > 1; 652 } 653 654 655 // If there are no backends to archive, then print an error and exit. 656 if (backendsToArchive.isEmpty()) 657 { 658 logger.warn(WARN_BACKUPDB_NO_BACKENDS_TO_ARCHIVE); 659 return 1; 660 } 661 662 663 // Iterate through the backends to archive and back them up individually. 664 boolean errorsEncountered = false; 665 for (Backend<?> b : backendsToArchive) 666 { 667 // Acquire a shared lock for this backend. 668 try 669 { 670 String lockFile = LockFileManager.getBackendLockFileName(b); 671 StringBuilder failureReason = new StringBuilder(); 672 if (! LockFileManager.acquireSharedLock(lockFile, failureReason)) 673 { 674 logger.error(ERR_BACKUPDB_CANNOT_LOCK_BACKEND, b.getBackendID(), failureReason); 675 errorsEncountered = true; 676 continue; 677 } 678 } 679 catch (Exception e) 680 { 681 logger.error(ERR_BACKUPDB_CANNOT_LOCK_BACKEND, b.getBackendID(), getExceptionMessage(e)); 682 errorsEncountered = true; 683 continue; 684 } 685 686 687 logger.info(NOTE_BACKUPDB_STARTING_BACKUP, b.getBackendID()); 688 689 690 // Get the config entry for this backend. 691 BackendCfg configEntry = configEntries.get(b.getBackendID()); 692 693 694 // Get the path to the directory to use for this backup. If we will be 695 // backing up multiple backends (or if we are backing up all backends, 696 // even if there's only one of them), then create a subdirectory for each 697 // backend. 698 String backupDirPath; 699 if (multiple) 700 { 701 backupDirPath = backupDirectory.getValue() + File.separator + 702 b.getBackendID(); 703 } 704 else 705 { 706 backupDirPath = backupDirectory.getValue(); 707 } 708 709 710 // If the directory doesn't exist, then create it. If it does exist, then 711 // see if it has a backup descriptor file. 712 BackupDirectory backupDir; 713 backupDirFile = new File(backupDirPath); 714 if (backupDirFile.exists()) 715 { 716 String descriptorPath = backupDirPath + File.separator + 717 BACKUP_DIRECTORY_DESCRIPTOR_FILE; 718 File descriptorFile = new File(descriptorPath); 719 if (descriptorFile.exists()) 720 { 721 try 722 { 723 backupDir = 724 BackupDirectory.readBackupDirectoryDescriptor(backupDirPath); 725 } 726 catch (ConfigException ce) 727 { 728 logger.error(ERR_BACKUPDB_CANNOT_PARSE_BACKUP_DESCRIPTOR, descriptorPath, ce.getMessage()); 729 errorsEncountered = true; 730 731 try 732 { 733 String lockFile = LockFileManager.getBackendLockFileName(b); 734 StringBuilder failureReason = new StringBuilder(); 735 if (! LockFileManager.releaseLock(lockFile, failureReason)) 736 { 737 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), failureReason); 738 } 739 } 740 catch (Exception e) 741 { 742 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), getExceptionMessage(e)); 743 } 744 745 continue; 746 } 747 catch (Exception e) 748 { 749 logger.error(ERR_BACKUPDB_CANNOT_PARSE_BACKUP_DESCRIPTOR, descriptorPath, getExceptionMessage(e)); 750 errorsEncountered = true; 751 752 try 753 { 754 String lockFile = LockFileManager.getBackendLockFileName(b); 755 StringBuilder failureReason = new StringBuilder(); 756 if (! LockFileManager.releaseLock(lockFile, failureReason)) 757 { 758 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), failureReason); 759 } 760 } 761 catch (Exception e2) 762 { 763 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), getExceptionMessage(e2)); 764 } 765 766 continue; 767 } 768 } 769 else 770 { 771 backupDir = new BackupDirectory(backupDirPath, configEntry.dn()); 772 } 773 } 774 else 775 { 776 try 777 { 778 backupDirFile.mkdirs(); 779 } 780 catch (Exception e) 781 { 782 logger.error(ERR_BACKUPDB_CANNOT_CREATE_BACKUP_DIR, backupDirPath, getExceptionMessage(e)); 783 errorsEncountered = true; 784 785 try 786 { 787 String lockFile = LockFileManager.getBackendLockFileName(b); 788 StringBuilder failureReason = new StringBuilder(); 789 if (! LockFileManager.releaseLock(lockFile, failureReason)) 790 { 791 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), failureReason); 792 } 793 } 794 catch (Exception e2) 795 { 796 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), getExceptionMessage(e2)); 797 } 798 799 continue; 800 } 801 802 backupDir = new BackupDirectory(backupDirPath, configEntry.dn()); 803 } 804 805 806 // Create a backup configuration and determine whether the requested 807 // backup can be performed using the selected backend. 808 BackupConfig backupConfig = new BackupConfig(backupDir, backupID, 809 incremental.isPresent()); 810 backupConfig.setCompressData(compress.isPresent()); 811 backupConfig.setEncryptData(encrypt.isPresent()); 812 backupConfig.setHashData(hash.isPresent()); 813 backupConfig.setSignHash(signHash.isPresent()); 814 backupConfig.setIncrementalBaseID(incrementalBase); 815 816 if (!b.supports(BackendOperation.BACKUP)) 817 { 818 logger.error(ERR_BACKUPDB_CANNOT_BACKUP, b.getBackendID()); 819 errorsEncountered = true; 820 821 try 822 { 823 String lockFile = LockFileManager.getBackendLockFileName(b); 824 StringBuilder failureReason = new StringBuilder(); 825 if (! LockFileManager.releaseLock(lockFile, failureReason)) 826 { 827 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), failureReason); 828 } 829 } 830 catch (Exception e2) 831 { 832 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), getExceptionMessage(e2)); 833 } 834 835 continue; 836 } 837 838 839 // Perform the backup. 840 try 841 { 842 b.createBackup(backupConfig); 843 } 844 catch (DirectoryException de) 845 { 846 logger.error(ERR_BACKUPDB_ERROR_DURING_BACKUP, b.getBackendID(), de.getMessageObject()); 847 errorsEncountered = true; 848 849 try 850 { 851 String lockFile = LockFileManager.getBackendLockFileName(b); 852 StringBuilder failureReason = new StringBuilder(); 853 if (! LockFileManager.releaseLock(lockFile, failureReason)) 854 { 855 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), failureReason); 856 } 857 } 858 catch (Exception e) 859 { 860 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), getExceptionMessage(e)); 861 } 862 863 continue; 864 } 865 catch (Exception e) 866 { 867 logger.error(ERR_BACKUPDB_ERROR_DURING_BACKUP, b.getBackendID(), getExceptionMessage(e)); 868 errorsEncountered = true; 869 870 try 871 { 872 String lockFile = LockFileManager.getBackendLockFileName(b); 873 StringBuilder failureReason = new StringBuilder(); 874 if (! LockFileManager.releaseLock(lockFile, failureReason)) 875 { 876 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), failureReason); 877 } 878 } 879 catch (Exception e2) 880 { 881 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), getExceptionMessage(e2)); 882 } 883 884 continue; 885 } 886 887 888 // Release the shared lock for the backend. 889 try 890 { 891 String lockFile = LockFileManager.getBackendLockFileName(b); 892 StringBuilder failureReason = new StringBuilder(); 893 if (! LockFileManager.releaseLock(lockFile, failureReason)) 894 { 895 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), failureReason); 896 errorsEncountered = true; 897 } 898 } 899 catch (Exception e) 900 { 901 logger.warn(WARN_BACKUPDB_CANNOT_UNLOCK_BACKEND, b.getBackendID(), getExceptionMessage(e)); 902 errorsEncountered = true; 903 } 904 } 905 906 907 // Print a final completed message, indicating whether there were any errors 908 // in the process. 909 int ret = 0; 910 if (errorsEncountered) 911 { 912 logger.info(NOTE_BACKUPDB_COMPLETED_WITH_ERRORS); 913 ret = 1; 914 } 915 else 916 { 917 logger.info(NOTE_BACKUPDB_COMPLETED_SUCCESSFULLY); 918 } 919 return ret; 920 } 921 922 /** {@inheritDoc} */ 923 @Override 924 public String getTaskId() { 925 return backupIDString != null ? backupIDString.getValue() : null; 926 } 927}