View Javadoc

1   /**
2    * Copyright 2008 Bryan Ray
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    * http://www.apache.org/licenses/LICENSE-2.0 
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License. 
15   */
16  package uk.org.bryanray.testtoys.generator;
17  
18  import java.io.BufferedWriter;
19  import java.io.FileInputStream;
20  import java.io.FileNotFoundException;
21  import java.io.FileOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  import java.io.OutputStreamWriter;
26  import java.io.Writer;
27  import java.util.Arrays;
28  import java.util.HashMap;
29  import java.util.Iterator;
30  import java.util.Map;
31  import java.util.Properties;
32  import java.util.Map.Entry;
33  
34  import org.apache.commons.cli.CommandLine;
35  import org.apache.commons.cli.CommandLineParser;
36  import org.apache.commons.cli.GnuParser;
37  import org.apache.commons.cli.HelpFormatter;
38  import org.apache.commons.cli.Option;
39  import org.apache.commons.cli.OptionBuilder;
40  import org.apache.commons.cli.Options;
41  import org.apache.commons.cli.ParseException;
42  
43  import uk.org.bryanray.testtoys.generator.sequencegenerators.SequenceGenerator;
44  import uk.org.bryanray.testtoys.util.BeanUtil;
45  import uk.org.bryanray.testtoys.util.ExceptionUtil;
46  import uk.org.bryanray.testtoys.util.TemplateUtil;
47  
48  /**
49   * <DL>
50   * <DT><B>Project:</B></DT>
51   * <DD>TestDataLoader</DD>
52   * <DT><B>Filename:</B></DT>
53   * <DD>Generator.java</DD>
54   * <DT><B>Creation Date:</B></DT>
55   * <DD>25 May 2008</DD>
56   * </DL>
57   * 
58   * The purpose of this this application is to generate a large quantity of test
59   * data for performance testing.
60   * 
61   * @author Bryan Ray
62   */
63  public class Generator {
64  
65  	/**
66  	 * A map that associates a sequence name with a {@link SequenceGenerator}
67  	 * instance. The sequence name is what is used in the template to specify
68  	 * which sequence to use.
69  	 */
70  	private Map<String, SequenceGenerator> sequences;
71  
72  	/**
73  	 * A map that associates a template with an output stream to which the
74  	 * template will repeatedly be written. The template is in the form of a
75  	 * {@link String} and the output stream is an instance of
76  	 * {@link OutputStream}.
77  	 */
78  	private Map<String, Writer> writerTemplates;
79  
80  	/**
81  	 * The number of entities to generate.
82  	 */
83  	private int entityCount = 0;
84  
85  	/**
86  	 * @param args
87  	 *            The command line arguments.
88  	 */
89  	public static void main(String[] args) {
90  		Generator generator = new Generator();
91  		// Read configuration file
92  		Options commandOptions = getCommandOptions();
93  		CommandLine commandLine = parseCommandArguments(args, commandOptions);
94  		if (commandLine.hasOption("configfile")) {
95  			try {
96  				String configFileName = commandLine
97  						.getOptionValue("configfile");
98  				generator.initialiseConfig(new FileInputStream(configFileName));
99  			} catch (FileNotFoundException fnfe) {
100 				throw ExceptionUtil.convertException(fnfe);
101 			}
102 		} else {
103 			HelpFormatter formatter = new HelpFormatter();
104 			formatter.printHelp("generator", commandOptions);
105 		}
106 
107 		// Generate test data
108 		generator.generate();
109 		generator.closeWriters();
110 	}
111 
112 	/**
113 	 * @param commandArguments
114 	 *            The command arguments passed to {@link #main(String[]).
115 	 * @param options
116 	 *            The options builder from {@link #getCommandOptions().
117 	 */
118 	private static CommandLine parseCommandArguments(String[] commandArguments,
119 			Options options) {
120 		CommandLineParser parser = new GnuParser();
121 		try {
122 			return parser.parse(options, commandArguments);
123 		} catch (ParseException pe) {
124 			throw ExceptionUtil.convertException(pe);
125 		}
126 	}
127 
128 	/**
129 	 * @return A command line object that follows the builder pattern.
130 	 */
131 	@SuppressWarnings("static-access")
132 	private static Options getCommandOptions() {
133 		Options options = new Options();
134 		Option configFile = OptionBuilder.withArgName("file").hasArg()
135 				.withDescription("use given configuration file").create(
136 						"configfile");
137 		options.addOption(configFile);
138 		return options;
139 	}
140 
141 	/**
142 	 * Convenience method that calls individual configuration methods.
143 	 * 
144 	 * @see #initialiseApplication(Properties)
145 	 * @see #initialiseSequences(Properties)
146 	 * @see #initialiseTemplates(Properties)
147 	 */
148 	private void initialiseConfig(InputStream inputStream) {
149 		Properties properties = new Properties();
150 		try {
151 			properties.load(inputStream);
152 		} catch (IOException ioe) {
153 			throw ExceptionUtil.convertException(ioe);
154 		}
155 		initialiseApplication(properties);
156 		initialiseSequences(properties);
157 		initialiseTemplates(properties);
158 	}
159 
160 	/**
161 	 * @param properties
162 	 *            The configuration that will determine how many entities will
163 	 *            be generated for the test data.
164 	 */
165 	private void initialiseApplication(Properties properties) {
166 		String entityCount = properties.getProperty("entity.count");
167 		if (null != entityCount) {
168 			this.entityCount = Integer.parseInt(entityCount);
169 		}
170 	}
171 
172 	/**
173 	 * @param properties
174 	 *            The configuration that will determine what values fill the
175 	 *            place holders in the output stream templates.
176 	 */
177 	private void initialiseSequences(Properties properties) {
178 		sequences = new HashMap<String, SequenceGenerator>();
179 		for (Iterator<Entry<Object, Object>> i = properties.entrySet()
180 				.iterator(); i.hasNext();) {
181 			Entry<Object, Object> entry = i.next();
182 			String key = entry.getKey().toString();
183 			if (key.startsWith("sequence.")) {
184 				String sequenceName = key.substring(9);
185 				String[] sequenceValues = entry.getValue().toString().split(
186 						",", 2);
187 				if (2 <= sequenceValues.length) {
188 					String className = sequenceValues[0];
189 					String[] constructorArguments = sequenceValues[1]
190 							.split(",");
191 					SequenceGenerator generator = (SequenceGenerator) BeanUtil
192 							.createInstance(className,
193 									new Class[] { java.util.List.class },
194 									new Object[] { Arrays
195 											.asList(constructorArguments) });
196 					sequences.put(sequenceName, generator);
197 				} else {
198 					throw new RuntimeException(
199 							"Bad configuration for sequence:" + sequenceName);
200 				}
201 			}
202 		}
203 	}
204 
205 	/**
206 	 * @param properties
207 	 *            The configuration for the output streams, determining the file
208 	 *            name for the stream and the template that will be written.
209 	 */
210 	private void initialiseTemplates(Properties properties) {
211 		writerTemplates = new HashMap<String, Writer>();
212 		for (Iterator<Entry<Object, Object>> i = properties.entrySet()
213 				.iterator(); i.hasNext();) {
214 			Entry<Object, Object> entry = i.next();
215 			String key = entry.getKey().toString();
216 			if (key.startsWith("template.")) {
217 				String templateName = key.substring(9);
218 				String template = entry.getValue().toString();
219 
220 				Object outputFileObject = properties.get("testdata."
221 						+ templateName);
222 				if (null != outputFileObject) {
223 					String outputFile = outputFileObject.toString();
224 					OutputStream outputStream = null;
225 					try {
226 						outputStream = new FileOutputStream(outputFile);
227 					} catch (FileNotFoundException fnfe) {
228 						throw ExceptionUtil.convertException(fnfe);
229 					}
230 					Writer writer = new BufferedWriter(new OutputStreamWriter(
231 							outputStream));
232 					writerTemplates.put(template, writer);
233 				} else {
234 					throw new RuntimeException(
235 							"Bad configuration for template:" + templateName);
236 				}
237 			}
238 		}
239 	}
240 
241 	/**
242 	 * Writes test data to the output streams.
243 	 */
244 	private void generate() {
245 		for (int i = 0; i < entityCount; i++) {
246 			for (Iterator<Entry<String, Writer>> templateIterator = writerTemplates
247 					.entrySet().iterator(); templateIterator.hasNext();) {
248 				Entry<String, Writer> entry = templateIterator.next();
249 				String template = entry.getKey();
250 				Writer writer = entry.getValue();
251 				String entity = TemplateUtil.substituteTemplatePlaceholders(
252 						template, new SequenceMapAdapter(sequences));
253 				try {
254 					writer.write(entity);
255 				} catch (IOException ioe) {
256 					throw ExceptionUtil.convertException(ioe);
257 				}
258 			}
259 
260 			for (Iterator<SequenceGenerator> generatorIterator = sequences
261 					.values().iterator(); generatorIterator.hasNext();) {
262 				SequenceGenerator generator = generatorIterator.next();
263 				generator.next();
264 			}
265 		}
266 	}
267 
268 	/**
269 	 * Closes all the output streams.
270 	 */
271 	private void closeWriters() {
272 		if (null != Generator.this.writerTemplates) {
273 			for (Iterator<Writer> i = Generator.this.writerTemplates.values()
274 					.iterator(); i.hasNext();) {
275 				Writer w = i.next();
276 				try {
277 					w.close();
278 				} catch (IOException ioe) {
279 					ioe.printStackTrace();
280 				}
281 			}
282 		}
283 	}
284 
285 	/**
286 	 * A finaliser that will always be called.
287 	 */
288 	private final Object finaliser = new Object() {
289 		@Override
290 		protected void finalize() throws Throwable {
291 			closeWriters();
292 		}
293 	};
294 }