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