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