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