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