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