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    
029    import org.opends.server.loggers.debug.DebugTracer;
030    import static org.opends.server.loggers.debug.DebugLogger.getTracer;
031    import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
032    import static org.opends.server.util.StaticUtils.*;
033    import org.opends.server.util.args.*;
034    import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH;
035    import org.opends.server.util.StaticUtils;
036    import org.opends.server.util.table.TableBuilder;
037    import org.opends.server.util.table.TextTablePrinter;
038    import org.opends.server.types.*;
039    import static org.opends.messages.ToolMessages.*;
040    import org.opends.messages.Message;
041    import static org.opends.server.tools.ToolConstants.OPTION_SHORT_CONFIG_CLASS;
042    import static org.opends.server.tools.ToolConstants.OPTION_LONG_CONFIG_CLASS;
043    import static org.opends.server.tools.ToolConstants.OPTION_SHORT_HELP;
044    import static org.opends.server.tools.ToolConstants.OPTION_LONG_HELP;
045    import org.opends.server.extensions.ConfigFileHandler;
046    import org.opends.server.core.DirectoryServer;
047    import org.opends.server.core.LockFileManager;
048    import org.opends.server.config.ConfigException;
049    import org.opends.server.api.Backend;
050    import org.opends.server.admin.std.server.BackendCfg;
051    import org.opends.server.admin.std.server.LocalDBBackendCfg;
052    import org.opends.server.backends.jeb.*;
053    import org.opends.server.protocols.asn1.ASN1OctetString;
054    import org.opends.server.protocols.asn1.ASN1Element;
055    
056    import java.io.*;
057    import java.util.*;
058    
059    import com.sleepycat.je.*;
060    
061    /**
062     * This program provides a utility that may be used to debug a JE backend. This
063     * tool provides the ability list various containers in the backend as well as
064     * dump the contents of database containers. This will be
065     * a process that is intended to run separate from Directory Server and not
066     * internally within the server process (e.g., via the tasks interface).
067     */
068    public class DBTest
069    {
070      /**
071       * The tracer object for the debug logger.
072       */
073      private static final DebugTracer TRACER = getTracer();
074    
075      // The error stream which this application should use.
076      private final PrintStream err;
077    
078      // The output stream which this application should use.
079      private final PrintStream out;
080    
081      // Flag indicating whether or not the global arguments have
082      // already been initialized.
083      private boolean globalArgumentsInitialized = false;
084    
085      // The command-line argument parser.
086      private final SubCommandArgumentParser parser;
087    
088      // The argument which should be used to request usage information.
089      private BooleanArgument showUsageArgument;
090    
091      // The argument which should be used to specify the config class.
092      private StringArgument configClass;
093    
094      // THe argument which should be used to specify the config file.
095      private StringArgument configFile;
096    
097      // Flag indicating whether or not the sub-commands have
098      // already been initialized.
099      private boolean subCommandsInitialized = false;
100    
101    
102    
103      /**
104       * Provides the command-line arguments to the main application for
105       * processing.
106       *
107       * @param args
108       *          The set of command-line arguments provided to this
109       *          program.
110       */
111      public static void main(String[] args) {
112        int exitCode = main(args, true, System.out, System.err);
113        if (exitCode != 0) {
114          System.exit(filterExitCode(exitCode));
115        }
116      }
117    
118    
119      /**
120       * Provides the command-line arguments to the main application for
121       * processing and returns the exit code as an integer.
122       *
123       * @param args
124       *          The set of command-line arguments provided to this
125       *          program.
126       * @param initializeServer
127       *          Indicates whether to perform basic initialization (which
128       *          should not be done if the tool is running in the same
129       *          JVM as the server).
130       * @param outStream
131       *          The output stream for standard output.
132       * @param errStream
133       *          The output stream for standard error.
134       * @return Zero to indicate that the program completed successfully,
135       *         or non-zero to indicate that an error occurred.
136       */
137      public static int main(String[] args, boolean initializeServer,
138                             OutputStream outStream, OutputStream errStream) {
139        DBTest app = new DBTest(outStream, errStream);
140    
141        // Run the application.
142        return app.run(args, initializeServer);
143      }
144    
145      /**
146       * Creates a new dsconfig application instance.
147       *
148       * @param out
149       *          The application output stream.
150       * @param err
151       *          The application error stream.
152       */
153      public DBTest(OutputStream out, OutputStream err)
154      {
155        if (out != null) {
156          this.out = new PrintStream(out);
157        } else {
158          this.out = NullOutputStream.printStream();
159        }
160    
161        if (err != null) {
162          this.err = new PrintStream(err);
163        } else {
164          this.err = NullOutputStream.printStream();
165        }
166    
167        Message toolDescription = INFO_DESCRIPTION_DBTEST_TOOL.get();
168        this.parser = new SubCommandArgumentParser(this.getClass().getName(),
169                                                   toolDescription, false);
170      }
171    
172      // Displays the provided message followed by a help usage reference.
173      private void displayMessageAndUsageReference(Message message) {
174        printMessage(message);
175        printMessage(Message.EMPTY);
176        printMessage(parser.getHelpUsageReference());
177      }
178    
179    
180    
181      /**
182       * Registers the global arguments with the argument parser.
183       *
184       * @throws ArgumentException
185       *           If a global argument could not be registered.
186       */
187      private void initializeGlobalArguments() throws ArgumentException {
188        if (!globalArgumentsInitialized) {
189          configClass =
190              new StringArgument("configclass", OPTION_SHORT_CONFIG_CLASS,
191                                 OPTION_LONG_CONFIG_CLASS, true, false,
192                                 true, INFO_CONFIGCLASS_PLACEHOLDER.get(),
193                                 ConfigFileHandler.class.getName(), null,
194                                 INFO_DESCRIPTION_CONFIG_CLASS.get());
195          configClass.setHidden(true);
196    
197          configFile =
198              new StringArgument("configfile", 'f', "configFile", true, false,
199                                 true, INFO_CONFIGFILE_PLACEHOLDER.get(), null,
200                                 null,
201                                 INFO_DESCRIPTION_CONFIG_FILE.get());
202          configFile.setHidden(true);
203    
204    
205          showUsageArgument =
206              new BooleanArgument("help", OPTION_SHORT_HELP, OPTION_LONG_HELP,
207                                  INFO_DESCRIPTION_USAGE.get());
208    
209          // Register the global arguments.
210          parser.addGlobalArgument(showUsageArgument);
211          parser.setUsageArgument(showUsageArgument, out);
212          parser.addGlobalArgument(configClass);
213          parser.addGlobalArgument(configFile);
214    
215          globalArgumentsInitialized = true;
216        }
217      }
218    
219    
220    
221      /**
222       * Registers the sub-commands with the argument parser.
223       *
224       * @throws ArgumentException
225       *           If a sub-command could not be created.
226       */
227      private void initializeSubCommands() throws ArgumentException {
228        if (!subCommandsInitialized) {
229          StringArgument backendID;
230          StringArgument baseDN;
231          StringArgument databaseName;
232          BooleanArgument skipDecode;
233          StringArgument maxKeyValue;
234          StringArgument minKeyValue;
235          IntegerArgument maxDataSize;
236          IntegerArgument minDataSize;
237          SubCommand sub;
238    
239          sub = new SubCommand(parser, "list-root-containers",
240                         INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_ROOT_CONTAINERS.get());
241    
242    
243          sub = new SubCommand(parser, "list-entry-containers",
244                        INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_ENTRY_CONTAINERS.get());
245          backendID =
246              new StringArgument("backendid", 'n', "backendID", true, false, true,
247                                 INFO_BACKENDNAME_PLACEHOLDER.get(), null, null,
248                                 INFO_DESCRIPTION_DBTEST_BACKEND_ID.get());
249          sub.addArgument(backendID);
250    
251    
252          sub = new SubCommand(parser, "list-database-containers",
253                     INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_DATABASE_CONTAINERS.get());
254          backendID =
255              new StringArgument("backendid", 'n', "backendID", true, false, true,
256                                 INFO_BACKENDNAME_PLACEHOLDER.get(), null, null,
257                                 INFO_DESCRIPTION_DBTEST_BACKEND_ID.get());
258          sub.addArgument(backendID);
259          baseDN =
260              new StringArgument("basedn", 'b', "baseDN", false,
261                                 false, true, INFO_BASEDN_PLACEHOLDER.get(), null,
262                                 null,
263                                 INFO_DESCRIPTION_DBTEST_BASE_DN.get());
264          sub.addArgument(baseDN);
265    
266    
267          sub = new SubCommand(parser, "dump-database-container",
268                      INFO_DESCRIPTION_DBTEST_SUBCMD_DUMP_DATABASE_CONTAINER.get());
269          backendID =
270              new StringArgument("backendid", 'n', "backendID", true, false, true,
271                                 INFO_BACKENDNAME_PLACEHOLDER.get(), null, null,
272                                 INFO_DESCRIPTION_DBTEST_BACKEND_ID.get());
273          sub.addArgument(backendID);
274          baseDN =
275              new StringArgument("basedn", 'b', "baseDN", true,
276                                 false, true, INFO_BASEDN_PLACEHOLDER.get(), null,
277                                 null,
278                                 INFO_DESCRIPTION_DBTEST_BASE_DN.get());
279          sub.addArgument(baseDN);
280          databaseName =
281              new StringArgument("databasename", 'd', "databaseName", true,
282                                 false, true, INFO_DATABASE_NAME_PLACEHOLDER.get(),
283                                 null, null,
284                                 INFO_DESCRIPTION_DBTEST_DATABASE_NAME.get());
285          sub.addArgument(databaseName);
286          skipDecode =
287              new BooleanArgument("skipdecode", 'p', "skipDecode",
288                                  INFO_DESCRIPTION_DBTEST_SKIP_DECODE.get());
289          sub.addArgument(skipDecode);
290          maxKeyValue = new StringArgument("maxkeyvalue", 'K', "maxKeyValue", false,
291                                           false, true,
292                                           INFO_MAX_KEY_VALUE_PLACEHOLDER.get(),
293                                           null, null,
294                                       INFO_DESCRIPTION_DBTEST_MAX_KEY_VALUE.get());
295          sub.addArgument(maxKeyValue);
296          minKeyValue = new StringArgument("minkeyvalue", 'k', "minKeyValue", false,
297                                           false, true,
298                                           INFO_MIN_KEY_VALUE_PLACEHOLDER.get(),
299                                           null,
300                                           null,
301                                       INFO_DESCRIPTION_DBTEST_MIN_KEY_VALUE.get());
302          sub.addArgument(minKeyValue);
303          maxDataSize = new IntegerArgument("maxdatasize", 'S', "maxDataSize",
304                                            false, false, true,
305                                            INFO_MAX_DATA_SIZE_PLACEHOLDER.get(),
306                                            -1,
307                                            null,
308                                       INFO_DESCRIPTION_DBTEST_MAX_DATA_SIZE.get());
309          sub.addArgument(maxDataSize);
310          minDataSize = new IntegerArgument("mindatasize", 's', "minDataSize",
311                                            false, false, true,
312                                            INFO_MIN_DATA_SIZE_PLACEHOLDER.get(),
313                                            -1,
314                                            null,
315                                       INFO_DESCRIPTION_DBTEST_MIN_DATA_SIZE.get());
316          sub.addArgument(minDataSize);
317    
318    
319          sub = new SubCommand(parser, "list-index-status",
320                            INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_INDEX_STATUS.get());
321          backendID =
322              new StringArgument("backendid", 'n', "backendID", true, false, true,
323                                 INFO_BACKENDNAME_PLACEHOLDER.get(), null, null,
324                                 INFO_DESCRIPTION_DBTEST_BACKEND_ID.get());
325          sub.addArgument(backendID);
326          baseDN =
327              new StringArgument("basedn", 'b', "baseDN", true,
328                                 true, true, INFO_BASEDN_PLACEHOLDER.get(), null,
329                                 null,
330                                 INFO_DESCRIPTION_DBTEST_BASE_DN.get());
331          sub.addArgument(baseDN);
332    
333          subCommandsInitialized = true;
334        }
335      }
336    
337    
338      /**
339       * Parses the provided command-line arguments and makes the
340       * appropriate changes to the Directory Server configuration.
341       *
342       * @param args
343       *          The command-line arguments provided to this program.
344       * @param initializeServer
345       *          Indicates whether to perform basic initialization (which
346       *          should not be done if the tool is running in the same
347       *          JVM as the server).
348       * @return The exit code from the configuration processing. A
349       *         nonzero value indicates that there was some kind of
350       *         problem during the configuration processing.
351       */
352      private int run(String[] args, boolean initializeServer) {
353        // Register global arguments and sub-commands.
354        try {
355          initializeGlobalArguments();
356          initializeSubCommands();
357        } catch (ArgumentException e) {
358          Message message = ERR_CANNOT_INITIALIZE_ARGS.get(e.getMessage());
359          printMessage(message);
360          return 1;
361        }
362    
363        // Parse the command-line arguments provided to this program.
364        try {
365          parser.parseArguments(args);
366        } catch (ArgumentException ae) {
367          Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
368          displayMessageAndUsageReference(message);
369          return 1;
370        }
371    
372        // If the usage/version argument was provided, then we don't need
373        // to do anything else.
374        if (parser.usageOrVersionDisplayed()) {
375          return 0;
376        }
377    
378        // Only initialize the server when run as a standalone
379        // application.
380        if (initializeServer) {
381          // Perform the initial bootstrap of the Directory Server and process the
382          // configuration.
383          DirectoryServer directoryServer = DirectoryServer.getInstance();
384          try
385          {
386            directoryServer.bootstrapClient();
387            directoryServer.initializeJMX();
388          }
389          catch (Exception e)
390          {
391            Message message = ERR_SERVER_BOOTSTRAP_ERROR.get(
392                    getExceptionMessage(e));
393            printMessage(message);
394            return 1;
395          }
396    
397          try
398          {
399            directoryServer.initializeConfiguration(configClass.getValue(),
400                                                    configFile.getValue());
401          }
402          catch (InitializationException ie)
403          {
404            Message message = ERR_CANNOT_LOAD_CONFIG.get(
405                ie.getMessage());
406            printMessage(message);
407            return 1;
408          }
409          catch (Exception e)
410          {
411            Message message = ERR_CANNOT_LOAD_CONFIG.get(
412                getExceptionMessage(e));
413            printMessage(message);
414            return 1;
415          }
416    
417    
418    
419          // Initialize the Directory Server schema elements.
420          try
421          {
422            directoryServer.initializeSchema();
423          }
424          catch (ConfigException ce)
425          {
426            Message message = ERR_CANNOT_LOAD_SCHEMA.get(
427                ce.getMessage());
428            printMessage(message);
429            return 1;
430          }
431          catch (InitializationException ie)
432          {
433            Message message = ERR_CANNOT_LOAD_SCHEMA.get(
434                ie.getMessage());
435            printMessage(message);
436            return 1;
437          }
438          catch (Exception e)
439          {
440            Message message = ERR_CANNOT_LOAD_SCHEMA.get(
441                getExceptionMessage(e));
442            printMessage(message);
443            return 1;
444          }
445        }
446    
447        // Make sure that we have a sub-command.
448        if (parser.getSubCommand() == null)
449        {
450          Message message = ERR_DBTEST_MISSING_SUBCOMMAND.get();
451          displayMessageAndUsageReference(message);
452          return 1;
453        }
454    
455        // Retrieve the sub-command implementation and run it.
456        SubCommand subCommand = parser.getSubCommand();
457        try {
458          if(subCommand.getName().equals("list-root-containers"))
459          {
460            return listRootContainers();
461          }
462          else if(subCommand.getName().equals("list-entry-containers"))
463          {
464            return listEntryContainers(subCommand.getArgument("backendid"));
465          }
466          else if(subCommand.getName().equals("list-database-containers"))
467          {
468            return listDatabaseContainers(subCommand.getArgument("backendid"),
469                                          subCommand.getArgument("basedn"));
470          }
471          else if(subCommand.getName().equals("dump-database-container"))
472          {
473            return dumpDatabaseContainer(subCommand.getArgument("backendid"),
474                                         subCommand.getArgument("basedn"),
475                                         subCommand.getArgument("databasename"),
476                                         subCommand.getArgument("skipdecode"),
477                                         subCommand.getArgument("maxkeyvalue"),
478                                         subCommand.getArgument("minkeyvalue"),
479                                         subCommand.getArgument("maxdatasize"),
480                                         subCommand.getArgument("mindatasize"));
481          }
482          else if(subCommand.getName().equals("list-index-status"))
483          {
484            return listIndexStatus(subCommand.getArgument("backendid"),
485                                          subCommand.getArgument("basedn"));
486          }
487          {
488            return 0;
489          }
490        } catch (Exception e) {
491          if (debugEnabled()) {
492            TRACER.debugCaught(DebugLogLevel.ERROR, e);
493          }
494          printMessage(Message.raw(StaticUtils.stackTraceToString(e)));
495          return 1;
496        }
497      }
498    
499      private int listRootContainers()
500      {
501        TreeMap<LocalDBBackendCfg, BackendImpl> jeBackends = getJEBackends();
502        int count = 0;
503    
504        // Create a table of their properties.
505        TableBuilder builder = new TableBuilder();
506    
507        builder.appendHeading(INFO_LABEL_DBTEST_BACKEND_ID.get());
508        builder.appendHeading(INFO_LABEL_DBTEST_DB_DIRECTORY.get());
509    
510        for(Map.Entry<LocalDBBackendCfg, BackendImpl> backend :
511            jeBackends.entrySet())
512        {
513          builder.startRow();
514          builder.appendCell(backend.getValue().getBackendID());
515          builder.appendCell(backend.getKey().getDBDirectory());
516          count++;
517        }
518    
519        TextTablePrinter printer = new TextTablePrinter(out);
520        builder.print(printer);
521        out.format("%nTotal: %d%n", count);
522    
523        return 0;
524      }
525    
526      private int listEntryContainers(Argument backendID)
527      {
528        TreeMap<LocalDBBackendCfg, BackendImpl> jeBackends = getJEBackends();
529        BackendImpl backend = null;
530    
531        for(BackendImpl b : jeBackends.values())
532        {
533          if(b.getBackendID().equalsIgnoreCase(backendID.getValue()))
534          {
535            backend = b;
536            break;
537          }
538        }
539    
540        if(backend == null)
541        {
542          printMessage(ERR_DBTEST_NO_BACKENDS_FOR_ID.get(backendID.getValue()));
543          return 1;
544        }
545    
546        // Acquire an shared lock for the backend.
547        try
548        {
549          String lockFile = LockFileManager.getBackendLockFileName(backend);
550          StringBuilder failureReason = new StringBuilder();
551          if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
552          {
553            Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
554                backend.getBackendID(), String.valueOf(failureReason));
555            printMessage(message);
556            return 1;
557          }
558        }
559        catch (Exception e)
560        {
561          Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
562              backend.getBackendID(), getExceptionMessage(e));
563          printMessage(message);
564          return 1;
565        }
566    
567        RootContainer rc;
568        try
569        {
570          rc = backend.getReadOnlyRootContainer();
571        }
572        catch(Exception e)
573        {
574          printMessage(ERR_DBTEST_ERROR_INITIALIZING_BACKEND.get(
575              backend.getBackendID(),
576              StaticUtils.stackTraceToSingleLineString(e)));
577          return 1;
578        }
579    
580        try
581        {
582          // Create a table of their properties.
583          TableBuilder builder = new TableBuilder();
584          int count = 0;
585    
586          builder.appendHeading(INFO_LABEL_DBTEST_BASE_DN.get());
587          builder.appendHeading(INFO_LABEL_DBTEST_JE_DATABASE_PREFIX.get());
588          builder.appendHeading(INFO_LABEL_DBTEST_ENTRY_COUNT.get());
589    
590          for(EntryContainer ec : rc.getEntryContainers())
591          {
592            builder.startRow();
593            builder.appendCell(ec.getBaseDN().toNormalizedString());
594            builder.appendCell(ec.getDatabasePrefix());
595            builder.appendCell(ec.getEntryCount());
596            count++;
597          }
598    
599          TextTablePrinter printer = new TextTablePrinter(out);
600          builder.print(printer);
601          out.format("%nTotal: %d%n", count);
602    
603          return 0;
604    
605    
606        }
607        catch(DatabaseException de)
608        {
609          printMessage(ERR_DBTEST_ERROR_READING_DATABASE.get(
610              StaticUtils.stackTraceToSingleLineString(de)));
611          return 1;
612        }
613        finally
614        {
615          try
616          {
617            // Close the root container
618            rc.close();
619          }
620          catch(DatabaseException de)
621          {
622            // Ignore.
623          }
624    
625          // Release the shared lock on the backend.
626          try
627          {
628            String lockFile = LockFileManager.getBackendLockFileName(backend);
629            StringBuilder failureReason = new StringBuilder();
630            if (! LockFileManager.releaseLock(lockFile, failureReason))
631            {
632            Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
633                backend.getBackendID(), String.valueOf(failureReason));
634              printMessage(message);
635            }
636          }
637          catch (Exception e)
638          {
639          Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
640              backend.getBackendID(), getExceptionMessage(e));
641            printMessage(message);
642          }
643        }
644      }
645    
646      private int listDatabaseContainers(Argument backendID,
647                                         Argument baseDN)
648      {
649        TreeMap<LocalDBBackendCfg, BackendImpl> jeBackends = getJEBackends();
650        BackendImpl backend = null;
651        DN base = null;
652    
653        for(BackendImpl b : jeBackends.values())
654        {
655          if(b.getBackendID().equalsIgnoreCase(backendID.getValue()))
656          {
657            backend = b;
658            break;
659          }
660        }
661    
662        if(backend == null)
663        {
664          printMessage(ERR_DBTEST_NO_BACKENDS_FOR_ID.get(backendID.getValue()));
665          return 1;
666        }
667    
668        if(baseDN.isPresent())
669        {
670          try
671          {
672            base = DN.decode(baseDN.getValue());
673          }
674          catch(DirectoryException de)
675          {
676            printMessage(ERR_DBTEST_DECODE_BASE_DN.get(baseDN.getValue(),
677                                                       getExceptionMessage(de)));
678            return 1;
679          }
680        }
681    
682        // Acquire an shared lock for the backend.
683        try
684        {
685          String lockFile = LockFileManager.getBackendLockFileName(backend);
686          StringBuilder failureReason = new StringBuilder();
687          if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
688          {
689            Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
690                backend.getBackendID(), String.valueOf(failureReason));
691            printMessage(message);
692            return 1;
693          }
694        }
695        catch (Exception e)
696        {
697          Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
698              backend.getBackendID(), getExceptionMessage(e));
699          printMessage(message);
700          return 1;
701        }
702    
703        RootContainer rc;
704        try
705        {
706          rc = backend.getReadOnlyRootContainer();
707        }
708        catch(Exception e)
709        {
710          printMessage(ERR_DBTEST_ERROR_INITIALIZING_BACKEND.get(
711              backend.getBackendID(),
712              StaticUtils.stackTraceToSingleLineString(e)));
713          return 1;
714        }
715    
716    
717        try
718        {
719          // Create a table of their properties.
720          TableBuilder builder = new TableBuilder();
721          int count = 0;
722    
723          builder.appendHeading(INFO_LABEL_DBTEST_DATABASE_NAME.get());
724          builder.appendHeading(INFO_LABEL_DBTEST_DATABASE_TYPE.get());
725          builder.appendHeading(INFO_LABEL_DBTEST_JE_DATABASE_NAME.get());
726          builder.appendHeading(INFO_LABEL_DBTEST_ENTRY_COUNT.get());
727    
728          if(base != null)
729          {
730            EntryContainer ec = rc.getEntryContainer(base);
731    
732            if(ec == null)
733            {
734              printMessage(ERR_DBTEST_NO_ENTRY_CONTAINERS_FOR_BASE_DN.get(
735                  base.toNormalizedString(), backend.getBackendID()));
736              return 1;
737            }
738    
739            ArrayList<DatabaseContainer> databaseContainers =
740                new ArrayList<DatabaseContainer>();
741            ec.listDatabases(databaseContainers);
742            for(DatabaseContainer dc : databaseContainers)
743            {
744              builder.startRow();
745              builder.appendCell(dc.getName().replace(ec.getDatabasePrefix()+"_",
746                                                      ""));
747              builder.appendCell(dc.getClass().getSimpleName());
748              builder.appendCell(dc.getName());
749              builder.appendCell(dc.getRecordCount());
750              count++;
751            }
752          }
753          else
754          {
755            for(EntryContainer ec : rc.getEntryContainers())
756            {
757              builder.startRow();
758              ArrayList<DatabaseContainer> databaseContainers =
759                  new ArrayList<DatabaseContainer>();
760              ec.listDatabases(databaseContainers);
761              builder.appendCell("Base DN: " +
762                  ec.getBaseDN().toNormalizedString());
763              for(DatabaseContainer dc : databaseContainers)
764              {
765                builder.startRow();
766                builder.appendCell(dc.getName().replace(
767                    ec.getDatabasePrefix()+"_",""));
768                builder.appendCell(dc.getClass().getSimpleName());
769                builder.appendCell(dc.getName());
770                builder.appendCell(dc.getRecordCount());
771                count++;
772              }
773            }
774          }
775    
776          TextTablePrinter printer = new TextTablePrinter(out);
777          builder.print(printer);
778          out.format("%nTotal: %d%n", count);
779          return 0;
780    
781        }
782        catch(DatabaseException de)
783        {
784          printMessage(ERR_DBTEST_ERROR_READING_DATABASE.get(
785              StaticUtils.stackTraceToSingleLineString(de)));
786          return 1;
787        }
788        finally
789        {
790          try
791          {
792            // Close the root container
793            rc.close();
794          }
795          catch(DatabaseException de)
796          {
797            // Ignore.
798          }
799    
800          // Release the shared lock on the backend.
801          try
802          {
803            String lockFile = LockFileManager.getBackendLockFileName(backend);
804            StringBuilder failureReason = new StringBuilder();
805            if (! LockFileManager.releaseLock(lockFile, failureReason))
806            {
807              Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
808                  backend.getBackendID(), String.valueOf(failureReason));
809              printMessage(message);
810            }
811          }
812          catch (Exception e)
813          {
814            Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
815                backend.getBackendID(), getExceptionMessage(e));
816            printMessage(message);
817          }
818        }
819      }
820    
821      private int listIndexStatus(Argument backendID,
822                                  Argument baseDN)
823      {
824        TreeMap<LocalDBBackendCfg, BackendImpl> jeBackends = getJEBackends();
825        BackendImpl backend = null;
826        DN base = null;
827    
828        for(BackendImpl b : jeBackends.values())
829        {
830          if(b.getBackendID().equalsIgnoreCase(backendID.getValue()))
831          {
832            backend = b;
833            break;
834          }
835        }
836    
837        if(backend == null)
838        {
839          printMessage(ERR_DBTEST_NO_BACKENDS_FOR_ID.get(backendID.getValue()));
840          return 1;
841        }
842    
843        if(baseDN.isPresent())
844        {
845          try
846          {
847            base = DN.decode(baseDN.getValue());
848          }
849          catch(DirectoryException de)
850          {
851            printMessage(ERR_DBTEST_DECODE_BASE_DN.get(baseDN.getValue(),
852                                                       getExceptionMessage(de)));
853            return 1;
854          }
855        }
856    
857        // Acquire an shared lock for the backend.
858        try
859        {
860          String lockFile = LockFileManager.getBackendLockFileName(backend);
861          StringBuilder failureReason = new StringBuilder();
862          if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
863          {
864            Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
865                backend.getBackendID(), String.valueOf(failureReason));
866            printMessage(message);
867            return 1;
868          }
869        }
870        catch (Exception e)
871        {
872          Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
873              backend.getBackendID(), getExceptionMessage(e));
874          printMessage(message);
875          return 1;
876        }
877    
878        RootContainer rc;
879        try
880        {
881          rc = backend.getReadOnlyRootContainer();
882        }
883        catch(Exception e)
884        {
885          printMessage(ERR_DBTEST_ERROR_INITIALIZING_BACKEND.get(
886              backend.getBackendID(),
887              StaticUtils.stackTraceToSingleLineString(e)));
888          return 1;
889        }
890    
891    
892        try
893        {
894          // Create a table of their properties.
895          TableBuilder builder = new TableBuilder();
896          int count = 0;
897    
898          builder.appendHeading(INFO_LABEL_DBTEST_INDEX_NAME.get());
899          builder.appendHeading(INFO_LABEL_DBTEST_INDEX_TYPE.get());
900          builder.appendHeading(INFO_LABEL_DBTEST_JE_DATABASE_NAME.get());
901          builder.appendHeading(INFO_LABEL_DBTEST_INDEX_STATUS.get());
902    
903          EntryContainer ec = rc.getEntryContainer(base);
904    
905          if(ec == null)
906          {
907            printMessage(ERR_DBTEST_NO_ENTRY_CONTAINERS_FOR_BASE_DN.get(
908                base.toNormalizedString(), backend.getBackendID()));
909            return 1;
910          }
911    
912          ArrayList<DatabaseContainer> databaseContainers =
913              new ArrayList<DatabaseContainer>();
914          ec.listDatabases(databaseContainers);
915          for(DatabaseContainer dc : databaseContainers)
916          {
917            if(dc instanceof Index || dc instanceof VLVIndex)
918            {
919              builder.startRow();
920              builder.appendCell(dc.getName().replace(ec.getDatabasePrefix()+"_",
921                                                      ""));
922              builder.appendCell(dc.getClass().getSimpleName());
923              builder.appendCell(dc.getName());
924              if(dc instanceof Index)
925              {
926                builder.appendCell(ec.getState().getIndexTrustState(null,
927                                                                    ((Index)dc)));
928              }
929              else if(dc instanceof VLVIndex)
930              {
931                builder.appendCell(ec.getState().getIndexTrustState(null,
932                                                                   ((VLVIndex)dc)));
933              }
934              count++;
935            }
936          }
937    
938          TextTablePrinter printer = new TextTablePrinter(out);
939          builder.print(printer);
940          out.format("%nTotal: %d%n", count);
941          return 0;
942        }
943        catch(DatabaseException de)
944        {
945          printMessage(ERR_DBTEST_ERROR_READING_DATABASE.get(
946              StaticUtils.stackTraceToSingleLineString(de)));
947          return 1;
948        }
949        finally
950        {
951          try
952          {
953            // Close the root container
954            rc.close();
955          }
956          catch(DatabaseException de)
957          {
958            // Ignore.
959          }
960    
961          // Release the shared lock on the backend.
962          try
963          {
964            String lockFile = LockFileManager.getBackendLockFileName(backend);
965            StringBuilder failureReason = new StringBuilder();
966            if (! LockFileManager.releaseLock(lockFile, failureReason))
967            {
968            Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
969                backend.getBackendID(), String.valueOf(failureReason));
970              printMessage(message);
971            }
972          }
973          catch (Exception e)
974          {
975          Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
976              backend.getBackendID(), getExceptionMessage(e));
977            printMessage(message);
978          }
979        }
980      }
981    
982      private int dumpDatabaseContainer(Argument backendID, Argument baseDN,
983                                        Argument databaseName, Argument skipDecode,
984                                        Argument maxKeyValue, Argument minKeyValue,
985                                        Argument maxDataSize,
986                                        Argument minDataSize)
987      {
988        TreeMap<LocalDBBackendCfg, BackendImpl> jeBackends = getJEBackends();
989        BackendImpl backend = null;
990        DN base = null;
991    
992        for(BackendImpl b : jeBackends.values())
993        {
994          if(b.getBackendID().equalsIgnoreCase(backendID.getValue()))
995          {
996            backend = b;
997            break;
998          }
999        }
1000    
1001        if(backend == null)
1002        {
1003          printMessage(ERR_DBTEST_NO_BACKENDS_FOR_ID.get(backendID.getValue()));
1004          return 1;
1005        }
1006    
1007        try
1008        {
1009          base = DN.decode(baseDN.getValue());
1010        }
1011        catch(DirectoryException de)
1012        {
1013          printMessage(ERR_DBTEST_DECODE_BASE_DN.get(baseDN.getValue(),
1014                                                     getExceptionMessage(de)));
1015          return 1;
1016        }
1017    
1018        // Acquire an shared lock for the backend.
1019        try
1020        {
1021          String lockFile = LockFileManager.getBackendLockFileName(backend);
1022          StringBuilder failureReason = new StringBuilder();
1023          if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
1024          {
1025            Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
1026                backend.getBackendID(), String.valueOf(failureReason));
1027            printMessage(message);
1028            return 1;
1029          }
1030        }
1031        catch (Exception e)
1032        {
1033          Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
1034              backend.getBackendID(), getExceptionMessage(e));
1035          printMessage(message);
1036          return 1;
1037        }
1038    
1039        RootContainer rc;
1040        try
1041        {
1042          rc = backend.getReadOnlyRootContainer();
1043        }
1044        catch(Exception e)
1045        {
1046          printMessage(ERR_DBTEST_ERROR_INITIALIZING_BACKEND.get(
1047              backend.getBackendID(),
1048              StaticUtils.stackTraceToSingleLineString(e)));
1049          return 1;
1050        }
1051    
1052        try
1053        {
1054          EntryContainer ec = rc.getEntryContainer(base);
1055    
1056          if(ec == null)
1057          {
1058            printMessage(ERR_DBTEST_NO_ENTRY_CONTAINERS_FOR_BASE_DN.get(
1059                base.toNormalizedString(), backend.getBackendID()));
1060            return 1;
1061          }
1062    
1063          DatabaseContainer databaseContainer = null;
1064          ArrayList<DatabaseContainer> databaseContainers =
1065              new ArrayList<DatabaseContainer>();
1066          ec.listDatabases(databaseContainers);
1067          for(DatabaseContainer dc : databaseContainers)
1068          {
1069            if(dc.getName().replace(ec.getDatabasePrefix()+"_","").
1070                equalsIgnoreCase(databaseName.getValue()))
1071            {
1072              databaseContainer = dc;
1073              break;
1074            }
1075          }
1076    
1077          if(databaseContainer == null)
1078          {
1079            printMessage(ERR_DBTEST_NO_DATABASE_CONTAINERS_FOR_NAME.get(
1080                databaseName.getValue(), base.toNormalizedString(),
1081                backend.getBackendID()));
1082            return 1;
1083          }
1084    
1085          int count = 0;
1086          long totalKeySize = 0;
1087          long totalDataSize = 0;
1088          int indent = 4;
1089    
1090          Cursor cursor =
1091              databaseContainer.openCursor(null, CursorConfig.DEFAULT);
1092    
1093          try
1094          {
1095            DatabaseEntry key = new DatabaseEntry();
1096            DatabaseEntry data = new DatabaseEntry();
1097            LockMode lockMode = LockMode.DEFAULT;
1098            OperationStatus status;
1099            Comparator<byte[]> defaultComparator =
1100                new AttributeIndex.KeyComparator();
1101            Comparator<byte[]> dnComparator =
1102                new EntryContainer.KeyReverseComparator();
1103            byte[] start = null;
1104            byte[] end = null;
1105            int minSize = -1;
1106            int maxSize = -1;
1107    
1108            if(maxDataSize.isPresent())
1109            {
1110              try
1111              {
1112                maxSize = maxDataSize.getIntValue();
1113              }
1114              catch(Exception e)
1115              {
1116                printMessage(ERR_DBTEST_CANNOT_DECODE_SIZE.get(
1117                    maxDataSize.getValue(), getExceptionMessage(e)));
1118                return 1;
1119              }
1120            }
1121    
1122            if(minDataSize.isPresent())
1123            {
1124              try
1125              {
1126                minSize = minDataSize.getIntValue();
1127              }
1128              catch(Exception e)
1129              {
1130                printMessage(ERR_DBTEST_CANNOT_DECODE_SIZE.get(
1131                    minDataSize.getValue(), getExceptionMessage(e)));
1132                return 1;
1133              }
1134            }
1135    
1136            // Parse the min value if given
1137            if(minKeyValue.isPresent())
1138            {
1139              try
1140              {
1141                if(minKeyValue.getValue().startsWith("0x"))
1142                {
1143                  start =
1144                      StaticUtils.hexStringToByteArray(minKeyValue.getValue().
1145                          substring(2));
1146                }
1147                else
1148                {
1149                  if(databaseContainer instanceof DN2ID ||
1150                      databaseContainer instanceof DN2URI)
1151                  {
1152                    // Encode the value as a DN
1153                    start = StaticUtils.getBytes(
1154                        DN.decode(minKeyValue.getValue()).toNormalizedString());
1155                  }
1156                  else if(databaseContainer instanceof ID2Entry)
1157                  {
1158                    // Encode the value as an entryID
1159                    start = JebFormat.entryIDToDatabase(
1160                        Long.parseLong(minKeyValue.getValue()));
1161                  }
1162                  else if(databaseContainer instanceof VLVIndex)
1163                  {
1164                    // Encode the value as a size/value pair
1165                    byte[] vBytes =
1166                        new ASN1OctetString(minKeyValue.getValue()).value();
1167                    byte[] vLength = ASN1Element.encodeLength(vBytes.length);
1168                    start = new byte[vBytes.length + vLength.length];
1169                    System.arraycopy(vLength, 0, start, 0, vLength.length);
1170                    System.arraycopy(vBytes, 0, start, vLength.length,
1171                                     vBytes.length);
1172                  }
1173                  else
1174                  {
1175                    start = new ASN1OctetString(minKeyValue.getValue()).value();
1176                  }
1177                }
1178              }
1179              catch(Exception e)
1180              {
1181                printMessage(ERR_DBTEST_CANNOT_DECODE_KEY.get(
1182                    minKeyValue.getValue(), getExceptionMessage(e)));
1183                return 1;
1184              }
1185            }
1186    
1187            // Parse the max value if given
1188            if(maxKeyValue.isPresent())
1189            {
1190              try
1191              {
1192                if(maxKeyValue.getValue().startsWith("0x"))
1193                {
1194                  end =
1195                      StaticUtils.hexStringToByteArray(maxKeyValue.getValue().
1196                          substring(2));
1197                }
1198                else
1199                {
1200                  if(databaseContainer instanceof DN2ID ||
1201                      databaseContainer instanceof DN2URI)
1202                  {
1203                    // Encode the value as a DN
1204                    end = StaticUtils.getBytes(
1205                        DN.decode(maxKeyValue.getValue()).toNormalizedString());
1206                  }
1207                  else if(databaseContainer instanceof ID2Entry)
1208                  {
1209                    // Encode the value as an entryID
1210                    end = JebFormat.entryIDToDatabase(
1211                        Long.parseLong(maxKeyValue.getValue()));
1212                  }
1213                  else if(databaseContainer instanceof VLVIndex)
1214                  {
1215                    // Encode the value as a size/value pair
1216                    byte[] vBytes =
1217                        new ASN1OctetString(maxKeyValue.getValue()).value();
1218                    byte[] vLength = ASN1Element.encodeLength(vBytes.length);
1219                    end = new byte[vBytes.length + vLength.length];
1220                    System.arraycopy(vLength, 0, end, 0, vLength.length);
1221                    System.arraycopy(vBytes, 0, end, vLength.length,
1222                                     vBytes.length);
1223                  }
1224                  else
1225                  {
1226                    end = new ASN1OctetString(maxKeyValue.getValue()).value();
1227                  }
1228                }
1229              }
1230              catch(Exception e)
1231              {
1232                printMessage(ERR_DBTEST_CANNOT_DECODE_KEY.get(
1233                    maxKeyValue.getValue(), getExceptionMessage(e)));
1234                return 1;
1235              }
1236            }
1237    
1238    
1239            if(start != null)
1240            {
1241              key.setData(start);
1242              status = cursor.getSearchKey(key, data, lockMode);
1243            }
1244            else
1245            {
1246              status = cursor.getFirst(key, data, lockMode);
1247            }
1248    
1249            while(status == OperationStatus.SUCCESS)
1250            {
1251              // Make sure this record is within the value size params
1252              if((minSize > 0 && data.getSize() < minSize) ||
1253                  (maxSize > 0 && data.getSize() > maxSize))
1254              {
1255                status = cursor.getNext(key, data, lockMode);
1256                continue;
1257              }
1258    
1259              // Make sure we haven't gone pass the max value yet
1260              if(end != null)
1261              {
1262                if(databaseContainer instanceof DN2ID)
1263                {
1264                  if(dnComparator.compare(key.getData(), end) > 0)
1265                  {
1266                    break;
1267                  }
1268                }
1269                else if(databaseContainer instanceof DN2URI)
1270                {
1271                  if(dnComparator.compare(key.getData(), end) > 0)
1272                  {
1273                    break;
1274                  }
1275                }
1276                else if(databaseContainer instanceof Index)
1277                {
1278                  if(((Index)databaseContainer).indexer.getComparator().
1279                      compare(key.getData(), end) > 0)
1280                  {
1281                    break;
1282                  }
1283                }
1284                else if(databaseContainer instanceof VLVIndex)
1285                {
1286                  if(((VLVIndex)databaseContainer).comparator.
1287                      compare(key.getData(), end) > 0)
1288                  {
1289                    break;
1290                  }
1291                }
1292                else
1293                {
1294                  if(defaultComparator.compare(key.getData(),
1295                                               end) > 0)
1296                  {
1297                    break;
1298                  }
1299                }
1300              }
1301    
1302              Message keyLabel = INFO_LABEL_DBTEST_KEY.get();
1303              Message dataLabel = INFO_LABEL_DBTEST_DATA.get();
1304    
1305              String formatedKey = null;
1306              String formatedData = null;
1307    
1308              if(!skipDecode.isPresent())
1309              {
1310                if(databaseContainer instanceof DN2ID)
1311                {
1312                  try
1313                  {
1314                    formatedKey = DN.decode(new ASN1OctetString(
1315                        key.getData())).toNormalizedString();
1316                    keyLabel = INFO_LABEL_DBTEST_ENTRY_DN.get();
1317                  }
1318                  catch(Exception e)
1319                  {
1320                    Message message =
1321                        ERR_DBTEST_DECODE_FAIL.get(getExceptionMessage(e));
1322                    printMessage(message);
1323                  }
1324                  formatedData = String.valueOf(
1325                      JebFormat.entryIDFromDatabase(data.getData()));
1326                  dataLabel = INFO_LABEL_DBTEST_ENTRY_ID.get();
1327                }
1328                else if(databaseContainer instanceof ID2Entry)
1329                {
1330                  formatedKey = String.valueOf(
1331                      JebFormat.entryIDFromDatabase(key.getData()));
1332                  keyLabel = INFO_LABEL_DBTEST_ENTRY_ID.get();
1333                  try
1334                  {
1335                    formatedData = System.getProperty("line.separator") +
1336                        JebFormat.entryFromDatabase(data.getData(),
1337                             ec.getRootContainer().getCompressedSchema()).
1338                                  toLDIFString();
1339                    dataLabel = INFO_LABEL_DBTEST_ENTRY.get();
1340                  }
1341                  catch(Exception e)
1342                  {
1343                    Message message =
1344                        ERR_DBTEST_DECODE_FAIL.get(getExceptionMessage(e));
1345                    printMessage(message);
1346                  }
1347                }
1348                else if(databaseContainer instanceof DN2URI)
1349                {
1350                  try
1351                  {
1352                    formatedKey = DN.decode(new ASN1OctetString(
1353                        key.getData())).toNormalizedString();
1354                    keyLabel = INFO_LABEL_DBTEST_ENTRY_DN.get();
1355                  }
1356                  catch(Exception e)
1357                  {
1358                    Message message =
1359                        ERR_DBTEST_DECODE_FAIL.get(getExceptionMessage(e));
1360                    printMessage(message);
1361                  }
1362                  formatedData = new ASN1OctetString(key.getData()).stringValue();
1363                  dataLabel = INFO_LABEL_DBTEST_URI.get();
1364                }
1365                else if(databaseContainer instanceof Index)
1366                {
1367                  formatedKey = new ASN1OctetString(key.getData()).stringValue();
1368                  keyLabel = INFO_LABEL_DBTEST_INDEX_VALUE.get();
1369    
1370                  EntryIDSet idSet = new EntryIDSet(key.getData(),
1371                                                    data.getData());
1372                  if(idSet.isDefined())
1373                  {
1374                    int lineCount = 0;
1375                    StringBuilder builder = new StringBuilder();
1376    
1377                    Iterator<EntryID> i = idSet.iterator();
1378                    while(i.hasNext())
1379                    {
1380                      builder.append(i.next());
1381                      if(lineCount == 10)
1382                      {
1383                        builder.append(System.getProperty("line.separator"));
1384                        lineCount = 0;
1385                      }
1386                      else
1387                      {
1388                        builder.append(" ");
1389                        lineCount++;
1390                      }
1391                    }
1392                    formatedData = builder.toString();
1393                  }
1394                  else
1395                  {
1396                    formatedData = idSet.toString();
1397                  }
1398                  dataLabel = INFO_LABEL_DBTEST_INDEX_ENTRY_ID_LIST.get();
1399                }
1400                else if(databaseContainer instanceof VLVIndex)
1401                {
1402                  VLVIndex index = (VLVIndex)databaseContainer;
1403                  SortKey[] sortKeys = index.sortOrder.getSortKeys();
1404    
1405                  int pos = 0;
1406                  byte[] keyBytes = key.getData();
1407                  if(keyBytes.length > 0)
1408                  {
1409                    StringBuilder builder = new StringBuilder();
1410    
1411                    // Decode the attribute values
1412                    for(SortKey sortKey : sortKeys)
1413                    {
1414                      int valueLength = keyBytes[pos] & 0x7F;
1415                      if (keyBytes[pos++] != valueLength)
1416                      {
1417                        int numLengthBytes = valueLength;
1418                        valueLength = 0;
1419                        for (int k=0; k < numLengthBytes; k++, pos++)
1420                        {
1421                          valueLength = (valueLength << 8) |
1422                              (keyBytes[pos] & 0xFF);
1423                        }
1424                      }
1425    
1426                      byte[] valueBytes = new byte[valueLength];
1427                      System.arraycopy(keyBytes, pos, valueBytes, 0,
1428                                       valueLength);
1429                      builder.append(sortKey.getAttributeType().getNameOrOID());
1430                      builder.append(": ");
1431                      if(valueBytes.length == 0)
1432                      {
1433                        builder.append("NULL");
1434                      }
1435                      else
1436                      {
1437                        builder.append(
1438                            new ASN1OctetString(valueBytes).stringValue());
1439                      }
1440                      builder.append(" ");
1441                      pos += valueLength;
1442                    }
1443    
1444                    byte[] entryIDBytes = new byte[8];
1445                    System.arraycopy(keyBytes, pos, entryIDBytes, 0,
1446                                     entryIDBytes.length);
1447                    long entryID = JebFormat.entryIDFromDatabase(entryIDBytes);
1448    
1449                    formatedKey = System.getProperty("line.separator") +
1450                        String.valueOf(entryID) + ": " + builder.toString();
1451                  }
1452                  else
1453                  {
1454                    formatedKey = "UNBOUNDED";
1455                  }
1456                  keyLabel = INFO_LABEL_DBTEST_VLV_INDEX_LAST_SORT_KEYS.get();
1457    
1458                  try
1459                  {
1460                    StringBuilder builder = new StringBuilder();
1461                    SortValuesSet svs = new SortValuesSet(key.getData(),
1462                                                          data.getData(),
1463                                                          index);
1464                    long[] entryIDs = svs.getEntryIDs();
1465                    for(int i = 0; i < entryIDs.length; i++)
1466                    {
1467                      builder.append(String.valueOf(entryIDs[i]));
1468                      builder.append(": ");
1469                      for(int j = 0; j < sortKeys.length; j++)
1470                      {
1471                        SortKey sortKey = index.sortOrder.getSortKeys()[j];
1472                        byte[] value = svs.getValue(i * sortKeys.length + j);
1473                        builder.append(sortKey.getAttributeType().getNameOrOID());
1474                        builder.append(": ");
1475                        if(value == null)
1476                        {
1477                          builder.append("NULL");
1478                        }
1479                        else if(value.length == 0)
1480                        {
1481                          builder.append("SIZE-EXCEEDED");
1482                        }
1483                        else
1484                        {
1485                          builder.append(
1486                              new ASN1OctetString(value).stringValue());
1487                        }
1488                        builder.append(" ");
1489                      }
1490                      builder.append(System.getProperty("line.separator"));
1491                    }
1492                    formatedData = System.getProperty("line.separator") +
1493                        builder.toString();
1494                    dataLabel = INFO_LABEL_DBTEST_INDEX_ENTRY_ID_LIST.get();
1495                  }
1496                  catch(Exception e)
1497                  {
1498                    Message message =
1499                        ERR_DBTEST_DECODE_FAIL.get(getExceptionMessage(e));
1500                    printMessage(message);
1501                  }
1502                }
1503              }
1504    
1505              if(formatedKey == null)
1506              {
1507                StringBuilder keyBuilder = new StringBuilder();
1508                StaticUtils.byteArrayToHexPlusAscii(keyBuilder, key.getData(),
1509                                                    indent);
1510                formatedKey = System.getProperty("line.separator") +
1511                    keyBuilder.toString();
1512              }
1513              if(formatedData == null)
1514              {
1515                StringBuilder dataBuilder = new StringBuilder();
1516                StaticUtils.byteArrayToHexPlusAscii(dataBuilder, data.getData(),
1517                                                    indent);
1518                formatedData = System.getProperty("line.separator") +
1519                    dataBuilder.toString();
1520              }
1521    
1522              out.format("%s (%d bytes): %s%n", keyLabel,
1523                         key.getData().length, formatedKey);
1524              out.format("%s (%d bytes): %s%n%n", dataLabel,
1525                         data.getData().length, formatedData);
1526    
1527              status = cursor.getNext(key, data, lockMode);
1528              count++;
1529              totalKeySize += key.getData().length;
1530              totalDataSize += data.getData().length;
1531            }
1532          }
1533          finally
1534          {
1535            cursor.close();
1536          }
1537          out.format("%nTotal Records: %d%n", count);
1538          if(count > 0)
1539          {
1540            out.format("Total / Average Key Size: %d bytes / %d bytes%n",
1541                       totalKeySize, totalKeySize / count);
1542            out.format("Total / Average Data Size: %d bytes / %d bytes%n",
1543                       totalDataSize, totalDataSize / count);
1544          }
1545          return 0;
1546        }
1547        catch(DatabaseException de)
1548        {
1549          printMessage(ERR_DBTEST_ERROR_READING_DATABASE.get(
1550              StaticUtils.stackTraceToSingleLineString(de)));
1551          return 1;
1552        }
1553        finally
1554        {
1555          try
1556          {
1557            // Close the root container
1558            rc.close();
1559          }
1560          catch(DatabaseException de)
1561          {
1562            // Ignore.
1563          }
1564    
1565          // Release the shared lock on the backend.
1566          try
1567          {
1568            String lockFile = LockFileManager.getBackendLockFileName(backend);
1569            StringBuilder failureReason = new StringBuilder();
1570            if (! LockFileManager.releaseLock(lockFile, failureReason))
1571            {
1572              Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
1573                  backend.getBackendID(), String.valueOf(failureReason));
1574              printMessage(message);
1575            }
1576          }
1577          catch (Exception e)
1578          {
1579            Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
1580                backend.getBackendID(), getExceptionMessage(e));
1581            printMessage(message);
1582          }
1583        }
1584      }
1585    
1586      private TreeMap<LocalDBBackendCfg, BackendImpl> getJEBackends()
1587      {
1588        ArrayList<Backend> backendList = new ArrayList<Backend>();
1589        ArrayList<BackendCfg>  entryList   = new ArrayList<BackendCfg>();
1590        ArrayList<List<DN>> dnList = new ArrayList<List<DN>>();
1591        int code = BackendToolUtils.getBackends(backendList, entryList, dnList);
1592        // TODO: Throw error if return code is not 0
1593    
1594        TreeMap<LocalDBBackendCfg, BackendImpl> jeBackends =
1595            new TreeMap<LocalDBBackendCfg, BackendImpl>();
1596        for(int i = 0; i < backendList.size(); i++)
1597        {
1598          Backend backend = backendList.get(i);
1599          if(backend instanceof BackendImpl)
1600          {
1601            jeBackends.put((LocalDBBackendCfg)entryList.get(i),
1602                           (BackendImpl)backend);
1603          }
1604        }
1605    
1606        return jeBackends;
1607      }
1608    
1609      /**
1610       * Displays a message to the error stream.
1611       *
1612       * @param msg
1613       *          The message.
1614       */
1615      public final void printMessage(Message msg) {
1616        err.println(wrapText(msg.toString(), MAX_LINE_WIDTH));
1617      }
1618    }