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.forgerock.opendj.ldif; 028 029import static com.forgerock.opendj.ldap.CoreMessages.*; 030 031import java.io.IOException; 032import java.io.InputStream; 033import java.util.Collections; 034import java.util.HashMap; 035import java.util.LinkedList; 036import java.util.List; 037import java.util.Map; 038import java.util.NoSuchElementException; 039import java.util.Random; 040 041import org.forgerock.i18n.LocalizableMessage; 042import org.forgerock.opendj.ldap.DecodeException; 043import org.forgerock.opendj.ldap.Entry; 044import org.forgerock.opendj.ldap.schema.Schema; 045 046import org.forgerock.util.Reject; 047 048/** 049 * A template driven entry generator, as used by the make-ldif tool. 050 * <p> 051 * To build a generator with default values, including default template file, 052 * use the empty constructor: 053 * 054 * <pre> 055 * generator = new EntryGenerator(); 056 * </pre> 057 * <p> 058 * To build a generator with some custom values, use the non-empty constructor 059 * and the <code>set</code> methods: 060 * 061 * <pre> 062 * generator = new EntryGenerator(templatePath).setResourcePath(path).setSchema(schema) 063 * </pre> 064 */ 065public final class EntryGenerator implements EntryReader { 066 067 private static final int DEFAULT_RANDOM_SEED = 1; 068 069 /** Template file that contains directives for generation of entries. */ 070 private TemplateFile templateFile; 071 072 /** Warnings issued by the parsing of the template file. */ 073 private final List<LocalizableMessage> warnings = new LinkedList<>(); 074 075 /** Indicates if the generator is closed. */ 076 private boolean isClosed; 077 078 /** Indicates if the generator is initialized, which means template file has been parsed. */ 079 private boolean isInitialized; 080 081 /** Random seed is used to generate random data. */ 082 private int randomSeed = DEFAULT_RANDOM_SEED; 083 084 /** 085 * Path to the directory that may contain additional resource files needed 086 * during the generation process. It may be {@code null}. 087 */ 088 private String resourcePath; 089 090 /** 091 * Schema is used to create attributes. If not provided, the default schema 092 * is used. 093 */ 094 private Schema schema; 095 096 /** 097 * Path of template file, can be {@code null} if template file has been 098 * provided through another way. 099 */ 100 private String templatePath; 101 102 /** 103 * Lines of template file, can be {@code null} if template file has been 104 * provided through another way. 105 */ 106 private String[] templateLines; 107 108 /** 109 * Input stream containing template file, can be {@code null} if template 110 * file has been provided through another way. 111 */ 112 private InputStream templateStream; 113 114 /** Indicates whether branch entries should be generated. 115 * 116 * Default is {@code true} 117 */ 118 private boolean generateBranches = true; 119 120 /** Dictionary of constants to use in the template file. */ 121 private Map<String, String> constants = new HashMap<>(); 122 123 /** 124 * Creates a generator using default values. 125 * <p> 126 * The default template file will be used to generate entries. 127 */ 128 public EntryGenerator() { 129 // nothing to do 130 } 131 132 /** 133 * Creates a generator from the provided template path. 134 * 135 * @param templatePath 136 * Path of the template file. 137 */ 138 public EntryGenerator(final String templatePath) { 139 Reject.ifNull(templatePath); 140 this.templatePath = templatePath; 141 } 142 143 /** 144 * Creates a generator from the provided template lines. 145 * 146 * @param templateLines 147 * Lines defining the template file. 148 */ 149 public EntryGenerator(final String... templateLines) { 150 Reject.ifNull(templateLines); 151 this.templateLines = templateLines; 152 } 153 154 /** 155 * Creates a generator from the provided template lines. 156 * 157 * @param templateLines 158 * Lines defining the template file. 159 */ 160 public EntryGenerator(final List<String> templateLines) { 161 Reject.ifNull(templateLines); 162 this.templateLines = templateLines.toArray(new String[templateLines.size()]); 163 } 164 165 /** 166 * Creates a generator from the provided input stream. 167 * 168 * @param templateStream 169 * Input stream to read the template file. 170 */ 171 public EntryGenerator(final InputStream templateStream) { 172 Reject.ifNull(templateStream); 173 this.templateStream = templateStream; 174 } 175 176 /** 177 * Sets the random seed to use when generating entries. 178 * 179 * @param seed 180 * The random seed to use. 181 * @return A reference to this {@code EntryGenerator}. 182 */ 183 public EntryGenerator setRandomSeed(final int seed) { 184 randomSeed = seed; 185 return this; 186 } 187 188 /** 189 * Sets the resource path, used to looks for resources files like first 190 * names, last names, or other custom resources. 191 * 192 * @param path 193 * The resource path. 194 * @return A reference to this {@code EntryGenerator}. 195 */ 196 public EntryGenerator setResourcePath(final String path) { 197 Reject.ifNull(path); 198 resourcePath = path; 199 return this; 200 } 201 202 /** 203 * Sets the schema which should be when generating entries. The default 204 * schema is used if no other is specified. 205 * 206 * @param schema 207 * The schema which should be used for generating entries. 208 * @return A reference to this {@code EntryGenerator}. 209 */ 210 public EntryGenerator setSchema(final Schema schema) { 211 this.schema = schema; 212 return this; 213 } 214 215 /** 216 * Sets a constant to use in template file. It overrides the constant set in 217 * the template file. 218 * 219 * @param name 220 * The name of the constant. 221 * @param value 222 * The value of the constant. 223 * @return A reference to this {@code EntryGenerator}. 224 */ 225 public EntryGenerator setConstant(String name, Object value) { 226 constants.put(name, value.toString()); 227 return this; 228 } 229 230 /** 231 * Sets the flag which indicates whether branch entries should be generated. 232 * 233 * The default is {@code true}. 234 * 235 * @param generateBranches 236 * Indicates whether or not the branches DN entries has to be generated. 237 * @return A reference to this {@code EntryGenerator}. 238 */ 239 public EntryGenerator setGenerateBranches(boolean generateBranches) { 240 this.generateBranches = generateBranches; 241 return this; 242 } 243 244 /** 245 * Checks if there are some warning(s) after parsing the template file. 246 * <p> 247 * Warnings are available only after the first call to {@code hasNext()} or 248 * {@code readEntry()} methods. 249 * 250 * @return {@code true} if there is at least one warning. 251 */ 252 public boolean hasWarnings() { 253 return !warnings.isEmpty(); 254 } 255 256 /** 257 * Returns the warnings generated by the parsing of template file. 258 * <p> 259 * Warnings are available only after the first call to {@code hasNext()} or 260 * {@code readEntry()} methods. 261 * 262 * @return The list of warnings, which is empty if there are no warnings. 263 */ 264 public List<LocalizableMessage> getWarnings() { 265 return Collections.unmodifiableList(warnings); 266 } 267 268 @Override 269 public void close() { 270 isClosed = true; 271 } 272 273 @Override 274 public boolean hasNext() throws IOException { 275 if (isClosed) { 276 return false; 277 } 278 ensureGeneratorIsInitialized(); 279 return templateFile.hasNext(); 280 } 281 282 @Override 283 public Entry readEntry() throws IOException { 284 if (!hasNext()) { 285 throw new NoSuchElementException(); 286 } else { 287 return templateFile.nextEntry(); 288 } 289 } 290 291 /** 292 * Check that generator is initialized, and initialize it 293 * if it has not been initialized. 294 */ 295 private void ensureGeneratorIsInitialized() throws IOException { 296 if (!isInitialized) { 297 isInitialized = true; 298 initialize(); 299 } 300 } 301 302 /** 303 * Initializes the generator, by retrieving template file and parsing it. 304 */ 305 private void initialize() throws IOException { 306 if (schema == null) { 307 schema = Schema.getDefaultSchema(); 308 } 309 templateFile = new TemplateFile(schema, constants, resourcePath, new Random(randomSeed), generateBranches); 310 try { 311 if (templatePath != null) { 312 templateFile.parse(templatePath, warnings); 313 } else if (templateLines != null) { 314 templateFile.parse(templateLines, warnings); 315 } else if (templateStream != null) { 316 templateFile.parse(templateStream, warnings); 317 } else { 318 // use default template file 319 templateFile.parse(warnings); 320 } 321 } catch (IOException e) { 322 throw e; 323 } catch (Exception e) { 324 throw DecodeException.fatalError(ERR_ENTRY_GENERATOR_EXCEPTION_DURING_PARSE.get(e.getMessage()), e); 325 } 326 } 327 328}