001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2008 Sun Microsystems, Inc. 015 * Portions Copyright 2012-2016 ForgeRock AS. 016 */ 017package org.opends.server.tools; 018 019import static com.forgerock.opendj.cli.ArgumentConstants.*; 020import static com.forgerock.opendj.cli.Utils.*; 021import static com.forgerock.opendj.cli.CommonArguments.*; 022 023import static org.opends.messages.ToolMessages.*; 024import static org.opends.server.util.StaticUtils.*; 025 026import java.io.OutputStream; 027import java.io.PrintStream; 028import java.util.ArrayList; 029import java.util.List; 030import java.util.logging.Level; 031 032import org.forgerock.i18n.LocalizableMessage; 033import org.forgerock.opendj.config.server.ConfigException; 034import org.forgerock.opendj.ldap.DN; 035import org.opends.server.admin.std.server.BackendCfg; 036import org.opends.server.api.Backend; 037import org.opends.server.api.Backend.BackendOperation; 038import org.opends.server.backends.VerifyConfig; 039import org.opends.server.core.CoreConfigManager; 040import org.opends.server.core.DirectoryServer; 041import org.opends.server.core.DirectoryServer.DirectoryServerVersionHandler; 042import org.opends.server.core.LockFileManager; 043import org.opends.server.extensions.ConfigFileHandler; 044import org.opends.server.loggers.JDKLogging; 045import org.opends.server.types.InitializationException; 046import org.opends.server.types.NullOutputStream; 047import org.opends.server.util.BuildVersion; 048 049import com.forgerock.opendj.cli.ArgumentException; 050import com.forgerock.opendj.cli.ArgumentParser; 051import com.forgerock.opendj.cli.BooleanArgument; 052import com.forgerock.opendj.cli.StringArgument; 053 054/** 055 * This program provides a utility to verify the contents of the indexes 056 * of a Directory Server backend. This will be a process that is 057 * intended to run separate from Directory Server and not internally within the 058 * server process (e.g., via the tasks interface). 059 */ 060public class VerifyIndex 061{ 062 063 /** 064 * Processes the command-line arguments and invokes the verify process. 065 * 066 * @param args The command-line arguments provided to this program. 067 */ 068 public static void main(String[] args) 069 { 070 int retCode = mainVerifyIndex(args, true, System.out, System.err); 071 if(retCode != 0) 072 { 073 System.exit(filterExitCode(retCode)); 074 } 075 } 076 077 /** 078 * Processes the command-line arguments and invokes the verify process. 079 * 080 * @param args The command-line arguments provided to this 081 * program. 082 * @param initializeServer Indicates whether to initialize the server. 083 * @param outStream The output stream to use for standard output, or 084 * {@code null} if standard output is not needed. 085 * @param errStream The output stream to use for standard error, or 086 * {@code null} if standard error is not needed. 087 * 088 * @return The error code. 089 */ 090 public static int mainVerifyIndex(String[] args, boolean initializeServer, 091 OutputStream outStream, 092 OutputStream errStream) 093 { 094 PrintStream err = NullOutputStream.wrapOrNullStream(errStream); 095 JDKLogging.enableConsoleLoggingForOpenDJ(Level.FINE); 096 097 // Define the command-line arguments that may be used with this program. 098 StringArgument configClass = null; 099 StringArgument configFile = null; 100 StringArgument baseDNString = null; 101 StringArgument indexList = null; 102 BooleanArgument cleanMode = null; 103 BooleanArgument countErrors = null; 104 BooleanArgument displayUsage = null; 105 106 107 // Create the command-line argument parser for use with this program. 108 LocalizableMessage toolDescription = INFO_VERIFYINDEX_TOOL_DESCRIPTION.get(); 109 ArgumentParser argParser = 110 new ArgumentParser("org.opends.server.tools.VerifyIndex", 111 toolDescription, false); 112 argParser.setShortToolDescription(REF_SHORT_DESC_VERIFY_INDEX.get()); 113 argParser.setVersionHandler(new DirectoryServerVersionHandler()); 114 115 // Initialize all the command-line argument types and register them with the 116 // parser. 117 try 118 { 119 configClass = 120 StringArgument.builder(OPTION_LONG_CONFIG_CLASS) 121 .shortIdentifier(OPTION_SHORT_CONFIG_CLASS) 122 .description(INFO_DESCRIPTION_CONFIG_CLASS.get()) 123 .hidden() 124 .required() 125 .defaultValue(ConfigFileHandler.class.getName()) 126 .valuePlaceholder(INFO_CONFIGCLASS_PLACEHOLDER.get()) 127 .buildAndAddToParser(argParser); 128 configFile = 129 StringArgument.builder("configFile") 130 .shortIdentifier('f') 131 .description(INFO_DESCRIPTION_CONFIG_FILE.get()) 132 .hidden() 133 .required() 134 .valuePlaceholder(INFO_CONFIGFILE_PLACEHOLDER.get()) 135 .buildAndAddToParser(argParser); 136 baseDNString = 137 StringArgument.builder(OPTION_LONG_BASEDN) 138 .shortIdentifier(OPTION_SHORT_BASEDN) 139 .description(INFO_VERIFYINDEX_DESCRIPTION_BASE_DN.get()) 140 .required() 141 .valuePlaceholder(INFO_BASEDN_PLACEHOLDER.get()) 142 .buildAndAddToParser(argParser); 143 indexList = 144 StringArgument.builder("index") 145 .shortIdentifier('i') 146 .description(INFO_VERIFYINDEX_DESCRIPTION_INDEX_NAME.get()) 147 .multiValued() 148 .valuePlaceholder(INFO_INDEX_PLACEHOLDER.get()) 149 .buildAndAddToParser(argParser); 150 cleanMode = 151 BooleanArgument.builder("clean") 152 .shortIdentifier('c') 153 .description(INFO_VERIFYINDEX_DESCRIPTION_VERIFY_CLEAN.get()) 154 .buildAndAddToParser(argParser); 155 countErrors = 156 BooleanArgument.builder("countErrors") 157 .description(INFO_VERIFYINDEX_DESCRIPTION_COUNT_ERRORS.get()) 158 .buildAndAddToParser(argParser); 159 160 displayUsage = showUsageArgument(); 161 argParser.addArgument(displayUsage); 162 argParser.setUsageArgument(displayUsage); 163 } 164 catch (ArgumentException ae) 165 { 166 printWrappedText(err, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage())); 167 return 1; 168 } 169 170 171 // Parse the command-line arguments provided to this program. 172 try 173 { 174 argParser.parseArguments(args); 175 } 176 catch (ArgumentException ae) 177 { 178 argParser.displayMessageAndUsageReference(err, ERR_ERROR_PARSING_ARGS.get(ae.getMessage())); 179 return 1; 180 } 181 182 183 // If we should just display usage or version information, 184 // then print it and exit. 185 if (argParser.usageOrVersionDisplayed()) 186 { 187 return 0; 188 } 189 190 if (cleanMode.isPresent() && indexList.getValues().size() != 1) 191 { 192 argParser.displayMessageAndUsageReference(err, ERR_VERIFYINDEX_VERIFY_CLEAN_REQUIRES_SINGLE_INDEX.get()); 193 return 1; 194 } 195 196 // Checks the version - if upgrade required, the tool is unusable 197 try 198 { 199 BuildVersion.checkVersionMismatch(); 200 } 201 catch (InitializationException e) 202 { 203 printWrappedText(err, e.getMessage()); 204 return 1; 205 } 206 207 // Perform the initial bootstrap of the Directory Server and process the 208 // configuration. 209 DirectoryServer directoryServer = DirectoryServer.getInstance(); 210 211 if (initializeServer) 212 { 213 try 214 { 215 DirectoryServer.bootstrapClient(); 216 DirectoryServer.initializeJMX(); 217 } 218 catch (Exception e) 219 { 220 printWrappedText(err, ERR_SERVER_BOOTSTRAP_ERROR.get(getExceptionMessage(e))); 221 return 1; 222 } 223 224 try 225 { 226 directoryServer.initializeConfiguration(configClass.getValue(), 227 configFile.getValue()); 228 } 229 catch (InitializationException ie) 230 { 231 printWrappedText(err, ERR_CANNOT_LOAD_CONFIG.get(ie.getMessage())); 232 return 1; 233 } 234 catch (Exception e) 235 { 236 printWrappedText(err, ERR_CANNOT_LOAD_CONFIG.get(getExceptionMessage(e))); 237 return 1; 238 } 239 240 241 242 // Initialize the Directory Server schema elements. 243 try 244 { 245 directoryServer.initializeSchema(); 246 } 247 catch (ConfigException | InitializationException e) 248 { 249 printWrappedText(err, ERR_CANNOT_LOAD_SCHEMA.get(e.getMessage())); 250 return 1; 251 } 252 catch (Exception e) 253 { 254 printWrappedText(err, ERR_CANNOT_LOAD_SCHEMA.get(getExceptionMessage(e))); 255 return 1; 256 } 257 258 259 // Initialize the Directory Server core configuration. 260 try 261 { 262 CoreConfigManager coreConfigManager = new CoreConfigManager(directoryServer.getServerContext()); 263 coreConfigManager.initializeCoreConfig(); 264 } 265 catch (ConfigException | InitializationException e) 266 { 267 printWrappedText(err, ERR_CANNOT_INITIALIZE_CORE_CONFIG.get(e.getMessage())); 268 return 1; 269 } 270 catch (Exception e) 271 { 272 printWrappedText(err, ERR_CANNOT_INITIALIZE_CORE_CONFIG.get(getExceptionMessage(e))); 273 return 1; 274 } 275 276 277 // Initialize the Directory Server crypto manager. 278 try 279 { 280 directoryServer.initializeCryptoManager(); 281 } 282 catch (ConfigException | InitializationException e) 283 { 284 printWrappedText(err, ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get(e.getMessage())); 285 return 1; 286 } 287 catch (Exception e) 288 { 289 printWrappedText(err, ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get(getExceptionMessage(e))); 290 return 1; 291 } 292 } 293 294 // Decode the base DN provided by the user. 295 DN verifyBaseDN ; 296 try 297 { 298 verifyBaseDN = DN.valueOf(baseDNString.getValue()); 299 } 300 catch (Exception e) 301 { 302 printWrappedText(err, ERR_CANNOT_DECODE_BASE_DN.get(baseDNString.getValue(), getExceptionMessage(e))); 303 return 1; 304 } 305 306 307 // Get information about the backends defined in the server. Iterate 308 // through them, finding the one backend to be verified. 309 ArrayList<Backend> backendList = new ArrayList<>(); 310 ArrayList<BackendCfg> entryList = new ArrayList<>(); 311 ArrayList<List<DN>> dnList = new ArrayList<>(); 312 BackendToolUtils.getBackends(backendList, entryList, dnList); 313 314 Backend<?> backend = null; 315 int numBackends = backendList.size(); 316 for (int i=0; i < numBackends; i++) 317 { 318 Backend<?> b = backendList.get(i); 319 List<DN> baseDNs = dnList.get(i); 320 321 if (baseDNs.contains(verifyBaseDN)) 322 { 323 if (backend != null) 324 { 325 printWrappedText(err, ERR_MULTIPLE_BACKENDS_FOR_BASE.get(baseDNString.getValue())); 326 return 1; 327 } 328 backend = b; 329 } 330 } 331 332 if (backend == null) 333 { 334 printWrappedText(err, ERR_NO_BACKENDS_FOR_BASE.get(baseDNString.getValue())); 335 return 1; 336 } 337 338 if (!backend.supports(BackendOperation.INDEXING)) 339 { 340 printWrappedText(err, ERR_BACKEND_NO_INDEXING_SUPPORT.get()); 341 return 1; 342 } 343 344 // Initialize the verify configuration. 345 VerifyConfig verifyConfig = new VerifyConfig(); 346 verifyConfig.setBaseDN(verifyBaseDN); 347 if (cleanMode.isPresent()) 348 { 349 for (String s : indexList.getValues()) 350 { 351 verifyConfig.addCleanIndex(s); 352 } 353 } 354 else 355 { 356 for (String s : indexList.getValues()) 357 { 358 verifyConfig.addCompleteIndex(s); 359 } 360 } 361 362 363 // Acquire a shared lock for the backend. 364 try 365 { 366 String lockFile = LockFileManager.getBackendLockFileName(backend); 367 StringBuilder failureReason = new StringBuilder(); 368 if (! LockFileManager.acquireSharedLock(lockFile, failureReason)) 369 { 370 printWrappedText(err, ERR_VERIFYINDEX_CANNOT_LOCK_BACKEND.get(backend.getBackendID(), failureReason)); 371 return 1; 372 } 373 } 374 catch (Exception e) 375 { 376 printWrappedText(err, ERR_VERIFYINDEX_CANNOT_LOCK_BACKEND.get(backend.getBackendID(), getExceptionMessage(e))); 377 return 1; 378 } 379 380 381 try 382 { 383 // Launch the verify process. 384 final long errorCount = backend.verifyBackend(verifyConfig); 385 if (countErrors.isPresent()) 386 { 387 if (errorCount > Integer.MAX_VALUE) 388 { 389 return Integer.MAX_VALUE; 390 } 391 return (int) errorCount; 392 } 393 return 0; 394 } 395 catch (InitializationException e) 396 { 397 printWrappedText(err, ERR_VERIFYINDEX_ERROR_DURING_VERIFY.get(e.getMessage())); 398 return 1; 399 } 400 catch (Exception e) 401 { 402 printWrappedText(err, ERR_VERIFYINDEX_ERROR_DURING_VERIFY.get(stackTraceToSingleLineString(e))); 403 return 1; 404 } 405 finally 406 { 407 // Release the shared lock on the backend. 408 try 409 { 410 String lockFile = LockFileManager.getBackendLockFileName(backend); 411 StringBuilder failureReason = new StringBuilder(); 412 if (! LockFileManager.releaseLock(lockFile, failureReason)) 413 { 414 printWrappedText(err, WARN_VERIFYINDEX_CANNOT_UNLOCK_BACKEND.get(backend.getBackendID(), failureReason)); 415 } 416 } 417 catch (Exception e) 418 { 419 printWrappedText(err, 420 WARN_VERIFYINDEX_CANNOT_UNLOCK_BACKEND.get(backend.getBackendID(), getExceptionMessage(e))); 421 } 422 } 423 } 424}