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