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 }