001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.tools; 028 import org.opends.messages.Message; 029 030 import static org.opends.server.util.StaticUtils.wrapText; 031 import org.opends.server.util.args.ArgumentException; 032 import org.opends.server.util.args.ArgumentParser; 033 import org.opends.server.util.args.BooleanArgument; 034 import org.opends.server.util.args.StringArgument; 035 import org.opends.server.extensions.ConfigFileHandler; 036 037 import static org.opends.messages.ToolMessages.*; 038 import org.opends.server.config.ConfigException; 039 import static org.opends.server.loggers.ErrorLogger.logError; 040 import org.opends.server.loggers.TextWriter; 041 import org.opends.server.loggers.ErrorLogger; 042 import org.opends.server.loggers.TextErrorLogPublisher; 043 import org.opends.server.loggers.debug.TextDebugLogPublisher; 044 import org.opends.server.loggers.debug.DebugLogger; 045 import static org.opends.server.util.ServerConstants.*; 046 import static org.opends.server.util.StaticUtils.*; 047 import org.opends.server.core.DirectoryServer; 048 import org.opends.server.core.CoreConfigManager; 049 import org.opends.server.core.LockFileManager; 050 import org.opends.server.types.*; 051 import org.opends.server.api.Backend; 052 import org.opends.server.api.ErrorLogPublisher; 053 import org.opends.server.api.DebugLogPublisher; 054 import org.opends.server.backends.jeb.BackendImpl; 055 import org.opends.server.backends.jeb.RebuildConfig; 056 import org.opends.server.admin.std.server.BackendCfg; 057 058 import java.io.OutputStream; 059 import java.io.PrintStream; 060 import java.util.ArrayList; 061 import java.util.List; 062 063 064 /** 065 * This program provides a utility to rebuild the contents of the indexes 066 * of a Directory Server backend. This will be a process that is 067 * intended to run separate from Directory Server and not internally within the 068 * server process (e.g., via the tasks interface). 069 */ 070 public class RebuildIndex 071 { 072 073 /** 074 * Processes the command-line arguments and invokes the rebuild process. 075 * 076 * @param args The command-line arguments provided to this program. 077 */ 078 public static void main(String[] args) 079 { 080 int retCode = mainRebuildIndex(args, true, System.out, System.err); 081 082 if(retCode != 0) 083 { 084 System.exit(filterExitCode(retCode)); 085 } 086 } 087 088 /** 089 * Processes the command-line arguments and invokes the rebuild 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 mainRebuildIndex(String[] args, boolean initializeServer, 102 OutputStream outStream, 103 OutputStream errStream) 104 { 105 PrintStream out; 106 if (outStream == null) 107 { 108 out = NullOutputStream.printStream(); 109 } 110 else 111 { 112 out = new PrintStream(outStream); 113 } 114 115 PrintStream err; 116 if (errStream == null) 117 { 118 err = NullOutputStream.printStream(); 119 } 120 else 121 { 122 err = new PrintStream(errStream); 123 } 124 125 // Define the command-line arguments that may be used with this program. 126 StringArgument configClass = null; 127 StringArgument configFile = null; 128 StringArgument baseDNString = null; 129 StringArgument indexList = null; 130 BooleanArgument displayUsage = null; 131 132 133 // Create the command-line argument parser for use with this program. 134 Message toolDescription = INFO_REBUILDINDEX_TOOL_DESCRIPTION.get(); 135 ArgumentParser argParser = 136 new ArgumentParser("org.opends.server.tools.RebuildIndex", 137 toolDescription, false); 138 139 140 // Initialize all the command-line argument types and register them with the 141 // parser. 142 try 143 { 144 configClass = 145 new StringArgument("configclass", 'C', "configClass", true, false, 146 true, INFO_CONFIGCLASS_PLACEHOLDER.get(), 147 ConfigFileHandler.class.getName(), null, 148 INFO_DESCRIPTION_CONFIG_CLASS.get()); 149 configClass.setHidden(true); 150 argParser.addArgument(configClass); 151 152 153 configFile = 154 new StringArgument("configfile", 'f', "configFile", true, false, 155 true, INFO_CONFIGFILE_PLACEHOLDER.get(), null, 156 null, 157 INFO_DESCRIPTION_CONFIG_FILE.get()); 158 configFile.setHidden(true); 159 argParser.addArgument(configFile); 160 161 162 baseDNString = 163 new StringArgument("basedn", 'b', "baseDN", true, false, true, 164 INFO_BASEDN_PLACEHOLDER.get(), null, null, 165 INFO_REBUILDINDEX_DESCRIPTION_BASE_DN.get()); 166 argParser.addArgument(baseDNString); 167 168 169 indexList = 170 new StringArgument("index", 'i', "index", 171 false, true, true, 172 INFO_INDEX_PLACEHOLDER.get(), null, null, 173 INFO_REBUILDINDEX_DESCRIPTION_INDEX_NAME.get()); 174 argParser.addArgument(indexList); 175 176 177 displayUsage = 178 new BooleanArgument("help", 'H', "help", 179 INFO_DESCRIPTION_USAGE.get()); 180 argParser.addArgument(displayUsage); 181 argParser.setUsageArgument(displayUsage); 182 } 183 catch (ArgumentException ae) 184 { 185 Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()); 186 187 err.println(wrapText(message, MAX_LINE_WIDTH)); 188 return 1; 189 } 190 191 192 // Parse the command-line arguments provided to this program. 193 try 194 { 195 argParser.parseArguments(args); 196 } 197 catch (ArgumentException ae) 198 { 199 Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage()); 200 201 err.println(wrapText(message, MAX_LINE_WIDTH)); 202 err.println(argParser.getUsage()); 203 return 1; 204 } 205 206 207 // If we should just display usage or version information, 208 // then print it and exit. 209 if (argParser.usageOrVersionDisplayed()) 210 { 211 return 0; 212 } 213 214 215 216 217 // If no arguments were provided, then display usage information and exit. 218 int numArgs = args.length; 219 if (numArgs == 0) 220 { 221 out.println(argParser.getUsage()); 222 return 1; 223 } 224 225 226 if (indexList.getValues().size() <= 0) 227 { 228 Message message = ERR_REBUILDINDEX_REQUIRES_AT_LEAST_ONE_INDEX.get(); 229 230 err.println(wrapText(message, MAX_LINE_WIDTH)); 231 out.println(argParser.getUsage()); 232 return 1; 233 } 234 235 // Perform the initial bootstrap of the Directory Server and process the 236 // configuration. 237 DirectoryServer directoryServer = DirectoryServer.getInstance(); 238 239 if (initializeServer) 240 { 241 try 242 { 243 DirectoryServer.bootstrapClient(); 244 DirectoryServer.initializeJMX(); 245 } 246 catch (Exception e) 247 { 248 Message message = ERR_SERVER_BOOTSTRAP_ERROR.get( 249 getExceptionMessage(e)); 250 err.println(wrapText(message, MAX_LINE_WIDTH)); 251 return 1; 252 } 253 254 try 255 { 256 directoryServer.initializeConfiguration(configClass.getValue(), 257 configFile.getValue()); 258 } 259 catch (InitializationException ie) 260 { 261 Message message = ERR_CANNOT_LOAD_CONFIG.get(ie.getMessage()); 262 err.println(wrapText(message, MAX_LINE_WIDTH)); 263 return 1; 264 } 265 catch (Exception e) 266 { 267 Message message = ERR_CANNOT_LOAD_CONFIG.get(getExceptionMessage(e)); 268 err.println(wrapText(message, MAX_LINE_WIDTH)); 269 return 1; 270 } 271 272 273 274 // Initialize the Directory Server schema elements. 275 try 276 { 277 directoryServer.initializeSchema(); 278 } 279 catch (ConfigException ce) 280 { 281 Message message = ERR_CANNOT_LOAD_SCHEMA.get(ce.getMessage()); 282 err.println(wrapText(message, MAX_LINE_WIDTH)); 283 return 1; 284 } 285 catch (InitializationException ie) 286 { 287 Message message = ERR_CANNOT_LOAD_SCHEMA.get(ie.getMessage()); 288 err.println(wrapText(message, MAX_LINE_WIDTH)); 289 return 1; 290 } 291 catch (Exception e) 292 { 293 Message message = ERR_CANNOT_LOAD_SCHEMA.get(getExceptionMessage(e)); 294 err.println(wrapText(message, MAX_LINE_WIDTH)); 295 return 1; 296 } 297 298 299 // Initialize the Directory Server core configuration. 300 try 301 { 302 CoreConfigManager coreConfigManager = new CoreConfigManager(); 303 coreConfigManager.initializeCoreConfig(); 304 } 305 catch (ConfigException ce) 306 { 307 Message message = ERR_CANNOT_INITIALIZE_CORE_CONFIG.get( 308 ce.getMessage()); 309 err.println(wrapText(message, MAX_LINE_WIDTH)); 310 return 1; 311 } 312 catch (InitializationException ie) 313 { 314 Message message = ERR_CANNOT_INITIALIZE_CORE_CONFIG.get( 315 ie.getMessage()); 316 err.println(wrapText(message, MAX_LINE_WIDTH)); 317 return 1; 318 } 319 catch (Exception e) 320 { 321 Message message = ERR_CANNOT_INITIALIZE_CORE_CONFIG.get( 322 getExceptionMessage(e)); 323 err.println(wrapText(message, MAX_LINE_WIDTH)); 324 return 1; 325 } 326 327 328 // Initialize the Directory Server crypto manager. 329 try 330 { 331 directoryServer.initializeCryptoManager(); 332 } 333 catch (ConfigException ce) 334 { 335 Message message = ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get( 336 ce.getMessage()); 337 err.println(wrapText(message, MAX_LINE_WIDTH)); 338 return 1; 339 } 340 catch (InitializationException ie) 341 { 342 Message message = ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get( 343 ie.getMessage()); 344 err.println(wrapText(message, MAX_LINE_WIDTH)); 345 return 1; 346 } 347 catch (Exception e) 348 { 349 Message message = ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get( 350 getExceptionMessage(e)); 351 err.println(wrapText(message, MAX_LINE_WIDTH)); 352 return 1; 353 } 354 355 356 357 try 358 { 359 ErrorLogPublisher errorLogPublisher = 360 TextErrorLogPublisher.getStartupTextErrorPublisher( 361 new TextWriter.STREAM(out)); 362 DebugLogPublisher debugLogPublisher = 363 TextDebugLogPublisher.getStartupTextDebugPublisher( 364 new TextWriter.STREAM(out)); 365 ErrorLogger.addErrorLogPublisher(errorLogPublisher); 366 DebugLogger.addDebugLogPublisher(debugLogPublisher); 367 } 368 catch(Exception e) 369 { 370 err.println("Error installing the custom error logger: " + 371 stackTraceToSingleLineString(e)); 372 } 373 } 374 375 // Decode the base DN provided by the user. 376 DN rebuildBaseDN; 377 try 378 { 379 rebuildBaseDN = DN.decode(baseDNString.getValue()); 380 } 381 catch (DirectoryException de) 382 { 383 Message message = ERR_CANNOT_DECODE_BASE_DN.get( 384 baseDNString.getValue(), de.getMessageObject()); 385 logError(message); 386 return 1; 387 } 388 catch (Exception e) 389 { 390 Message message = ERR_CANNOT_DECODE_BASE_DN.get( 391 baseDNString.getValue(), getExceptionMessage(e)); 392 logError(message); 393 return 1; 394 } 395 396 // Get information about the backends defined in the server. 397 Backend backend = null; 398 DN[] baseDNArray; 399 400 ArrayList<Backend> backendList = new ArrayList<Backend>(); 401 ArrayList<BackendCfg> entryList = new ArrayList<BackendCfg>(); 402 ArrayList<List<DN>> dnList = new ArrayList<List<DN>>(); 403 int code = BackendToolUtils.getBackends(backendList, entryList, dnList); 404 405 int numBackends = backendList.size(); 406 for (int i=0; i < numBackends; i++) 407 { 408 Backend b = backendList.get(i); 409 List<DN> baseDNs = dnList.get(i); 410 411 for (DN baseDN : baseDNs) 412 { 413 if (baseDN.equals(rebuildBaseDN)) 414 { 415 if (backend == null) 416 { 417 backend = b; 418 baseDNArray = new DN[baseDNs.size()]; 419 baseDNs.toArray(baseDNArray); 420 } 421 else 422 { 423 Message message = 424 ERR_MULTIPLE_BACKENDS_FOR_BASE.get(baseDNString.getValue()); 425 logError(message); 426 return 1; 427 } 428 break; 429 } 430 } 431 } 432 433 if (backend == null) 434 { 435 Message message = ERR_NO_BACKENDS_FOR_BASE.get(baseDNString.getValue()); 436 logError(message); 437 return 1; 438 } 439 440 if (!(backend instanceof BackendImpl)) 441 { 442 Message message = ERR_BACKEND_NO_INDEXING_SUPPORT.get(); 443 logError(message); 444 return 1; 445 } 446 447 // Initialize the rebuild configuration. 448 RebuildConfig rebuildConfig = new RebuildConfig(); 449 rebuildConfig.setBaseDN(rebuildBaseDN); 450 for (String s : indexList.getValues()) 451 { 452 rebuildConfig.addRebuildIndex(s); 453 } 454 455 // Acquire an exclusive lock for the backend. 456 //TODO: Find a way to do this with the server online. 457 try 458 { 459 String lockFile = LockFileManager.getBackendLockFileName(backend); 460 StringBuilder failureReason = new StringBuilder(); 461 if (! LockFileManager.acquireExclusiveLock(lockFile, failureReason)) 462 { 463 Message message = ERR_REBUILDINDEX_CANNOT_EXCLUSIVE_LOCK_BACKEND.get( 464 backend.getBackendID(), String.valueOf(failureReason)); 465 logError(message); 466 return 1; 467 } 468 } 469 catch (Exception e) 470 { 471 Message message = ERR_REBUILDINDEX_CANNOT_EXCLUSIVE_LOCK_BACKEND.get( 472 backend.getBackendID(), getExceptionMessage(e)); 473 logError(message); 474 return 1; 475 } 476 477 // Launch the rebuild process. 478 int returnCode = 0; 479 try 480 { 481 BackendImpl jebBackend = (BackendImpl)backend; 482 jebBackend.rebuildBackend(rebuildConfig); 483 } 484 catch (Exception e) 485 { 486 Message message = 487 ERR_REBUILDINDEX_ERROR_DURING_REBUILD.get(getExceptionMessage(e)); 488 logError(message); 489 returnCode = 1; 490 } 491 finally 492 { 493 // Release the shared lock on the backend. 494 try 495 { 496 String lockFile = LockFileManager.getBackendLockFileName(backend); 497 StringBuilder failureReason = new StringBuilder(); 498 if (! LockFileManager.releaseLock(lockFile, failureReason)) 499 { 500 Message message = WARN_REBUILDINDEX_CANNOT_UNLOCK_BACKEND.get( 501 backend.getBackendID(), String.valueOf(failureReason)); 502 logError(message); 503 } 504 } 505 catch (Exception e) 506 { 507 Message message = WARN_REBUILDINDEX_CANNOT_UNLOCK_BACKEND.get( 508 backend.getBackendID(), getExceptionMessage(e)); 509 logError(message); 510 } 511 } 512 513 return returnCode; 514 } 515 }