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 configFile; 153 StringArgument templatePath; 154 StringArgument ldifFile; 155 StringArgument resourcePath; 156 157 try 158 { 159 configFile = 160 StringArgument.builder("configFile") 161 .shortIdentifier('c') 162 .description(INFO_DESCRIPTION_CONFIG_FILE.get()) 163 .hidden() 164 .required() 165 .valuePlaceholder(INFO_CONFIGFILE_PLACEHOLDER.get()) 166 .buildAndAddToParser(argParser); 167 resourcePath = 168 StringArgument.builder("resourcePath") 169 .shortIdentifier('r') 170 .description(INFO_MAKELDIF_DESCRIPTION_RESOURCE_PATH.get()) 171 .hidden() 172 .required() 173 .valuePlaceholder(INFO_PATH_PLACEHOLDER.get()) 174 .buildAndAddToParser(argParser); 175 templatePath = 176 StringArgument.builder("templateFile") 177 .shortIdentifier('t') 178 .description(INFO_MAKELDIF_DESCRIPTION_TEMPLATE.get()) 179 .required() 180 .valuePlaceholder(INFO_FILE_PLACEHOLDER.get()) 181 .buildAndAddToParser(argParser); 182 ldifFile = 183 StringArgument.builder("ldifFile") 184 .shortIdentifier('o') 185 .description(INFO_MAKELDIF_DESCRIPTION_LDIF.get()) 186 .required() 187 .valuePlaceholder(INFO_FILE_PLACEHOLDER.get()) 188 .buildAndAddToParser(argParser); 189 randomSeed = 190 IntegerArgument.builder(OPTION_LONG_RANDOM_SEED) 191 .shortIdentifier(OPTION_SHORT_RANDOM_SEED) 192 .description(INFO_MAKELDIF_DESCRIPTION_SEED.get()) 193 .defaultValue(0) 194 .valuePlaceholder(INFO_SEED_PLACEHOLDER.get()) 195 .buildAndAddToParser(argParser); 196 197 showUsage = showUsageArgument(); 198 argParser.addArgument(showUsage); 199 argParser.setUsageArgument(showUsage); 200 } 201 catch (ArgumentException ae) 202 { 203 printWrappedText(err, ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage())); 204 return 1; 205 } 206 207 208 // Parse the command-line arguments provided to the program. 209 try 210 { 211 argParser.parseArguments(args); 212 } 213 catch (ArgumentException ae) 214 { 215 argParser.displayMessageAndUsageReference(err, ERR_ERROR_PARSING_ARGS.get(ae.getMessage())); 216 return 1; 217 } 218 219 220 // If we should just display usage or version information, 221 // then print it and exit. 222 if (argParser.usageOrVersionDisplayed()) 223 { 224 return 0; 225 } 226 227 // Checks the version - if upgrade required, the tool is unusable 228 try 229 { 230 BuildVersion.checkVersionMismatch(); 231 } 232 catch (InitializationException e) 233 { 234 printWrappedText(err, e.getMessage()); 235 return 1; 236 } 237 238 if (initializeServer) 239 { 240 // Initialize the Directory Server configuration handler using the 241 // information that was provided. 242 DirectoryServer directoryServer = DirectoryServer.getInstance(); 243 DirectoryServer.bootstrapClient(); 244 245 try 246 { 247 DirectoryServer.initializeJMX(); 248 } 249 catch (Exception e) 250 { 251 printWrappedText(err, ERR_MAKELDIF_CANNOT_INITIALIZE_JMX.get(configFile.getValue(), e.getMessage())); 252 return 1; 253 } 254 255 try 256 { 257 directoryServer.initializeConfiguration(configFile.getValue()); 258 } 259 catch (Exception e) 260 { 261 printWrappedText(err, ERR_MAKELDIF_CANNOT_INITIALIZE_CONFIG.get(configFile.getValue(), e.getMessage())); 262 return 1; 263 } 264 } 265 266 if (initializeSchema) 267 { 268 try 269 { 270 DirectoryServer.getInstance().initializeSchema(); 271 } 272 catch (Exception e) 273 { 274 printWrappedText(err, ERR_MAKELDIF_CANNOT_INITIALIZE_SCHEMA.get(configFile.getValue(), e.getMessage())); 275 return 1; 276 } 277 } 278 279 280 // Create the random number generator that will be used for the generation 281 // process. 282 Random random; 283 if (randomSeed.isPresent()) 284 { 285 try 286 { 287 random = new Random(randomSeed.getIntValue()); 288 } 289 catch (Exception e) 290 { 291 random = new Random(); 292 } 293 } 294 else 295 { 296 random = new Random(); 297 } 298 299 300 // If a resource path was provided, then make sure it's acceptable. 301 File resourceDir = new File(resourcePath.getValue()); 302 if (! resourceDir.exists()) 303 { 304 printWrappedText(err, ERR_MAKELDIF_NO_SUCH_RESOURCE_DIRECTORY.get(resourcePath.getValue())); 305 return 1; 306 } 307 308 309 // Load and parse the template file. 310 LinkedList<LocalizableMessage> warnings = new LinkedList<>(); 311 TemplateFile templateFile = new TemplateFile(resourcePath.getValue(), random); 312 try 313 { 314 templateFile.parse(templatePath.getValue(), warnings); 315 } 316 catch (IOException ioe) 317 { 318 printWrappedText(err, ERR_MAKELDIF_IOEXCEPTION_DURING_PARSE.get(ioe.getMessage())); 319 return 1; 320 } 321 catch (Exception e) 322 { 323 printWrappedText(err, ERR_MAKELDIF_EXCEPTION_DURING_PARSE.get(e.getMessage())); 324 return 1; 325 } 326 327 328 // If there were any warnings, then print them. 329 if (! warnings.isEmpty()) 330 { 331 for (LocalizableMessage s : warnings) 332 { 333 printWrappedText(err, s); 334 } 335 } 336 337 338 // Create the LDIF writer that will be used to actually write the LDIF. 339 LDIFExportConfig exportConfig = 340 new LDIFExportConfig(ldifFile.getValue(), 341 ExistingFileBehavior.OVERWRITE); 342 try 343 { 344 ldifWriter = new LDIFWriter(exportConfig); 345 } 346 catch (IOException ioe) 347 { 348 printWrappedText(err, ERR_MAKELDIF_UNABLE_TO_CREATE_LDIF.get(ldifFile.getValue(), ioe)); 349 return 1; 350 } 351 352 353 // Generate the LDIF content. 354 try 355 { 356 templateFile.generateLDIF(this); 357 } 358 catch (Exception e) 359 { 360 printWrappedText(err, ERR_MAKELDIF_ERROR_WRITING_LDIF.get(ldifFile.getValue(), stackTraceToSingleLineString(e))); 361 return 1; 362 } 363 finally 364 { 365 close(ldifWriter); 366 } 367 368 369 // If we've gotten here, then everything was successful. 370 return 0; 371 } 372 373 374 /** 375 * Processes the provided set of command-line arguments and begins generating 376 * the LDIF content. 377 * 378 * @param args The command-line arguments provided for this program. 379 * 380 * @return A result code of zero if all processing completed properly, or 381 * a nonzero result if a problem occurred. 382 */ 383 public int makeLDIFMain(String[] args) 384 { 385 return makeLDIFMain(args, true, true, System.out, System.err); 386 } 387 388 389 390 /** 391 * Writes the provided entry to the appropriate target. 392 * 393 * @param entry The entry to be written. 394 * 395 * @return <CODE>true</CODE> if the entry writer will accept more entries, or 396 * <CODE>false</CODE> if not. 397 * 398 * @throws IOException If a problem occurs while writing the entry to its 399 * intended destination. 400 * 401 * @throws MakeLDIFException If some other problem occurs. 402 */ 403 @Override 404 public boolean writeEntry(TemplateEntry entry) 405 throws IOException, MakeLDIFException 406 { 407 try 408 { 409 if (entry.getDN() != null) 410 { 411 ldifWriter.writeTemplateEntry(entry); 412 413 if ((++entriesWritten % 1000) == 0) 414 { 415 printWrappedText(out, INFO_MAKELDIF_PROCESSED_N_ENTRIES.get(entriesWritten)); 416 } 417 } 418 else 419 { 420 AttributeType[] rdnAttrs = entry.getTemplate().getRDNAttributes(); 421 String nullRdn = ""; 422 for (AttributeType att : rdnAttrs) 423 { 424 if (entry.getValue(att) == null) 425 { 426 nullRdn = att.getNameOrOID(); 427 break ; 428 } 429 } 430 printWrappedText(err, ERR_MAKELDIF_CANNOT_WRITE_ENTRY_WITHOUT_DN.get(nullRdn)); 431 return true; 432 } 433 434 return true; 435 } 436 catch (IOException ioe) 437 { 438 throw ioe; 439 } 440 catch (Exception e) 441 { 442 LocalizableMessage message = ERR_MAKELDIF_CANNOT_WRITE_ENTRY.get( 443 entry.getDN(), stackTraceToSingleLineString(e)); 444 throw new MakeLDIFException(message, e); 445 } 446 } 447 448 449 450 /** 451 * Notifies the entry writer that no more entries will be provided and that 452 * any associated cleanup may be performed. 453 */ 454 @Override 455 public void closeEntryWriter() 456 { 457 printWrappedText(out, INFO_MAKELDIF_PROCESSING_COMPLETE.get(entriesWritten)); 458 } 459} 460