/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2013 ForgeRock AS
*/
package org.opends.server.tools;
import org.opends.server.loggers.debug.DebugTracer;
import static org.opends.server.loggers.debug.DebugLogger.getTracer;
import static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
import static org.opends.server.util.StaticUtils.*;
import org.opends.server.util.args.*;
import static org.opends.server.util.ServerConstants.MAX_LINE_WIDTH;
import org.opends.server.util.BuildVersion;
import org.opends.server.util.StaticUtils;
import org.opends.server.util.table.TableBuilder;
import org.opends.server.util.table.TextTablePrinter;
import org.opends.server.types.*;
import static org.opends.messages.ToolMessages.*;
import org.opends.messages.Message;
import static org.opends.server.tools.ToolConstants.OPTION_SHORT_CONFIG_CLASS;
import static org.opends.server.tools.ToolConstants.OPTION_LONG_CONFIG_CLASS;
import static org.opends.server.tools.ToolConstants.OPTION_SHORT_HELP;
import static org.opends.server.tools.ToolConstants.OPTION_LONG_HELP;
import org.opends.server.extensions.ConfigFileHandler;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.LockFileManager;
import org.opends.server.core.CoreConfigManager;
import org.opends.server.config.ConfigException;
import org.opends.server.api.Backend;
import org.opends.server.admin.std.server.BackendCfg;
import org.opends.server.admin.std.server.LocalDBBackendCfg;
import org.opends.server.backends.jeb.*;
import java.io.*;
import java.util.*;
import com.sleepycat.je.*;
/**
* This program provides a utility that may be used to debug a JE backend. This
* tool provides the ability list various containers in the backend as well as
* dump the contents of database containers. This will be
* a process that is intended to run separate from Directory Server and not
* internally within the server process (e.g., via the tasks interface).
*/
public class DBTest
{
/**
* The tracer object for the debug logger.
*/
private static final DebugTracer TRACER = getTracer();
// The error stream which this application should use.
private final PrintStream err;
// The output stream which this application should use.
private final PrintStream out;
// Flag indicating whether or not the global arguments have
// already been initialized.
private boolean globalArgumentsInitialized = false;
// The command-line argument parser.
private final SubCommandArgumentParser parser;
// The argument which should be used to request usage information.
private BooleanArgument showUsageArgument;
// The argument which should be used to specify the config class.
private StringArgument configClass;
// THe argument which should be used to specify the config file.
private StringArgument configFile;
// Flag indicating whether or not the sub-commands have
// already been initialized.
private boolean subCommandsInitialized = false;
/**
* Provides the command-line arguments to the main application for
* processing.
*
* @param args
* The set of command-line arguments provided to this
* program.
*/
public static void main(String[] args) {
int exitCode = main(args, true, System.out, System.err);
if (exitCode != 0) {
System.exit(filterExitCode(exitCode));
}
}
/**
* Provides the command-line arguments to the main application for
* processing and returns the exit code as an integer.
*
* @param args
* The set of command-line arguments provided to this
* program.
* @param initializeServer
* Indicates whether to perform basic initialization (which
* should not be done if the tool is running in the same
* JVM as the server).
* @param outStream
* The output stream for standard output.
* @param errStream
* The output stream for standard error.
* @return Zero to indicate that the program completed successfully,
* or non-zero to indicate that an error occurred.
*/
public static int main(String[] args, boolean initializeServer,
OutputStream outStream, OutputStream errStream) {
DBTest app = new DBTest(outStream, errStream);
// Run the application.
return app.run(args, initializeServer);
}
/**
* Creates a new dsconfig application instance.
*
* @param out
* The application output stream.
* @param err
* The application error stream.
*/
public DBTest(OutputStream out, OutputStream err)
{
if (out != null) {
this.out = new PrintStream(out);
} else {
this.out = NullOutputStream.printStream();
}
if (err != null) {
this.err = new PrintStream(err);
} else {
this.err = NullOutputStream.printStream();
}
Message toolDescription = INFO_DESCRIPTION_DBTEST_TOOL.get();
this.parser = new SubCommandArgumentParser(this.getClass().getName(),
toolDescription, false);
}
// Displays the provided message followed by a help usage reference.
private void displayMessageAndUsageReference(Message message) {
printMessage(message);
printMessage(Message.EMPTY);
printMessage(parser.getHelpUsageReference());
}
/**
* Registers the global arguments with the argument parser.
*
* @throws ArgumentException
* If a global argument could not be registered.
*/
private void initializeGlobalArguments() throws ArgumentException {
if (!globalArgumentsInitialized) {
configClass =
new StringArgument("configclass", OPTION_SHORT_CONFIG_CLASS,
OPTION_LONG_CONFIG_CLASS, true, false,
true, INFO_CONFIGCLASS_PLACEHOLDER.get(),
ConfigFileHandler.class.getName(), null,
INFO_DESCRIPTION_CONFIG_CLASS.get());
configClass.setHidden(true);
configFile =
new StringArgument("configfile", 'f', "configFile", true, false,
true, INFO_CONFIGFILE_PLACEHOLDER.get(), null,
null,
INFO_DESCRIPTION_CONFIG_FILE.get());
configFile.setHidden(true);
showUsageArgument =
new BooleanArgument("help", OPTION_SHORT_HELP, OPTION_LONG_HELP,
INFO_DESCRIPTION_USAGE.get());
// Register the global arguments.
parser.addGlobalArgument(showUsageArgument);
parser.setUsageArgument(showUsageArgument, out);
parser.addGlobalArgument(configClass);
parser.addGlobalArgument(configFile);
globalArgumentsInitialized = true;
}
}
/**
* Registers the sub-commands with the argument parser.
*
* @throws ArgumentException
* If a sub-command could not be created.
*/
private void initializeSubCommands() throws ArgumentException {
if (!subCommandsInitialized) {
StringArgument backendID;
StringArgument baseDN;
StringArgument databaseName;
BooleanArgument skipDecode;
BooleanArgument statsOnly;
StringArgument maxKeyValue;
StringArgument minKeyValue;
IntegerArgument maxDataSize;
IntegerArgument minDataSize;
SubCommand sub;
sub = new SubCommand(parser, "list-root-containers",
INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_ROOT_CONTAINERS.get());
sub = new SubCommand(parser, "list-entry-containers",
INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_ENTRY_CONTAINERS.get());
backendID =
new StringArgument("backendid", 'n', "backendID", true, false, true,
INFO_BACKENDNAME_PLACEHOLDER.get(), null, null,
INFO_DESCRIPTION_DBTEST_BACKEND_ID.get());
sub.addArgument(backendID);
sub = new SubCommand(parser, "list-database-containers",
INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_DATABASE_CONTAINERS.get());
backendID =
new StringArgument("backendid", 'n', "backendID", true, false, true,
INFO_BACKENDNAME_PLACEHOLDER.get(), null, null,
INFO_DESCRIPTION_DBTEST_BACKEND_ID.get());
sub.addArgument(backendID);
baseDN =
new StringArgument("basedn", 'b', "baseDN", false,
false, true, INFO_BASEDN_PLACEHOLDER.get(), null,
null,
INFO_DESCRIPTION_DBTEST_BASE_DN.get());
sub.addArgument(baseDN);
sub = new SubCommand(parser, "dump-database-container",
INFO_DESCRIPTION_DBTEST_SUBCMD_DUMP_DATABASE_CONTAINER.get());
backendID =
new StringArgument("backendid", 'n', "backendID", true, false, true,
INFO_BACKENDNAME_PLACEHOLDER.get(), null, null,
INFO_DESCRIPTION_DBTEST_BACKEND_ID.get());
sub.addArgument(backendID);
baseDN =
new StringArgument("basedn", 'b', "baseDN", true,
false, true, INFO_BASEDN_PLACEHOLDER.get(), null,
null,
INFO_DESCRIPTION_DBTEST_BASE_DN.get());
sub.addArgument(baseDN);
databaseName =
new StringArgument("databasename", 'd', "databaseName", true,
false, true, INFO_DATABASE_NAME_PLACEHOLDER.get(),
null, null,
INFO_DESCRIPTION_DBTEST_DATABASE_NAME.get());
sub.addArgument(databaseName);
skipDecode =
new BooleanArgument("skipdecode", 'p', "skipDecode",
INFO_DESCRIPTION_DBTEST_SKIP_DECODE.get());
sub.addArgument(skipDecode);
statsOnly =
new BooleanArgument("statsonly", 'q', "statsOnly",
INFO_DESCRIPTION_DBTEST_STATS_ONLY.get());
sub.addArgument(statsOnly);
maxKeyValue = new StringArgument("maxkeyvalue", 'K', "maxKeyValue", false,
false, true,
INFO_MAX_KEY_VALUE_PLACEHOLDER.get(),
null, null,
INFO_DESCRIPTION_DBTEST_MAX_KEY_VALUE.get());
sub.addArgument(maxKeyValue);
minKeyValue = new StringArgument("minkeyvalue", 'k', "minKeyValue", false,
false, true,
INFO_MIN_KEY_VALUE_PLACEHOLDER.get(),
null,
null,
INFO_DESCRIPTION_DBTEST_MIN_KEY_VALUE.get());
sub.addArgument(minKeyValue);
maxDataSize = new IntegerArgument("maxdatasize", 'S', "maxDataSize",
false, false, true,
INFO_MAX_DATA_SIZE_PLACEHOLDER.get(),
-1,
null,
INFO_DESCRIPTION_DBTEST_MAX_DATA_SIZE.get());
sub.addArgument(maxDataSize);
minDataSize = new IntegerArgument("mindatasize", 's', "minDataSize",
false, false, true,
INFO_MIN_DATA_SIZE_PLACEHOLDER.get(),
-1,
null,
INFO_DESCRIPTION_DBTEST_MIN_DATA_SIZE.get());
sub.addArgument(minDataSize);
sub = new SubCommand(parser, "list-index-status",
INFO_DESCRIPTION_DBTEST_SUBCMD_LIST_INDEX_STATUS.get());
backendID =
new StringArgument("backendid", 'n', "backendID", true, false, true,
INFO_BACKENDNAME_PLACEHOLDER.get(), null, null,
INFO_DESCRIPTION_DBTEST_BACKEND_ID.get());
sub.addArgument(backendID);
baseDN =
new StringArgument("basedn", 'b', "baseDN", true,
true, true, INFO_BASEDN_PLACEHOLDER.get(), null,
null,
INFO_DESCRIPTION_DBTEST_BASE_DN.get());
sub.addArgument(baseDN);
subCommandsInitialized = true;
}
}
/**
* Parses the provided command-line arguments and makes the
* appropriate changes to the Directory Server configuration.
*
* @param args
* The command-line arguments provided to this program.
* @param initializeServer
* Indicates whether to perform basic initialization (which
* should not be done if the tool is running in the same
* JVM as the server).
* @return The exit code from the configuration processing. A
* nonzero value indicates that there was some kind of
* problem during the configuration processing.
*/
private int run(String[] args, boolean initializeServer) {
// Register global arguments and sub-commands.
try {
initializeGlobalArguments();
initializeSubCommands();
} catch (ArgumentException e) {
Message message = ERR_CANNOT_INITIALIZE_ARGS.get(e.getMessage());
printMessage(message);
return 1;
}
// Parse the command-line arguments provided to this program.
try {
parser.parseArguments(args);
} catch (ArgumentException ae) {
Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
displayMessageAndUsageReference(message);
return 1;
}
// If the usage/version argument was provided, then we don't need
// to do anything else.
if (parser.usageOrVersionDisplayed()) {
return 0;
}
// Checks the version - if upgrade required, the tool is unusable
try
{
BuildVersion.checkVersionMismatch();
}
catch (InitializationException e)
{
if (debugEnabled()) {
TRACER.debugCaught(DebugLogLevel.ERROR, e);
}
printMessage(e.getMessageObject());
return 1;
}
// Only initialize the server when run as a standalone
// application.
if (initializeServer) {
// Perform the initial bootstrap of the Directory Server and process the
// configuration.
DirectoryServer directoryServer = DirectoryServer.getInstance();
try
{
DirectoryServer.bootstrapClient();
DirectoryServer.initializeJMX();
}
catch (Exception e)
{
Message message = ERR_SERVER_BOOTSTRAP_ERROR.get(
getExceptionMessage(e));
printMessage(message);
return 1;
}
try
{
directoryServer.initializeConfiguration(configClass.getValue(),
configFile.getValue());
}
catch (InitializationException ie)
{
Message message = ERR_CANNOT_LOAD_CONFIG.get(
ie.getMessage());
printMessage(message);
return 1;
}
catch (Exception e)
{
Message message = ERR_CANNOT_LOAD_CONFIG.get(
getExceptionMessage(e));
printMessage(message);
return 1;
}
// Initialize the Directory Server schema elements.
try
{
directoryServer.initializeSchema();
}
catch (ConfigException ce)
{
Message message = ERR_CANNOT_LOAD_SCHEMA.get(
ce.getMessage());
printMessage(message);
return 1;
}
catch (InitializationException ie)
{
Message message = ERR_CANNOT_LOAD_SCHEMA.get(
ie.getMessage());
printMessage(message);
return 1;
}
catch (Exception e)
{
Message message = ERR_CANNOT_LOAD_SCHEMA.get(
getExceptionMessage(e));
printMessage(message);
return 1;
}
// Initialize the Directory Server core configuration.
try
{
CoreConfigManager coreConfigManager = new CoreConfigManager();
coreConfigManager.initializeCoreConfig();
}
catch (ConfigException ce)
{
Message message = ERR_CANNOT_INITIALIZE_CORE_CONFIG.get(
ce.getMessage());
printMessage(message);
return 1;
}
catch (InitializationException ie)
{
Message message = ERR_CANNOT_INITIALIZE_CORE_CONFIG.get(
ie.getMessage());
printMessage(message);
return 1;
}
catch (Exception e)
{
Message message = ERR_CANNOT_INITIALIZE_CORE_CONFIG.get(
getExceptionMessage(e));
printMessage(message);
return 1;
}
// Initialize the Directory Server crypto manager.
try
{
directoryServer.initializeCryptoManager();
}
catch (ConfigException ce)
{
Message message = ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get(
ce.getMessage());
printMessage(message);
return 1;
}
catch (InitializationException ie)
{
Message message = ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get(
ie.getMessage());
printMessage(message);
return 1;
}
catch (Exception e)
{
Message message = ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get(
getExceptionMessage(e));
printMessage(message);
return 1;
}
}
// Make sure that we have a sub-command.
if (parser.getSubCommand() == null)
{
Message message = ERR_DBTEST_MISSING_SUBCOMMAND.get();
displayMessageAndUsageReference(message);
return 1;
}
// Retrieve the sub-command implementation and run it.
SubCommand subCommand = parser.getSubCommand();
try {
if(subCommand.getName().equals("list-root-containers"))
{
return listRootContainers();
}
else if(subCommand.getName().equals("list-entry-containers"))
{
return listEntryContainers(subCommand.getArgument("backendid"));
}
else if(subCommand.getName().equals("list-database-containers"))
{
return listDatabaseContainers(subCommand.getArgument("backendid"),
subCommand.getArgument("basedn"));
}
else if(subCommand.getName().equals("dump-database-container"))
{
return dumpDatabaseContainer(subCommand.getArgument("backendid"),
subCommand.getArgument("basedn"),
subCommand.getArgument("databasename"),
subCommand.getArgument("skipdecode"),
subCommand.getArgument("statsonly"),
subCommand.getArgument("maxkeyvalue"),
subCommand.getArgument("minkeyvalue"),
subCommand.getArgument("maxdatasize"),
subCommand.getArgument("mindatasize"));
}
else if(subCommand.getName().equals("list-index-status"))
{
return listIndexStatus(subCommand.getArgument("backendid"),
subCommand.getArgument("basedn"));
}
{
return 0;
}
} catch (Exception e) {
if (debugEnabled()) {
TRACER.debugCaught(DebugLogLevel.ERROR, e);
}
printMessage(Message.raw(StaticUtils.stackTraceToString(e)));
return 1;
}
}
private int listRootContainers()
{
Map<LocalDBBackendCfg, BackendImpl> jeBackends = getJEBackends();
int count = 0;
// Create a table of their properties.
TableBuilder builder = new TableBuilder();
builder.appendHeading(INFO_LABEL_DBTEST_BACKEND_ID.get());
builder.appendHeading(INFO_LABEL_DBTEST_DB_DIRECTORY.get());
for(Map.Entry<LocalDBBackendCfg, BackendImpl> backend :
jeBackends.entrySet())
{
builder.startRow();
builder.appendCell(backend.getValue().getBackendID());
builder.appendCell(backend.getKey().getDBDirectory());
count++;
}
TextTablePrinter printer = new TextTablePrinter(out);
builder.print(printer);
out.format("%nTotal: %d%n", count);
return 0;
}
private int listEntryContainers(Argument backendID)
{
Map<LocalDBBackendCfg, BackendImpl> jeBackends = getJEBackends();
BackendImpl backend = null;
for(BackendImpl b : jeBackends.values())
{
if(b.getBackendID().equalsIgnoreCase(backendID.getValue()))
{
backend = b;
break;
}
}
if(backend == null)
{
printMessage(ERR_DBTEST_NO_BACKENDS_FOR_ID.get(backendID.getValue()));
return 1;
}
// Acquire an shared lock for the backend.
try
{
String lockFile = LockFileManager.getBackendLockFileName(backend);
StringBuilder failureReason = new StringBuilder();
if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
{
Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
backend.getBackendID(), String.valueOf(failureReason));
printMessage(message);
return 1;
}
}
catch (Exception e)
{
Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
backend.getBackendID(), getExceptionMessage(e));
printMessage(message);
return 1;
}
RootContainer rc;
try
{
rc = backend.getReadOnlyRootContainer();
}
catch(Exception e)
{
printMessage(ERR_DBTEST_ERROR_INITIALIZING_BACKEND.get(
backend.getBackendID(),
StaticUtils.stackTraceToSingleLineString(e)));
return 1;
}
try
{
// Create a table of their properties.
TableBuilder builder = new TableBuilder();
int count = 0;
builder.appendHeading(INFO_LABEL_DBTEST_BASE_DN.get());
builder.appendHeading(INFO_LABEL_DBTEST_JE_DATABASE_PREFIX.get());
builder.appendHeading(INFO_LABEL_DBTEST_ENTRY_COUNT.get());
for(EntryContainer ec : rc.getEntryContainers())
{
builder.startRow();
builder.appendCell(ec.getBaseDN().toNormalizedString());
builder.appendCell(ec.getDatabasePrefix());
builder.appendCell(ec.getEntryCount());
count++;
}
TextTablePrinter printer = new TextTablePrinter(out);
builder.print(printer);
out.format("%nTotal: %d%n", count);
return 0;
}
catch(DatabaseException de)
{
printMessage(ERR_DBTEST_ERROR_READING_DATABASE.get(
StaticUtils.stackTraceToSingleLineString(de)));
return 1;
}
finally
{
try
{
// Close the root container
rc.close();
}
catch(DatabaseException de)
{
// Ignore.
}
// Release the shared lock on the backend.
try
{
String lockFile = LockFileManager.getBackendLockFileName(backend);
StringBuilder failureReason = new StringBuilder();
if (! LockFileManager.releaseLock(lockFile, failureReason))
{
Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
backend.getBackendID(), String.valueOf(failureReason));
printMessage(message);
}
}
catch (Exception e)
{
Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
backend.getBackendID(), getExceptionMessage(e));
printMessage(message);
}
}
}
private int listDatabaseContainers(Argument backendID,
Argument baseDN)
{
Map<LocalDBBackendCfg, BackendImpl> jeBackends = getJEBackends();
BackendImpl backend = null;
DN base = null;
for(BackendImpl b : jeBackends.values())
{
if(b.getBackendID().equalsIgnoreCase(backendID.getValue()))
{
backend = b;
break;
}
}
if(backend == null)
{
printMessage(ERR_DBTEST_NO_BACKENDS_FOR_ID.get(backendID.getValue()));
return 1;
}
if(baseDN.isPresent())
{
try
{
base = DN.decode(baseDN.getValue());
}
catch(DirectoryException de)
{
printMessage(ERR_DBTEST_DECODE_BASE_DN.get(baseDN.getValue(),
getExceptionMessage(de)));
return 1;
}
}
// Acquire an shared lock for the backend.
try
{
String lockFile = LockFileManager.getBackendLockFileName(backend);
StringBuilder failureReason = new StringBuilder();
if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
{
Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
backend.getBackendID(), String.valueOf(failureReason));
printMessage(message);
return 1;
}
}
catch (Exception e)
{
Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
backend.getBackendID(), getExceptionMessage(e));
printMessage(message);
return 1;
}
RootContainer rc;
try
{
rc = backend.getReadOnlyRootContainer();
}
catch(Exception e)
{
printMessage(ERR_DBTEST_ERROR_INITIALIZING_BACKEND.get(
backend.getBackendID(),
StaticUtils.stackTraceToSingleLineString(e)));
return 1;
}
try
{
// Create a table of their properties.
TableBuilder builder = new TableBuilder();
int count = 0;
builder.appendHeading(INFO_LABEL_DBTEST_DATABASE_NAME.get());
builder.appendHeading(INFO_LABEL_DBTEST_DATABASE_TYPE.get());
builder.appendHeading(INFO_LABEL_DBTEST_JE_DATABASE_NAME.get());
builder.appendHeading(INFO_LABEL_DBTEST_ENTRY_COUNT.get());
if(base != null)
{
EntryContainer ec = rc.getEntryContainer(base);
if(ec == null)
{
printMessage(ERR_DBTEST_NO_ENTRY_CONTAINERS_FOR_BASE_DN.get(
base.toNormalizedString(), backend.getBackendID()));
return 1;
}
ArrayList<DatabaseContainer> databaseContainers =
new ArrayList<DatabaseContainer>();
ec.listDatabases(databaseContainers);
for(DatabaseContainer dc : databaseContainers)
{
builder.startRow();
builder.appendCell(dc.getName().replace(ec.getDatabasePrefix()+"_",
""));
builder.appendCell(dc.getClass().getSimpleName());
builder.appendCell(dc.getName());
builder.appendCell(dc.getRecordCount());
count++;
}
}
else
{
for(EntryContainer ec : rc.getEntryContainers())
{
builder.startRow();
ArrayList<DatabaseContainer> databaseContainers =
new ArrayList<DatabaseContainer>();
ec.listDatabases(databaseContainers);
builder.appendCell("Base DN: " +
ec.getBaseDN().toNormalizedString());
for(DatabaseContainer dc : databaseContainers)
{
builder.startRow();
builder.appendCell(dc.getName().replace(
ec.getDatabasePrefix()+"_",""));
builder.appendCell(dc.getClass().getSimpleName());
builder.appendCell(dc.getName());
builder.appendCell(dc.getRecordCount());
count++;
}
}
}
TextTablePrinter printer = new TextTablePrinter(out);
builder.print(printer);
out.format("%nTotal: %d%n", count);
return 0;
}
catch(DatabaseException de)
{
printMessage(ERR_DBTEST_ERROR_READING_DATABASE.get(
StaticUtils.stackTraceToSingleLineString(de)));
return 1;
}
finally
{
try
{
// Close the root container
rc.close();
}
catch(DatabaseException de)
{
// Ignore.
}
// Release the shared lock on the backend.
try
{
String lockFile = LockFileManager.getBackendLockFileName(backend);
StringBuilder failureReason = new StringBuilder();
if (! LockFileManager.releaseLock(lockFile, failureReason))
{
Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
backend.getBackendID(), String.valueOf(failureReason));
printMessage(message);
}
}
catch (Exception e)
{
Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
backend.getBackendID(), getExceptionMessage(e));
printMessage(message);
}
}
}
private int listIndexStatus(Argument backendID,
Argument baseDN)
{
Map<LocalDBBackendCfg, BackendImpl> jeBackends = getJEBackends();
BackendImpl backend = null;
DN base = null;
for(BackendImpl b : jeBackends.values())
{
if(b.getBackendID().equalsIgnoreCase(backendID.getValue()))
{
backend = b;
break;
}
}
if(backend == null)
{
printMessage(ERR_DBTEST_NO_BACKENDS_FOR_ID.get(backendID.getValue()));
return 1;
}
if(baseDN.isPresent())
{
try
{
base = DN.decode(baseDN.getValue());
}
catch(DirectoryException de)
{
printMessage(ERR_DBTEST_DECODE_BASE_DN.get(baseDN.getValue(),
getExceptionMessage(de)));
return 1;
}
}
// Acquire an shared lock for the backend.
try
{
String lockFile = LockFileManager.getBackendLockFileName(backend);
StringBuilder failureReason = new StringBuilder();
if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
{
Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
backend.getBackendID(), String.valueOf(failureReason));
printMessage(message);
return 1;
}
}
catch (Exception e)
{
Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
backend.getBackendID(), getExceptionMessage(e));
printMessage(message);
return 1;
}
RootContainer rc;
try
{
rc = backend.getReadOnlyRootContainer();
}
catch(Exception e)
{
printMessage(ERR_DBTEST_ERROR_INITIALIZING_BACKEND.get(
backend.getBackendID(),
StaticUtils.stackTraceToSingleLineString(e)));
return 1;
}
try
{
// Create a table of their properties.
TableBuilder builder = new TableBuilder();
int count = 0;
builder.appendHeading(INFO_LABEL_DBTEST_INDEX_NAME.get());
builder.appendHeading(INFO_LABEL_DBTEST_INDEX_TYPE.get());
builder.appendHeading(INFO_LABEL_DBTEST_JE_DATABASE_NAME.get());
builder.appendHeading(INFO_LABEL_DBTEST_INDEX_STATUS.get());
builder.appendHeading(INFO_LABEL_DBTEST_JE_RECORD_COUNT.get());
builder.appendHeading(
INFO_LABEL_DBTEST_INDEX_UNDEFINED_RECORD_COUNT.get());
builder.appendHeading(Message.raw("95%"));
builder.appendHeading(Message.raw("90%"));
builder.appendHeading(Message.raw("85%"));
EntryContainer ec = rc.getEntryContainer(base);
if(ec == null)
{
printMessage(ERR_DBTEST_NO_ENTRY_CONTAINERS_FOR_BASE_DN.get(
base.toNormalizedString(), backend.getBackendID()));
return 1;
}
ArrayList<DatabaseContainer> databaseContainers =
new ArrayList<DatabaseContainer>();
Map<Index, StringBuilder> undefinedKeys =
new HashMap<Index, StringBuilder>();
ec.listDatabases(databaseContainers);
for(DatabaseContainer dc : databaseContainers)
{
if(dc instanceof Index || dc instanceof VLVIndex)
{
builder.startRow();
builder.appendCell(dc.getName().replace(ec.getDatabasePrefix()+"_",
""));
builder.appendCell(dc.getClass().getSimpleName());
builder.appendCell(dc.getName());
if(dc instanceof Index)
{
builder.appendCell(ec.getState().getIndexTrustState(null,
((Index)dc)));
}
else if(dc instanceof VLVIndex)
{
builder.appendCell(ec.getState().getIndexTrustState(null,
((VLVIndex)dc)));
}
builder.appendCell(dc.getRecordCount());
if(dc instanceof Index)
{
Index index = (Index)dc;
long undefined = 0, ninetyFive = 0, ninety = 0, eighty = 0;
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry data = new DatabaseEntry();
LockMode lockMode = LockMode.DEFAULT;
OperationStatus status;
Cursor cursor = dc.openCursor(null, CursorConfig.DEFAULT);
status = cursor.getFirst(key, data, lockMode);
while(status == OperationStatus.SUCCESS)
{
byte[] bytes = data.getData();
if (bytes.length == 0 || ((bytes[0] & 0x80) == 0x80))
{
// Entry limit has exceeded and there is no encoded
// undefined set size.
undefined ++;
StringBuilder keyList = undefinedKeys.get(index);
if(keyList == null)
{
keyList = new StringBuilder();
undefinedKeys.put(index, keyList);
}
else
{
keyList.append(" ");
}
if(index == ec.getID2Children() || index == ec.getID2Subtree())
{
keyList.append("[").append(
JebFormat.entryIDFromDatabase(key.getData())).append("]");
}
else
{
keyList.append("[").append(
new String(key.getData())).append("]");
}
}
else
{
// Seems like entry limit has not been exceeded and the bytes
// is a list of entry IDs.
double percentFull =
(bytes.length / (double)8) / index.getIndexEntryLimit();
if(percentFull >= .8)
{
if(percentFull < .9)
{
eighty++;
}
else if(percentFull < .95)
{
ninety++;
}
else
{
ninetyFive++;
}
}
}
status = cursor.getNext(key, data, lockMode);
}
builder.appendCell(undefined);
builder.appendCell(ninetyFive);
builder.appendCell(ninety);
builder.appendCell(eighty);
cursor.close();
}
else
{
builder.appendCell("-");
builder.appendCell("-");
builder.appendCell("-");
builder.appendCell("-");
}
count++;
}
}
TextTablePrinter printer = new TextTablePrinter(out);
builder.print(printer);
out.format("%nTotal: %d%n", count);
for(Map.Entry<Index, StringBuilder> e : undefinedKeys.entrySet())
{
out.format("%nIndex: %s%n",
e.getKey().getName().replace(ec.getDatabasePrefix()+"_", ""));
out.format("Undefined keys: %s%n", e.getValue().toString());
}
return 0;
}
catch(DatabaseException de)
{
printMessage(ERR_DBTEST_ERROR_READING_DATABASE.get(
StaticUtils.stackTraceToSingleLineString(de)));
return 1;
}
finally
{
try
{
// Close the root container
rc.close();
}
catch(DatabaseException de)
{
// Ignore.
}
// Release the shared lock on the backend.
try
{
String lockFile = LockFileManager.getBackendLockFileName(backend);
StringBuilder failureReason = new StringBuilder();
if (! LockFileManager.releaseLock(lockFile, failureReason))
{
Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
backend.getBackendID(), String.valueOf(failureReason));
printMessage(message);
}
}
catch (Exception e)
{
Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
backend.getBackendID(), getExceptionMessage(e));
printMessage(message);
}
}
}
private int dumpDatabaseContainer(Argument backendID, Argument baseDN,
Argument databaseName, Argument skipDecode,
Argument statsOnly,
Argument maxKeyValue, Argument minKeyValue,
Argument maxDataSize, Argument minDataSize)
{
Map<LocalDBBackendCfg, BackendImpl> jeBackends = getJEBackends();
BackendImpl backend = null;
DN base = null;
for(BackendImpl b : jeBackends.values())
{
if(b.getBackendID().equalsIgnoreCase(backendID.getValue()))
{
backend = b;
break;
}
}
if(backend == null)
{
printMessage(ERR_DBTEST_NO_BACKENDS_FOR_ID.get(backendID.getValue()));
return 1;
}
try
{
base = DN.decode(baseDN.getValue());
}
catch(DirectoryException de)
{
printMessage(ERR_DBTEST_DECODE_BASE_DN.get(baseDN.getValue(),
getExceptionMessage(de)));
return 1;
}
// Acquire an shared lock for the backend.
try
{
String lockFile = LockFileManager.getBackendLockFileName(backend);
StringBuilder failureReason = new StringBuilder();
if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
{
Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
backend.getBackendID(), String.valueOf(failureReason));
printMessage(message);
return 1;
}
}
catch (Exception e)
{
Message message = ERR_DBTEST_CANNOT_LOCK_BACKEND.get(
backend.getBackendID(), getExceptionMessage(e));
printMessage(message);
return 1;
}
RootContainer rc;
try
{
rc = backend.getReadOnlyRootContainer();
}
catch(Exception e)
{
printMessage(ERR_DBTEST_ERROR_INITIALIZING_BACKEND.get(
backend.getBackendID(),
StaticUtils.stackTraceToSingleLineString(e)));
return 1;
}
try
{
EntryContainer ec = rc.getEntryContainer(base);
if(ec == null)
{
printMessage(ERR_DBTEST_NO_ENTRY_CONTAINERS_FOR_BASE_DN.get(
base.toNormalizedString(), backend.getBackendID()));
return 1;
}
DatabaseContainer databaseContainer = null;
ArrayList<DatabaseContainer> databaseContainers =
new ArrayList<DatabaseContainer>();
ec.listDatabases(databaseContainers);
for(DatabaseContainer dc : databaseContainers)
{
if(dc.getName().replace(ec.getDatabasePrefix()+"_","").
equalsIgnoreCase(databaseName.getValue()))
{
databaseContainer = dc;
break;
}
}
if(databaseContainer == null)
{
printMessage(ERR_DBTEST_NO_DATABASE_CONTAINERS_FOR_NAME.get(
databaseName.getValue(), base.toNormalizedString(),
backend.getBackendID()));
return 1;
}
int count = 0;
long totalKeySize = 0;
long totalDataSize = 0;
int indent = 4;
Cursor cursor =
databaseContainer.openCursor(null, CursorConfig.DEFAULT);
try
{
DatabaseEntry key = new DatabaseEntry();
DatabaseEntry data = new DatabaseEntry();
LockMode lockMode = LockMode.DEFAULT;
OperationStatus status;
Comparator<byte[]> defaultComparator =
new AttributeIndex.KeyComparator();
byte[] start = null;
byte[] end = null;
int minSize = -1;
int maxSize = -1;
if(maxDataSize.isPresent())
{
try
{
maxSize = maxDataSize.getIntValue();
}
catch(Exception e)
{
printMessage(ERR_DBTEST_CANNOT_DECODE_SIZE.get(
maxDataSize.getValue(), getExceptionMessage(e)));
return 1;
}
}
if(minDataSize.isPresent())
{
try
{
minSize = minDataSize.getIntValue();
}
catch(Exception e)
{
printMessage(ERR_DBTEST_CANNOT_DECODE_SIZE.get(
minDataSize.getValue(), getExceptionMessage(e)));
return 1;
}
}
// Parse the min value if given
if(minKeyValue.isPresent())
{
try
{
if(minKeyValue.getValue().startsWith("0x"))
{
start =
StaticUtils.hexStringToByteArray(minKeyValue.getValue().
substring(2));
}
else
{
if(databaseContainer instanceof DN2ID ||
databaseContainer instanceof DN2URI)
{
// Encode the value as a DN
start = StaticUtils.getBytes(
DN.decode(minKeyValue.getValue()).toNormalizedString());
}
else if(databaseContainer instanceof ID2Entry)
{
// Encode the value as an entryID
start = JebFormat.entryIDToDatabase(
Long.parseLong(minKeyValue.getValue()));
}
else if(databaseContainer instanceof VLVIndex)
{
// Encode the value as a size/value pair
byte[] vBytes = StaticUtils.getBytes(minKeyValue.getValue());
ByteStringBuilder builder = new ByteStringBuilder();
builder.appendBERLength(vBytes.length);
builder.append(vBytes);
start = builder.toByteArray();
}
else
{
start = StaticUtils.getBytes(minKeyValue.getValue());
}
}
}
catch(Exception e)
{
printMessage(ERR_DBTEST_CANNOT_DECODE_KEY.get(
minKeyValue.getValue(), getExceptionMessage(e)));
return 1;
}
}
// Parse the max value if given
if(maxKeyValue.isPresent())
{
try
{
if(maxKeyValue.getValue().startsWith("0x"))
{
end =
StaticUtils.hexStringToByteArray(maxKeyValue.getValue().
substring(2));
}
else
{
if(databaseContainer instanceof DN2ID ||
databaseContainer instanceof DN2URI)
{
// Encode the value as a DN
end = StaticUtils.getBytes(
DN.decode(maxKeyValue.getValue()).toNormalizedString());
}
else if(databaseContainer instanceof ID2Entry)
{
// Encode the value as an entryID
end = JebFormat.entryIDToDatabase(
Long.parseLong(maxKeyValue.getValue()));
}
else if(databaseContainer instanceof VLVIndex)
{
// Encode the value as a size/value pair
byte[] vBytes = StaticUtils.getBytes(maxKeyValue.getValue());
ByteStringBuilder builder = new ByteStringBuilder();
builder.appendBERLength(vBytes.length);
builder.append(vBytes);
start = builder.toByteArray();
}
else
{
end = StaticUtils.getBytes(maxKeyValue.getValue());
}
}
}
catch(Exception e)
{
printMessage(ERR_DBTEST_CANNOT_DECODE_KEY.get(
maxKeyValue.getValue(), getExceptionMessage(e)));
return 1;
}
}
if(start != null)
{
key.setData(start);
status = cursor.getSearchKey(key, data, lockMode);
}
else
{
status = cursor.getFirst(key, data, lockMode);
}
while(status == OperationStatus.SUCCESS)
{
// Make sure this record is within the value size params
if((minSize > 0 && data.getSize() < minSize) ||
(maxSize > 0 && data.getSize() > maxSize))
{
status = cursor.getNext(key, data, lockMode);
continue;
}
// Make sure we haven't gone pass the max value yet
if(end != null)
{
if(databaseContainer instanceof DN2ID)
{
if(defaultComparator.compare(key.getData(), end) > 0)
{
break;
}
}
else if(databaseContainer instanceof DN2URI)
{
if(defaultComparator.compare(key.getData(), end) > 0)
{
break;
}
}
else if(databaseContainer instanceof Index)
{
if(((Index)databaseContainer).indexer.getComparator().
compare(key.getData(), end) > 0)
{
break;
}
}
else if(databaseContainer instanceof VLVIndex)
{
if(((VLVIndex)databaseContainer).comparator.
compare(key.getData(), end) > 0)
{
break;
}
}
else
{
if(defaultComparator.compare(key.getData(),
end) > 0)
{
break;
}
}
}
if (!statsOnly.isPresent())
{
Message keyLabel = INFO_LABEL_DBTEST_KEY.get();
Message dataLabel = INFO_LABEL_DBTEST_DATA.get();
String formatedKey = null;
String formatedData = null;
if(!skipDecode.isPresent())
{
if(databaseContainer instanceof DN2ID)
{
try
{
formatedKey = JebFormat.dnFromDNKey(
key.getData(), 0, key.getSize(), ec.getBaseDN()).
toNormalizedString();
keyLabel = INFO_LABEL_DBTEST_ENTRY_DN.get();
}
catch(Exception e)
{
Message message =
ERR_DBTEST_DECODE_FAIL.get(getExceptionMessage(e));
printMessage(message);
}
formatedData = String.valueOf(
JebFormat.entryIDFromDatabase(data.getData()));
dataLabel = INFO_LABEL_DBTEST_ENTRY_ID.get();
}
else if(databaseContainer instanceof ID2Entry)
{
formatedKey = String.valueOf(
JebFormat.entryIDFromDatabase(key.getData()));
keyLabel = INFO_LABEL_DBTEST_ENTRY_ID.get();
try
{
formatedData = System.getProperty("line.separator") +
ID2Entry.entryFromDatabase(
ByteString.wrap(data.getData()),
ec.getRootContainer().getCompressedSchema()).
toLDIFString();
dataLabel = INFO_LABEL_DBTEST_ENTRY.get();
}
catch(Exception e)
{
Message message =
ERR_DBTEST_DECODE_FAIL.get(getExceptionMessage(e));
printMessage(message);
}
}
else if(databaseContainer instanceof DN2URI)
{
try
{
formatedKey = new String(key.getData());
keyLabel = INFO_LABEL_DBTEST_ENTRY_DN.get();
}
catch(Exception e)
{
Message message =
ERR_DBTEST_DECODE_FAIL.get(getExceptionMessage(e));
printMessage(message);
}
formatedData = new String(key.getData());
dataLabel = INFO_LABEL_DBTEST_URI.get();
}
else if(databaseContainer instanceof Index)
{
formatedKey = new String(key.getData());
keyLabel = INFO_LABEL_DBTEST_INDEX_VALUE.get();
EntryIDSet idSet = new EntryIDSet(key.getData(),
data.getData());
if(idSet.isDefined())
{
int lineCount = 0;
StringBuilder builder = new StringBuilder();
Iterator<EntryID> i = idSet.iterator();
while(i.hasNext())
{
builder.append(i.next());
if(lineCount == 10)
{
builder.append(System.getProperty("line.separator"));
lineCount = 0;
}
else
{
builder.append(" ");
lineCount++;
}
}
formatedData = builder.toString();
}
else
{
formatedData = idSet.toString();
}
dataLabel = INFO_LABEL_DBTEST_INDEX_ENTRY_ID_LIST.get();
}
else if(databaseContainer instanceof VLVIndex)
{
VLVIndex index = (VLVIndex)databaseContainer;
SortKey[] sortKeys = index.sortOrder.getSortKeys();
int pos = 0;
byte[] keyBytes = key.getData();
if(keyBytes.length > 0)
{
StringBuilder builder = new StringBuilder();
// Decode the attribute values
for(SortKey sortKey : sortKeys)
{
int valueLength = keyBytes[pos] & 0x7F;
if (keyBytes[pos++] != valueLength)
{
int numLengthBytes = valueLength;
valueLength = 0;
for (int k=0; k < numLengthBytes; k++, pos++)
{
valueLength = (valueLength << 8) |
(keyBytes[pos] & 0xFF);
}
}
byte[] valueBytes = new byte[valueLength];
System.arraycopy(keyBytes, pos, valueBytes, 0,
valueLength);
builder.append(sortKey.getAttributeType().getNameOrOID());
builder.append(": ");
if(valueBytes.length == 0)
{
builder.append("NULL");
}
else
{
builder.append(new String(valueBytes));
}
builder.append(" ");
pos += valueLength;
}
byte[] entryIDBytes = new byte[8];
System.arraycopy(keyBytes, pos, entryIDBytes, 0,
entryIDBytes.length);
long entryID = JebFormat.entryIDFromDatabase(entryIDBytes);
formatedKey = System.getProperty("line.separator") +
String.valueOf(entryID) + ": " + builder.toString();
}
else
{
formatedKey = "UNBOUNDED";
}
keyLabel = INFO_LABEL_DBTEST_VLV_INDEX_LAST_SORT_KEYS.get();
try
{
StringBuilder builder = new StringBuilder();
SortValuesSet svs = new SortValuesSet(key.getData(),
data.getData(),
index);
long[] entryIDs = svs.getEntryIDs();
for(int i = 0; i < entryIDs.length; i++)
{
builder.append(String.valueOf(entryIDs[i]));
builder.append(": ");
for(int j = 0; j < sortKeys.length; j++)
{
SortKey sortKey = index.sortOrder.getSortKeys()[j];
ByteString value = svs.getValue(i * sortKeys.length + j);
builder.append(sortKey.getAttributeType().getNameOrOID());
builder.append(": ");
if(value == null)
{
builder.append("NULL");
}
else if(value.length() == 0)
{
builder.append("SIZE-EXCEEDED");
}
else
{
builder.append(value.toString());
}
builder.append(" ");
}
builder.append(System.getProperty("line.separator"));
}
formatedData = System.getProperty("line.separator") +
builder.toString();
dataLabel = INFO_LABEL_DBTEST_INDEX_ENTRY_ID_LIST.get();
}
catch(Exception e)
{
Message message =
ERR_DBTEST_DECODE_FAIL.get(getExceptionMessage(e));
printMessage(message);
}
}
}
if(formatedKey == null)
{
StringBuilder keyBuilder = new StringBuilder();
StaticUtils.byteArrayToHexPlusAscii(keyBuilder, key.getData(),
indent);
formatedKey = System.getProperty("line.separator") +
keyBuilder.toString();
}
if(formatedData == null)
{
StringBuilder dataBuilder = new StringBuilder();
StaticUtils.byteArrayToHexPlusAscii(dataBuilder, data.getData(),
indent);
formatedData = System.getProperty("line.separator") +
dataBuilder.toString();
}
out.format("%s (%d bytes): %s%n", keyLabel,
key.getData().length, formatedKey);
out.format("%s (%d bytes): %s%n%n", dataLabel,
data.getData().length, formatedData);
}
status = cursor.getNext(key, data, lockMode);
count++;
totalKeySize += key.getData().length;
totalDataSize += data.getData().length;
}
}
finally
{
cursor.close();
}
out.format("%nTotal Records: %d%n", count);
if(count > 0)
{
out.format("Total / Average Key Size: %d bytes / %d bytes%n",
totalKeySize, totalKeySize / count);
out.format("Total / Average Data Size: %d bytes / %d bytes%n",
totalDataSize, totalDataSize / count);
}
return 0;
}
catch(DatabaseException de)
{
printMessage(ERR_DBTEST_ERROR_READING_DATABASE.get(
StaticUtils.stackTraceToSingleLineString(de)));
return 1;
}
finally
{
try
{
// Close the root container
rc.close();
}
catch(DatabaseException de)
{
// Ignore.
}
// Release the shared lock on the backend.
try
{
String lockFile = LockFileManager.getBackendLockFileName(backend);
StringBuilder failureReason = new StringBuilder();
if (! LockFileManager.releaseLock(lockFile, failureReason))
{
Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
backend.getBackendID(), String.valueOf(failureReason));
printMessage(message);
}
}
catch (Exception e)
{
Message message = WARN_DBTEST_CANNOT_UNLOCK_BACKEND.get(
backend.getBackendID(), getExceptionMessage(e));
printMessage(message);
}
}
}
private Map<LocalDBBackendCfg, BackendImpl> getJEBackends()
{
ArrayList<Backend> backendList = new ArrayList<Backend>();
ArrayList<BackendCfg> entryList = new ArrayList<BackendCfg>();
ArrayList<List<DN>> dnList = new ArrayList<List<DN>>();
int code = BackendToolUtils.getBackends(backendList, entryList, dnList);
// TODO: Throw error if return code is not 0
LinkedHashMap<LocalDBBackendCfg, BackendImpl> jeBackends =
new LinkedHashMap<LocalDBBackendCfg, BackendImpl>();
for(int i = 0; i < backendList.size(); i++)
{
Backend backend = backendList.get(i);
if(backend instanceof BackendImpl)
{
jeBackends.put((LocalDBBackendCfg)entryList.get(i),
(BackendImpl)backend);
}
}
return jeBackends;
}
/**
* Displays a message to the error stream.
*
* @param msg
* The message.
*/
public final void printMessage(Message msg) {
err.println(wrapText(msg.toString(), MAX_LINE_WIDTH));
}
}