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    }