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
030
031 import org.opends.server.api.Backend;
032 import org.opends.server.api.ErrorLogPublisher;
033 import org.opends.server.api.DebugLogPublisher;
034 import org.opends.server.backends.jeb.BackendImpl;
035 import org.opends.server.backends.jeb.VerifyConfig;
036 import org.opends.server.config.ConfigException;
037 import org.opends.server.core.CoreConfigManager;
038 import org.opends.server.core.DirectoryServer;
039 import org.opends.server.core.LockFileManager;
040 import org.opends.server.extensions.ConfigFileHandler;
041 import org.opends.server.loggers.TextWriter;
042 import org.opends.server.loggers.ErrorLogger;
043 import org.opends.server.loggers.TextErrorLogPublisher;
044 import org.opends.server.loggers.debug.TextDebugLogPublisher;
045 import org.opends.server.loggers.debug.DebugLogger;
046 import org.opends.server.types.DirectoryException;
047 import org.opends.server.types.DN;
048 import org.opends.server.types.InitializationException;
049 import org.opends.server.types.NullOutputStream;
050 import org.opends.server.util.args.ArgumentException;
051 import org.opends.server.util.args.ArgumentParser;
052 import org.opends.server.util.args.BooleanArgument;
053 import org.opends.server.util.args.StringArgument;
054
055 import java.io.OutputStream;
056 import java.io.PrintStream;
057 import java.util.ArrayList;
058 import java.util.List;
059
060 import static org.opends.server.loggers.ErrorLogger.*;
061 import static org.opends.messages.ToolMessages.*;
062 import org.opends.messages.Message;
063
064 import static org.opends.server.util.ServerConstants.*;
065 import static org.opends.server.util.StaticUtils.*;
066 import static org.opends.server.tools.ToolConstants.*;
067 import org.opends.server.admin.std.server.BackendCfg;
068
069
070 /**
071 * This program provides a utility to verify the contents of the indexes
072 * of a Directory Server backend. This will be a process that is
073 * intended to run separate from Directory Server and not internally within the
074 * server process (e.g., via the tasks interface).
075 */
076 public class VerifyIndex
077 {
078 /**
079 * Processes the command-line arguments and invokes the verify process.
080 *
081 * @param args The command-line arguments provided to this program.
082 */
083 public static void main(String[] args)
084 {
085 int retCode = mainVerifyIndex(args, true, System.out, System.err);
086
087 if(retCode != 0)
088 {
089 System.exit(filterExitCode(retCode));
090 }
091 }
092
093 /**
094 * Processes the command-line arguments and invokes the verify process.
095 *
096 * @param args The command-line arguments provided to this
097 * program.
098 * @param initializeServer Indicates whether to initialize the server.
099 * @param outStream The output stream to use for standard output, or
100 * {@code null} if standard output is not needed.
101 * @param errStream The output stream to use for standard error, or
102 * {@code null} if standard error is not needed.
103 *
104 * @return The error code.
105 */
106 public static int mainVerifyIndex(String[] args, boolean initializeServer,
107 OutputStream outStream,
108 OutputStream errStream)
109 {
110 PrintStream out;
111 if (outStream == null)
112 {
113 out = NullOutputStream.printStream();
114 }
115 else
116 {
117 out = new PrintStream(outStream);
118 }
119
120 PrintStream err;
121 if (errStream == null)
122 {
123 err = NullOutputStream.printStream();
124 }
125 else
126 {
127 err = new PrintStream(errStream);
128 }
129
130 // Define the command-line arguments that may be used with this program.
131 StringArgument configClass = null;
132 StringArgument configFile = null;
133 StringArgument baseDNString = null;
134 StringArgument indexList = null;
135 BooleanArgument cleanMode = null;
136 BooleanArgument countErrors = null;
137 BooleanArgument displayUsage = null;
138
139
140 // Create the command-line argument parser for use with this program.
141 Message toolDescription = INFO_VERIFYINDEX_TOOL_DESCRIPTION.get();
142 ArgumentParser argParser =
143 new ArgumentParser("org.opends.server.tools.VerifyIndex",
144 toolDescription, false);
145
146
147 // Initialize all the command-line argument types and register them with the
148 // parser.
149 try
150 {
151 configClass =
152 new StringArgument("configclass", OPTION_SHORT_CONFIG_CLASS,
153 OPTION_LONG_CONFIG_CLASS, true, false,
154 true, INFO_CONFIGCLASS_PLACEHOLDER.get(),
155 ConfigFileHandler.class.getName(), null,
156 INFO_DESCRIPTION_CONFIG_CLASS.get());
157 configClass.setHidden(true);
158 argParser.addArgument(configClass);
159
160
161 configFile =
162 new StringArgument("configfile", 'f', "configFile", true, false,
163 true, INFO_CONFIGFILE_PLACEHOLDER.get(), null,
164 null,
165 INFO_DESCRIPTION_CONFIG_FILE.get());
166 configFile.setHidden(true);
167 argParser.addArgument(configFile);
168
169
170 baseDNString =
171 new StringArgument("basedn", OPTION_SHORT_BASEDN,
172 OPTION_LONG_BASEDN, true, false, true,
173 INFO_BASEDN_PLACEHOLDER.get(), null, null,
174 INFO_VERIFYINDEX_DESCRIPTION_BASE_DN.get());
175 argParser.addArgument(baseDNString);
176
177
178 indexList =
179 new StringArgument("index", 'i', "index",
180 false, true, true,
181 INFO_INDEX_PLACEHOLDER.get(), null, null,
182 INFO_VERIFYINDEX_DESCRIPTION_INDEX_NAME.get());
183 argParser.addArgument(indexList);
184
185 cleanMode =
186 new BooleanArgument("clean", 'c', "clean",
187 INFO_VERIFYINDEX_DESCRIPTION_VERIFY_CLEAN.get());
188 argParser.addArgument(cleanMode);
189
190 countErrors =
191 new BooleanArgument("counterrors", null, "countErrors",
192 INFO_VERIFYINDEX_DESCRIPTION_COUNT_ERRORS.get());
193 argParser.addArgument(countErrors);
194
195 displayUsage =
196 new BooleanArgument("help", OPTION_SHORT_HELP, OPTION_LONG_HELP,
197 INFO_DESCRIPTION_USAGE.get());
198 argParser.addArgument(displayUsage);
199 argParser.setUsageArgument(displayUsage);
200 }
201 catch (ArgumentException ae)
202 {
203
204 Message message = ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage());
205
206 err.println(wrapText(message, MAX_LINE_WIDTH));
207 return 1;
208 }
209
210
211 // Parse the command-line arguments provided to this program.
212 try
213 {
214 argParser.parseArguments(args);
215 }
216 catch (ArgumentException ae)
217 {
218
219 Message message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
220
221 err.println(wrapText(message, MAX_LINE_WIDTH));
222 err.println(argParser.getUsage());
223 return 1;
224 }
225
226
227 // If we should just display usage or version information,
228 // then print it and exit.
229 if (argParser.usageOrVersionDisplayed())
230 {
231 return 0;
232 }
233
234
235
236
237 // If no arguments were provided, then display usage information and exit.
238 int numArgs = args.length;
239 if (numArgs == 0)
240 {
241 out.println(argParser.getUsage());
242 return 1;
243 }
244
245
246 if (cleanMode.isPresent() && indexList.getValues().size() != 1)
247 {
248 Message message =
249 ERR_VERIFYINDEX_VERIFY_CLEAN_REQUIRES_SINGLE_INDEX.get();
250
251 err.println(wrapText(message, MAX_LINE_WIDTH));
252 out.println(argParser.getUsage());
253 return 1;
254 }
255
256 // Perform the initial bootstrap of the Directory Server and process the
257 // configuration.
258 DirectoryServer directoryServer = DirectoryServer.getInstance();
259
260 if (initializeServer)
261 {
262 try
263 {
264 DirectoryServer.bootstrapClient();
265 DirectoryServer.initializeJMX();
266 }
267 catch (Exception e)
268 {
269 Message message =
270 ERR_SERVER_BOOTSTRAP_ERROR.get(getExceptionMessage(e));
271 err.println(wrapText(message, MAX_LINE_WIDTH));
272 return 1;
273 }
274
275 try
276 {
277 directoryServer.initializeConfiguration(configClass.getValue(),
278 configFile.getValue());
279 }
280 catch (InitializationException ie)
281 {
282 Message message =
283 ERR_CANNOT_LOAD_CONFIG.get(ie.getMessage());
284 err.println(wrapText(message, MAX_LINE_WIDTH));
285 return 1;
286 }
287 catch (Exception e)
288 {
289 Message message = ERR_CANNOT_LOAD_CONFIG.get(getExceptionMessage(e));
290 err.println(wrapText(message, MAX_LINE_WIDTH));
291 return 1;
292 }
293
294
295
296 // Initialize the Directory Server schema elements.
297 try
298 {
299 directoryServer.initializeSchema();
300 }
301 catch (ConfigException ce)
302 {
303 Message message = ERR_CANNOT_LOAD_SCHEMA.get(ce.getMessage());
304 err.println(wrapText(message, MAX_LINE_WIDTH));
305 return 1;
306 }
307 catch (InitializationException ie)
308 {
309 Message message = ERR_CANNOT_LOAD_SCHEMA.get(ie.getMessage());
310 err.println(wrapText(message, MAX_LINE_WIDTH));
311 return 1;
312 }
313 catch (Exception e)
314 {
315 Message message = ERR_CANNOT_LOAD_SCHEMA.get(getExceptionMessage(e));
316 err.println(wrapText(message, MAX_LINE_WIDTH));
317 return 1;
318 }
319
320
321 // Initialize the Directory Server core configuration.
322 try
323 {
324 CoreConfigManager coreConfigManager = new CoreConfigManager();
325 coreConfigManager.initializeCoreConfig();
326 }
327 catch (ConfigException ce)
328 {
329 Message message =
330 ERR_CANNOT_INITIALIZE_CORE_CONFIG.get(ce.getMessage());
331 err.println(wrapText(message, MAX_LINE_WIDTH));
332 return 1;
333 }
334 catch (InitializationException ie)
335 {
336 Message message =
337 ERR_CANNOT_INITIALIZE_CORE_CONFIG.get(ie.getMessage());
338 err.println(wrapText(message, MAX_LINE_WIDTH));
339 return 1;
340 }
341 catch (Exception e)
342 {
343 Message message =
344 ERR_CANNOT_INITIALIZE_CORE_CONFIG.get(getExceptionMessage(e));
345 err.println(wrapText(message, MAX_LINE_WIDTH));
346 return 1;
347 }
348
349
350 // Initialize the Directory Server crypto manager.
351 try
352 {
353 directoryServer.initializeCryptoManager();
354 }
355 catch (ConfigException ce)
356 {
357 Message message =
358 ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get(ce.getMessage());
359 err.println(wrapText(message, MAX_LINE_WIDTH));
360 return 1;
361 }
362 catch (InitializationException ie)
363 {
364 Message message =
365 ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get(ie.getMessage());
366 err.println(wrapText(message, MAX_LINE_WIDTH));
367 return 1;
368 }
369 catch (Exception e)
370 {
371 Message message =
372 ERR_CANNOT_INITIALIZE_CRYPTO_MANAGER.get(
373 getExceptionMessage(e));
374 err.println(wrapText(message, MAX_LINE_WIDTH));
375 return 1;
376 }
377
378
379 try
380 {
381 ErrorLogPublisher errorLogPublisher =
382 TextErrorLogPublisher.getStartupTextErrorPublisher(
383 new TextWriter.STREAM(out));
384 DebugLogPublisher debugLogPublisher =
385 TextDebugLogPublisher.getStartupTextDebugPublisher(
386 new TextWriter.STREAM(out));
387 ErrorLogger.addErrorLogPublisher(errorLogPublisher);
388 DebugLogger.addDebugLogPublisher(debugLogPublisher);
389 }
390 catch(Exception e)
391 {
392 err.println("Error installing the custom error logger: " +
393 stackTraceToSingleLineString(e));
394 }
395 }
396
397
398 // Decode the base DN provided by the user.
399 DN verifyBaseDN ;
400 try
401 {
402 verifyBaseDN = DN.decode(baseDNString.getValue());
403 }
404 catch (DirectoryException de)
405 {
406 Message message = ERR_CANNOT_DECODE_BASE_DN.get(
407 baseDNString.getValue(), de.getMessageObject());
408 logError(message);
409 return 1;
410 }
411 catch (Exception e)
412 {
413 Message message = ERR_CANNOT_DECODE_BASE_DN.get(
414 baseDNString.getValue(), getExceptionMessage(e));
415 logError(message);
416 return 1;
417 }
418
419
420 // Get information about the backends defined in the server. Iterate
421 // through them, finding the one backend to be verified.
422 Backend backend = null;
423
424 ArrayList<Backend> backendList = new ArrayList<Backend>();
425 ArrayList<BackendCfg> entryList = new ArrayList<BackendCfg>();
426 ArrayList<List<DN>> dnList = new ArrayList<List<DN>>();
427 BackendToolUtils.getBackends(backendList, entryList, dnList);
428
429 int numBackends = backendList.size();
430 for (int i=0; i < numBackends; i++)
431 {
432 Backend b = backendList.get(i);
433 List<DN> baseDNs = dnList.get(i);
434
435 for (DN baseDN : baseDNs)
436 {
437 if (baseDN.equals(verifyBaseDN))
438 {
439 if (backend == null)
440 {
441 backend = b;
442 }
443 else
444 {
445 Message message =
446 ERR_MULTIPLE_BACKENDS_FOR_BASE.get(baseDNString.getValue());
447 logError(message);
448 return 1;
449 }
450 break;
451 }
452 }
453 }
454
455 if (backend == null)
456 {
457 Message message = ERR_NO_BACKENDS_FOR_BASE.get(baseDNString.getValue());
458 logError(message);
459 return 1;
460 }
461
462 if (!(backend instanceof BackendImpl))
463 {
464 Message message = ERR_BACKEND_NO_INDEXING_SUPPORT.get();
465 logError(message);
466 return 1;
467 }
468
469 // Initialize the verify configuration.
470 VerifyConfig verifyConfig = new VerifyConfig();
471 verifyConfig.setBaseDN(verifyBaseDN);
472 if (cleanMode.isPresent())
473 {
474 for (String s : indexList.getValues())
475 {
476 verifyConfig.addCleanIndex(s);
477 }
478 }
479 else
480 {
481 for (String s : indexList.getValues())
482 {
483 verifyConfig.addCompleteIndex(s);
484 }
485 }
486
487
488 // Acquire a shared lock for the backend.
489 try
490 {
491 String lockFile = LockFileManager.getBackendLockFileName(backend);
492 StringBuilder failureReason = new StringBuilder();
493 if (! LockFileManager.acquireSharedLock(lockFile, failureReason))
494 {
495 Message message = ERR_VERIFYINDEX_CANNOT_LOCK_BACKEND.get(
496 backend.getBackendID(), String.valueOf(failureReason));
497 logError(message);
498 return 1;
499 }
500 }
501 catch (Exception e)
502 {
503 Message message = ERR_VERIFYINDEX_CANNOT_LOCK_BACKEND.get(
504 backend.getBackendID(), getExceptionMessage(e));
505 logError(message);
506 return 1;
507 }
508
509
510 // Launch the verify process.
511 int returnCode = 0 ;
512 try
513 {
514 BackendImpl jebBackend = (BackendImpl)backend;
515 long errorCount = jebBackend.verifyBackend(verifyConfig, null);
516 if (countErrors.isPresent())
517 {
518 if (errorCount > Integer.MAX_VALUE)
519 {
520 returnCode = Integer.MAX_VALUE;
521 }
522 else
523 {
524 returnCode = (int) errorCount;
525 }
526 }
527 }
528 catch (Exception e)
529 {
530 Message message = ERR_VERIFYINDEX_ERROR_DURING_VERIFY.get(
531 stackTraceToSingleLineString(e));
532 logError(message);
533 returnCode = 1;
534 }
535
536
537 // Release the shared lock on the backend.
538 try
539 {
540 String lockFile = LockFileManager.getBackendLockFileName(backend);
541 StringBuilder failureReason = new StringBuilder();
542 if (! LockFileManager.releaseLock(lockFile, failureReason))
543 {
544 Message message = WARN_VERIFYINDEX_CANNOT_UNLOCK_BACKEND.get(
545 backend.getBackendID(), String.valueOf(failureReason));
546 logError(message);
547 }
548 }
549 catch (Exception e)
550 {
551 Message message = WARN_VERIFYINDEX_CANNOT_UNLOCK_BACKEND.get(
552 backend.getBackendID(), getExceptionMessage(e));
553 logError(message);
554 }
555
556 return returnCode;
557 }
558 }