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-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2013-2015 ForgeRock AS. 026 */ 027package org.opends.server.tools.makeldif; 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.File; 036import java.io.IOException; 037import java.io.OutputStream; 038import java.io.PrintStream; 039import java.util.LinkedList; 040import java.util.Random; 041 042import org.forgerock.i18n.LocalizableMessage; 043import org.opends.server.core.DirectoryServer; 044import org.opends.server.core.DirectoryServer.DirectoryServerVersionHandler; 045import org.opends.server.loggers.JDKLogging; 046import org.opends.server.types.AttributeType; 047import org.opends.server.types.ExistingFileBehavior; 048import org.opends.server.types.InitializationException; 049import org.opends.server.types.LDIFExportConfig; 050import org.opends.server.types.NullOutputStream; 051import org.opends.server.util.BuildVersion; 052import org.opends.server.util.LDIFWriter; 053 054import com.forgerock.opendj.cli.ArgumentException; 055import com.forgerock.opendj.cli.ArgumentParser; 056import com.forgerock.opendj.cli.BooleanArgument; 057import com.forgerock.opendj.cli.CommonArguments; 058import com.forgerock.opendj.cli.IntegerArgument; 059import com.forgerock.opendj.cli.StringArgument; 060 061/** 062 * This class defines a program that can be used to generate LDIF content based 063 * on a template. 064 */ 065public class MakeLDIF 066 implements EntryWriter 067{ 068 /** 069 * The fully-qualified name of this class. 070 */ 071 private static final String CLASS_NAME = 072 "org.opends.server.tools.makeldif.MakeLDIF"; 073 074 /** The LDIF writer that will be used to write the entries. */ 075 private LDIFWriter ldifWriter; 076 077 /** The total number of entries that have been written. */ 078 private long entriesWritten; 079 080 private PrintStream out = System.out; 081 private PrintStream err = System.err; 082 083 /** 084 * Invokes the <CODE>makeLDIFMain</CODE> method with the provided set of 085 * arguments. 086 * 087 * @param args The command-line arguments provided for this program. 088 */ 089 public static void main(String[] args) 090 { 091 MakeLDIF makeLDIF = new MakeLDIF(); 092 int returnCode = makeLDIF.makeLDIFMain(args); 093 if (returnCode != 0) 094 { 095 System.exit(filterExitCode(returnCode)); 096 } 097 } 098 099 /** 100 * Provides the command-line arguments to the main application for 101 * processing and returns the exit code as an integer. 102 * 103 * @param args 104 * The set of command-line arguments provided to this 105 * program. 106 * @param outStream 107 * The output stream for standard output. 108 * @param errStream 109 * The output stream for standard error. 110 * @return Zero to indicate that the program completed successfully, 111 * or non-zero to indicate that an error occurred. 112 */ 113 public static int main(final String[] args, final OutputStream outStream, final OutputStream errStream) 114 { 115 return new MakeLDIF().makeLDIFMain(args, false, false, outStream, errStream); 116 } 117 118 /** 119 * Creates a new instance of this utility. It should just be used for 120 * invoking the <CODE>makeLDIFMain</CODE> method. 121 */ 122 public MakeLDIF() 123 { 124 ldifWriter = null; 125 entriesWritten = 0L; 126 } 127 128 /** 129 * Processes the provided set of command-line arguments and begins generating 130 * the LDIF content. 131 * 132 * @param args The command-line arguments provided for this program. 133 * @param initializeServer Indicates whether to initialize the server. 134 * @param initializeSchema Indicates whether to initialize the schema. 135 * @param outStream The output stream to use for standard output, or 136 * {@code null} if standard output is not needed. 137 * @param errStream The output stream to use for standard error, or 138 * {@code null} if standard error is not needed. 139 * @return A result code of zero if all processing completed properly, or 140 * a nonzero result if a problem occurred. 141 * 142 */ 143 public int makeLDIFMain(String[] args, boolean initializeServer, 144 boolean initializeSchema, 145 OutputStream outStream, 146 OutputStream errStream) 147 { 148 out = NullOutputStream.wrapOrNullStream(outStream); 149 err = NullOutputStream.wrapOrNullStream(errStream); 150 JDKLogging.disableLogging(); 151 152 153// Create and initialize the argument parser for this program. 154 LocalizableMessage toolDescription = INFO_MAKELDIF_TOOL_DESCRIPTION.get(); 155 ArgumentParser argParser = new ArgumentParser(CLASS_NAME, toolDescription, 156 false); 157 argParser.setShortToolDescription(REF_SHORT_DESC_MAKELDIF.get()); 158 argParser.setVersionHandler(new DirectoryServerVersionHandler()); 159 160 BooleanArgument showUsage; 161 IntegerArgument randomSeed; 162 StringArgument configClass; 163 StringArgument configFile; 164 StringArgument templatePath; 165 StringArgument ldifFile; 166 StringArgument resourcePath; 167 168 try 169 { 170 configFile = new StringArgument("configfile", 'c', "configFile", true, 171 false, true, 172 INFO_CONFIGFILE_PLACEHOLDER.get(), null, 173 null, 174 INFO_DESCRIPTION_CONFIG_FILE.get()); 175 configFile.setHidden(true); 176 argParser.addArgument(configFile); 177 178 179 configClass = new StringArgument("configclass", OPTION_SHORT_CONFIG_CLASS, 180 OPTION_LONG_CONFIG_CLASS, false, 181 false, true, 182 INFO_CONFIGCLASS_PLACEHOLDER.get(), null, 183 null, 184 INFO_DESCRIPTION_CONFIG_CLASS.get()); 185 configClass.setHidden(true); 186 argParser.addArgument(configClass); 187 188 189 resourcePath = 190 new StringArgument("resourcepath", 'r', "resourcePath", true, false, 191 true, INFO_PATH_PLACEHOLDER.get(), null, null, 192 INFO_MAKELDIF_DESCRIPTION_RESOURCE_PATH.get()); 193 resourcePath.setHidden(true); 194 argParser.addArgument(resourcePath); 195 196 197 templatePath = 198 new StringArgument("templatefile", 't', "templateFile", 199 true, false, true, INFO_FILE_PLACEHOLDER.get(), 200 null, null, 201 INFO_MAKELDIF_DESCRIPTION_TEMPLATE.get()); 202 argParser.addArgument(templatePath); 203 204 205 ldifFile = new StringArgument("ldiffile", 'o', "ldifFile", true, false, 206 true, INFO_FILE_PLACEHOLDER.get(), null, 207 null, INFO_MAKELDIF_DESCRIPTION_LDIF.get()); 208 argParser.addArgument(ldifFile); 209 210 211 randomSeed = new IntegerArgument("randomseed", OPTION_SHORT_RANDOM_SEED, 212 OPTION_LONG_RANDOM_SEED, false, 213 false, true, INFO_SEED_PLACEHOLDER.get(), 214 0, null, 215 INFO_MAKELDIF_DESCRIPTION_SEED.get()); 216 argParser.addArgument(randomSeed); 217 218 219 showUsage = CommonArguments.getShowUsage(); 220 argParser.addArgument(showUsage); 221 argParser.setUsageArgument(showUsage); 222 } 223 catch (ArgumentException ae) 224 { 225 printWrappedText(err, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage())); 226 return 1; 227 } 228 229 230 // Parse the command-line arguments provided to the program. 231 try 232 { 233 argParser.parseArguments(args); 234 } 235 catch (ArgumentException ae) 236 { 237 argParser.displayMessageAndUsageReference(err, ERR_ERROR_PARSING_ARGS.get(ae.getMessage())); 238 return 1; 239 } 240 241 242 // If we should just display usage or version information, 243 // then print it and exit. 244 if (argParser.usageOrVersionDisplayed()) 245 { 246 return 0; 247 } 248 249 // Checks the version - if upgrade required, the tool is unusable 250 try 251 { 252 BuildVersion.checkVersionMismatch(); 253 } 254 catch (InitializationException e) 255 { 256 printWrappedText(err, e.getMessage()); 257 return 1; 258 } 259 260 if (initializeServer) 261 { 262 // Initialize the Directory Server configuration handler using the 263 // information that was provided. 264 DirectoryServer directoryServer = DirectoryServer.getInstance(); 265 DirectoryServer.bootstrapClient(); 266 267 try 268 { 269 DirectoryServer.initializeJMX(); 270 } 271 catch (Exception e) 272 { 273 printWrappedText(err, ERR_MAKELDIF_CANNOT_INITIALIZE_JMX.get(configFile.getValue(), e.getMessage())); 274 return 1; 275 } 276 277 try 278 { 279 directoryServer.initializeConfiguration(configClass.getValue(), 280 configFile.getValue()); 281 } 282 catch (Exception e) 283 { 284 printWrappedText(err, ERR_MAKELDIF_CANNOT_INITIALIZE_CONFIG.get(configFile.getValue(), e.getMessage())); 285 return 1; 286 } 287 } 288 289 if (initializeSchema) 290 { 291 try 292 { 293 DirectoryServer.getInstance().initializeSchema(); 294 } 295 catch (Exception e) 296 { 297 printWrappedText(err, ERR_MAKELDIF_CANNOT_INITIALIZE_SCHEMA.get(configFile.getValue(), e.getMessage())); 298 return 1; 299 } 300 } 301 302 303 // Create the random number generator that will be used for the generation 304 // process. 305 Random random; 306 if (randomSeed.isPresent()) 307 { 308 try 309 { 310 random = new Random(randomSeed.getIntValue()); 311 } 312 catch (Exception e) 313 { 314 random = new Random(); 315 } 316 } 317 else 318 { 319 random = new Random(); 320 } 321 322 323 // If a resource path was provided, then make sure it's acceptable. 324 File resourceDir = new File(resourcePath.getValue()); 325 if (! resourceDir.exists()) 326 { 327 printWrappedText(err, ERR_MAKELDIF_NO_SUCH_RESOURCE_DIRECTORY.get(resourcePath.getValue())); 328 return 1; 329 } 330 331 332 // Load and parse the template file. 333 LinkedList<LocalizableMessage> warnings = new LinkedList<>(); 334 TemplateFile templateFile = new TemplateFile(resourcePath.getValue(), random); 335 try 336 { 337 templateFile.parse(templatePath.getValue(), warnings); 338 } 339 catch (IOException ioe) 340 { 341 printWrappedText(err, ERR_MAKELDIF_IOEXCEPTION_DURING_PARSE.get(ioe.getMessage())); 342 return 1; 343 } 344 catch (Exception e) 345 { 346 printWrappedText(err, ERR_MAKELDIF_EXCEPTION_DURING_PARSE.get(e.getMessage())); 347 return 1; 348 } 349 350 351 // If there were any warnings, then print them. 352 if (! warnings.isEmpty()) 353 { 354 for (LocalizableMessage s : warnings) 355 { 356 printWrappedText(err, s); 357 } 358 } 359 360 361 // Create the LDIF writer that will be used to actually write the LDIF. 362 LDIFExportConfig exportConfig = 363 new LDIFExportConfig(ldifFile.getValue(), 364 ExistingFileBehavior.OVERWRITE); 365 try 366 { 367 ldifWriter = new LDIFWriter(exportConfig); 368 } 369 catch (IOException ioe) 370 { 371 printWrappedText(err, ERR_MAKELDIF_UNABLE_TO_CREATE_LDIF.get(ldifFile.getValue(), ioe)); 372 return 1; 373 } 374 375 376 // Generate the LDIF content. 377 try 378 { 379 templateFile.generateLDIF(this); 380 } 381 catch (Exception e) 382 { 383 printWrappedText(err, ERR_MAKELDIF_ERROR_WRITING_LDIF.get(ldifFile.getValue(), stackTraceToSingleLineString(e))); 384 return 1; 385 } 386 finally 387 { 388 close(ldifWriter); 389 } 390 391 392 // If we've gotten here, then everything was successful. 393 return 0; 394 } 395 396 397 /** 398 * Processes the provided set of command-line arguments and begins generating 399 * the LDIF content. 400 * 401 * @param args The command-line arguments provided for this program. 402 * 403 * @return A result code of zero if all processing completed properly, or 404 * a nonzero result if a problem occurred. 405 */ 406 public int makeLDIFMain(String[] args) 407 { 408 return makeLDIFMain(args, true, true, System.out, System.err); 409 } 410 411 412 413 /** 414 * Writes the provided entry to the appropriate target. 415 * 416 * @param entry The entry to be written. 417 * 418 * @return <CODE>true</CODE> if the entry writer will accept more entries, or 419 * <CODE>false</CODE> if not. 420 * 421 * @throws IOException If a problem occurs while writing the entry to its 422 * intended destination. 423 * 424 * @throws MakeLDIFException If some other problem occurs. 425 */ 426 @Override 427 public boolean writeEntry(TemplateEntry entry) 428 throws IOException, MakeLDIFException 429 { 430 try 431 { 432 if (entry.getDN() != null) 433 { 434 ldifWriter.writeTemplateEntry(entry); 435 436 if ((++entriesWritten % 1000) == 0) 437 { 438 printWrappedText(out, INFO_MAKELDIF_PROCESSED_N_ENTRIES.get(entriesWritten)); 439 } 440 } 441 else 442 { 443 AttributeType[] rdnAttrs = entry.getTemplate().getRDNAttributes(); 444 String nullRdn = ""; 445 for (AttributeType att : rdnAttrs) 446 { 447 if (entry.getValue(att) == null) 448 { 449 nullRdn = att.getNameOrOID(); 450 break ; 451 } 452 } 453 printWrappedText(err, ERR_MAKELDIF_CANNOT_WRITE_ENTRY_WITHOUT_DN.get(nullRdn)); 454 return true; 455 } 456 457 return true; 458 } 459 catch (IOException ioe) 460 { 461 throw ioe; 462 } 463 catch (Exception e) 464 { 465 LocalizableMessage message = ERR_MAKELDIF_CANNOT_WRITE_ENTRY.get( 466 entry.getDN(), stackTraceToSingleLineString(e)); 467 throw new MakeLDIFException(message, e); 468 } 469 } 470 471 472 473 /** 474 * Notifies the entry writer that no more entries will be provided and that 475 * any associated cleanup may be performed. 476 */ 477 @Override 478 public void closeEntryWriter() 479 { 480 printWrappedText(out, INFO_MAKELDIF_PROCESSING_COMPLETE.get(entriesWritten)); 481 } 482} 483