001: /*
002: * (c) Copyright 2007 by Volker Bergmann. All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, is permitted under the terms of the
006: * GNU General Public License.
007: *
008: * For redistributing this software or a derivative work under a license other
009: * than the GPL-compatible Free Software License as defined by the Free
010: * Software Foundation or approved by OSI, you must first obtain a commercial
011: * license to this software product from Volker Bergmann.
012: *
013: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
014: * WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS,
015: * REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
016: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
017: * HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
018: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
019: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
020: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
021: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
022: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
023: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
024: * POSSIBILITY OF SUCH DAMAGE.
025: */
026:
027: package org.databene.benerator.main;
028:
029: import org.databene.platform.db.DBSystem;
030: import org.databene.platform.xml.XMLElement2BeanConverter;
031: import org.databene.platform.xml.XMLUtil;
032: import org.databene.model.Processor;
033: import org.databene.commons.*;
034: import org.databene.commons.context.ContextStack;
035: import org.databene.commons.context.DefaultContext;
036: import org.databene.commons.context.PropertiesContext;
037: import org.databene.script.ScriptConverter;
038: import org.databene.script.ScriptUtil;
039: import org.databene.task.TaskRunner;
040: import org.databene.task.Task;
041: import org.databene.task.PageListener;
042: import org.databene.benerator.factory.EntityGeneratorFactory;
043: import org.databene.benerator.factory.ComponentGeneratorFactory;
044: import org.databene.benerator.factory.GenerationSetup;
045: import org.databene.benerator.Generator;
046: import org.databene.model.consumer.Consumer;
047: import org.databene.model.consumer.ProcessorToConsumerAdapter;
048: import org.databene.model.data.*;
049: import org.databene.model.storage.StorageSystem;
050: import org.databene.model.storage.StorageSystemConsumer;
051: import org.databene.model.system.SystemToStorageAdapter;
052: import org.apache.commons.logging.Log;
053: import org.apache.commons.logging.LogFactory;
054: import org.w3c.dom.Node;
055: import org.w3c.dom.Document;
056: import org.w3c.dom.Element;
057: import org.w3c.dom.Attr;
058: import org.w3c.dom.NodeList;
059: import org.w3c.dom.NamedNodeMap;
060:
061: import java.io.IOException;
062: import java.util.*;
063: import java.util.concurrent.ExecutorService;
064: import java.util.concurrent.Executors;
065:
066: /**
067: * Parses and executes a benerator setup file.<br/>
068: * <br/>
069: * Created: 14.08.2007 19:14:28
070: * @author Volker Bergmann
071: */
072: public class Benerator implements GenerationSetup {
073:
074: private static final Log logger = LogFactory
075: .getLog(Benerator.class);
076:
077: public static final String DEFAULT_SCRIPT = "ftl";
078: public static final boolean DEFAULT_NULL = true;
079: public static final String DEFAULT_ENCODING = SystemInfo
080: .fileEncoding();
081: public static final int DEFAULT_PAGESIZE = 1;
082:
083: private DataModel model;
084: private ExecutorService executor;
085: private Escalator escalator;
086:
087: private String defaultScript;
088: private boolean defaultNull;
089: private String defaultEncoding;
090: private int defaultPagesize;
091:
092: //private int totalEntityCount = 0;
093:
094: public static void main(String[] args) throws IOException {
095: if (args.length == 0) {
096: printHelp();
097: java.lang.System.exit(-1);
098: }
099: new Benerator().processFile(args[0]);
100: }
101:
102: private static void printHelp() {
103: java.lang.System.out
104: .println("Please specify a file name as command line parameter");
105: }
106:
107: public Benerator() {
108: this .model = new DataModel();
109: this .executor = Executors.newCachedThreadPool();
110: this .escalator = new LoggerEscalator();
111: this .defaultScript = DEFAULT_SCRIPT;
112: this .defaultNull = DEFAULT_NULL;
113: this .defaultEncoding = DEFAULT_ENCODING;
114: this .defaultPagesize = DEFAULT_PAGESIZE;
115: }
116:
117: public void processFile(String uri) throws IOException {
118: try {
119: long startTime = java.lang.System.currentTimeMillis();
120: ContextStack context = new ContextStack();
121: context.push(new PropertiesContext(java.lang.System
122: .getenv()));
123: context.push(new PropertiesContext(java.lang.System
124: .getProperties()));
125: context.push(new DefaultContext());
126: context.set("benerator", this );
127: Document document = IOUtil.parseXML(uri);
128: Element root = document.getDocumentElement();
129: NodeList nodes = root.getChildNodes();
130: Set<Heavyweight> resources = new HashSet<Heavyweight>();
131: for (int i = 0; i < nodes.getLength(); i++) {
132: Node node = nodes.item(i);
133: if (!(node instanceof Element))
134: continue;
135: parseRootChild((Element) node, resources, context);
136: }
137: for (Heavyweight resource : resources) {
138: resource.close();
139: }
140: //java.lang.System.out.println("context: " + context);
141: long elapsedTime = java.lang.System.currentTimeMillis()
142: - startTime;
143: logger.info("Elapsed time: "
144: + RoundedNumberFormat.format(elapsedTime, 0)
145: + " ms");
146: /*
147: long elapsedTime = java.lang.System.currentTimeMillis() - startTime;
148: logger.info("Created " + RoundedNumberFormat.format(totalEntityCount, 0) + " entities " +
149: "in " + RoundedNumberFormat.format(elapsedTime, 0) + " ms " +
150: "(" + RoundedNumberFormat.format(totalEntityCount * 3600000L / elapsedTime, 0) + " p.h.)");
151: */
152: } finally {
153: this .executor.shutdownNow();
154: }
155: }
156:
157: private void parseRootChild(Element element,
158: Set<Heavyweight> resources, ContextStack context) {
159: String elementType = element.getNodeName();
160: if ("bean".equals(elementType))
161: parseBean(element, resources, context);
162: else if ("create-entities".equals(elementType))
163: parseAndRunCreateEntities(element, resources, context);
164: else if ("run-task".equals(elementType))
165: parseRunTask(element, context);
166: else if ("property".equals(elementType))
167: parseProperty(element, context);
168: else if ("include".equals(elementType))
169: parseInclude(element, context);
170: else if ("echo".equals(elementType))
171: parseEcho(element, context);
172: else if ("database".equals(elementType))
173: parseDatabase(element, resources, context);
174: else
175: throw new ConfigurationError("Unknown element: "
176: + elementType);
177: }
178:
179: private void parseProperty(Element element, ContextStack context) {
180: String propertyName = element.getAttribute("name");
181: Object propertyValue;
182: if (element.hasAttribute("value"))
183: propertyValue = parseAttribute(element, "value", context);
184: else if (element.hasAttribute("ref"))
185: propertyValue = context.get(parseAttribute(element, "ref",
186: context));
187: else
188: throw new ConfigurationError("Syntax error");
189: context.set(propertyName, propertyValue);
190: }
191:
192: private void parseEcho(Element element, ContextStack context) {
193: String message = parseAttribute(element, "message", context);
194: System.out.println(ScriptUtil.render(message, context,
195: defaultScript));
196: }
197:
198: private void parseInclude(Element element, ContextStack context) {
199: String uri = parseAttribute(element, "uri", context);
200: try {
201: ScriptConverter preprocessor = new ScriptConverter(context,
202: defaultScript);
203: DefaultEntryConverter converter = new DefaultEntryConverter(
204: preprocessor, context, true);
205: IOUtil.readProperties(uri, converter);
206: } catch (IOException e) {
207: throw new ConfigurationError(
208: "Properties not found at uri: " + uri);
209: }
210: }
211:
212: private Object parseBean(Element element,
213: Set<Heavyweight> resources, ContextStack context) {
214: try {
215: String beanId = parseAttribute(element, "id", context);
216: if (beanId != null)
217: logger.debug("Instantiating bean with id '" + beanId
218: + "'");
219: else
220: logger.debug("Instantiating bean of class "
221: + parseAttribute(element, "class", context));
222: Object bean = XMLElement2BeanConverter.convert(element,
223: context,
224: new ScriptConverter(context, defaultScript));
225: if (!StringUtil.isEmpty(beanId)) {
226: BeanUtil.setPropertyValue(bean, "id", beanId, false);
227: context.set(beanId, bean);
228: }
229: if (bean instanceof DescriptorProvider)
230: model.addDescriptorProvider((DescriptorProvider) bean);
231: if (bean instanceof Heavyweight)
232: resources.add((Heavyweight) bean);
233: return bean;
234: } catch (ConversionException e) {
235: throw new ConfigurationError(e);
236: }
237: }
238:
239: private void parseDatabase(Element element,
240: Set<Heavyweight> resources, ContextStack context) {
241: try {
242: String id = parseAttribute(element, "id", context);
243: if (id == null)
244: throw new ConfigurationError();
245: logger.debug("Instantiating database with id '" + id + "'");
246: DBSystem db = new DBSystem(id, parseAttribute(element,
247: "url", context), parseAttribute(element, "driver",
248: context), parseAttribute(element, "user", context),
249: parseAttribute(element, "password", context));
250: db.setSchema(parseAttribute(element, "schema", context));
251: context.set(id, db);
252: model.addDescriptorProvider(db);
253: resources.add(db);
254: } catch (ConversionException e) {
255: throw new ConfigurationError(e);
256: }
257: }
258:
259: private void parseRunTask(Element element, ContextStack context) {
260: try {
261: String beanName = parseAttribute(element, "name", context);
262: logger.debug("Instantiating task '" + beanName + "'");
263: ScriptConverter scriptConverter = new ScriptConverter(
264: context, defaultScript);
265: Task task = (Task) XMLElement2BeanConverter.convert(
266: element, context, scriptConverter);
267: int count = parseIntAttribute(element, "count", context, 1);
268: int pageSize = parseIntAttribute(element, "pagesize",
269: context, defaultPagesize);
270: int threads = parseIntAttribute(element, "threads",
271: context, 1);
272: PageListener pager = parsePager(element, context);
273: TaskRunner.run(task, context, count, pager, pageSize,
274: threads, executor);
275: } catch (ConversionException e) {
276: throw new ConfigurationError(e);
277: }
278: }
279:
280: private PageListener parsePager(Element element,
281: ContextStack context) {
282: String pagerSetup = parseAttribute(element, "pager", context);
283: if (StringUtil.isEmpty(pagerSetup))
284: return null;
285: PageListener pager = null;
286: try {
287: pager = (PageListener) BeanUtil.newInstance(pagerSetup);
288: } catch (Exception e) {
289: pager = (PageListener) context.get(pagerSetup);
290: }
291: if (pager == null)
292: throw new ConfigurationError(
293: "pager=\""
294: + pagerSetup
295: + "\" neither denotes a class nor an object in the context.");
296: return pager;
297: }
298:
299: private void parseAndRunCreateEntities(Element element,
300: Set<Heavyweight> resources, ContextStack context) {
301: PagedCreateEntityTask task = parseCreateEntities(element,
302: resources, context, false);
303: task.init(context);
304: try {
305: task.run();
306: } finally {
307: task.destroy();
308: }
309: }
310:
311: private PagedCreateEntityTask parseCreateEntities(Element element,
312: Set<Heavyweight> resources, ContextStack context,
313: boolean isSubTask) {
314: EntityDescriptor descriptor = mapEntityDescriptorElement(
315: element, context);
316: logger.info(descriptor);
317: // parse consumers
318: Collection<Consumer<Entity>> consumers = parseConsumers(
319: element, resources, context);
320: // parse variables
321: Map<String, Generator<? extends Object>> variables = parseVariables(
322: element, context);
323: // generate
324: Generator<Entity> entityGenerator = EntityGeneratorFactory
325: .createEntityGenerator(descriptor, context, this );
326: int count = parseIntAttribute(element, "count", context, -1);
327: int pageSize = parseIntAttribute(element, "pagesize", context,
328: defaultPagesize);
329: int threads = parseIntAttribute(element, "threads", context, 1);
330: Generator<Entity> configuredGenerator = new ConfiguredGenerator(
331: entityGenerator, variables, context);
332: // create sub create-entities
333: NodeList subCreates = element
334: .getElementsByTagName("create-entities");
335: List<PagedCreateEntityTask> subs = new ArrayList<PagedCreateEntityTask>(
336: subCreates.getLength());
337: for (int i = 0; i < subCreates.getLength(); i++) {
338: Element ceElement = (Element) subCreates.item(i);
339: subs.add(parseCreateEntities(ceElement, resources, context,
340: true));
341: }
342: // done
343: return new PagedCreateEntityTask(descriptor.getName(), count,
344: pageSize, threads, subs, configuredGenerator,
345: consumers, executor, isSubTask);
346: }
347:
348: private Map<String, Generator<? extends Object>> parseVariables(
349: Element parent, ContextStack context) {
350: HashMap<String, Generator<? extends Object>> variables = new HashMap<String, Generator<? extends Object>>();
351: NodeList varElements = parent.getElementsByTagName("variable");
352: for (int i = 0; i < varElements.getLength(); i++) {
353: Element varElement = (Element) varElements.item(i);
354: ComponentDescriptor componentDescriptor = mapComponentDescriptor(
355: varElement, context);
356: Generator<? extends Object> generator = ComponentGeneratorFactory
357: .getComponentGenerator(componentDescriptor,
358: context, this );
359: String varName = parseAttribute(varElement, "name", context);
360: variables.put(varName, generator);
361: }
362: return variables;
363: }
364:
365: private Collection<Consumer<Entity>> parseConsumers(Element parent,
366: Set<Heavyweight> resources, ContextStack context) {
367: String entityName = parseAttribute(parent, "name", context);
368: List<Consumer<Entity>> consumers = new ArrayList<Consumer<Entity>>();
369: Consumer<Entity> consumer;
370: if (parent.hasAttribute("consumer")) {
371: consumer = getConsumerFromContext(context, parseAttribute(
372: parent, "consumer", context));
373: consumers.add(consumer);
374: }
375: List<Element> consumerElements = getChildElements(parent,
376: "consumer");
377: for (int i = 0; i < consumerElements.size(); i++) {
378: Element consumerElement = (Element) consumerElements.get(i);
379: if (consumerElement.hasAttribute("ref")) {
380: consumer = getConsumerFromContext(context,
381: parseAttribute(consumerElement, "ref", context));
382: } else if (consumerElement.hasAttribute("class")) {
383: consumer = (Consumer<Entity>) parseBean(
384: consumerElement, resources, context);
385: } else
386: throw new UnsupportedOperationException(
387: "Don't know how to handle "
388: + XMLUtil.format(consumerElement));
389: consumers.add(consumer);
390: }
391: if (consumers.size() == 0)
392: escalator.escalate(
393: "No consumers defined for " + entityName, this ,
394: null);
395: return consumers;
396: }
397:
398: private List<Element> getChildElements(Element parent,
399: String nodeName) {
400: List<Element> children = new ArrayList<Element>();
401: NodeList nodes = parent.getChildNodes();
402: for (int i = 0; i < nodes.getLength(); i++) {
403: Node childNode = nodes.item(i);
404: if (!(childNode instanceof Element))
405: continue;
406: Element childElement = (Element) childNode;
407: String childType = childElement.getNodeName();
408: if (nodeName.equals(childType))
409: children.add(childElement);
410: }
411: return children;
412: }
413:
414: private Consumer<Entity> getConsumerFromContext(Context context,
415: String consumerId) {
416: Consumer<Entity> consumer;
417: Object tmp = context.get(consumerId);
418: if (tmp instanceof StorageSystem)
419: consumer = new StorageSystemConsumer((StorageSystem) tmp);
420: else if (tmp instanceof org.databene.model.system.System) {
421: StorageSystem storage = new SystemToStorageAdapter(
422: (org.databene.model.system.System) tmp);
423: consumer = new StorageSystemConsumer(storage);
424: } else if (tmp instanceof Consumer)
425: consumer = (Consumer<Entity>) tmp;
426: else if (tmp instanceof Processor)
427: consumer = new ProcessorToConsumerAdapter(
428: (Processor<Entity>) tmp);
429: else if (StringUtil.isEmpty(consumerId))
430: throw new ConfigurationError("Empty consumer id");
431: else if (tmp == null)
432: throw new ConfigurationError("Consumer not found: "
433: + consumerId);
434: else
435: throw new UnsupportedOperationException(
436: "Consumer type not supported: " + tmp.getClass());
437: return consumer;
438: }
439:
440: private EntityDescriptor mapEntityDescriptorElement(
441: Element element, Context context) {
442: String entityName = parseAttribute(element, "name", context);
443: EntityDescriptor parentDescriptor = model
444: .getTypeDescriptor(entityName);
445: EntityDescriptor ctDescriptor = new EntityDescriptor(
446: entityName, false, parentDescriptor); // TODO v0.5 how to handle case sensitivity here?
447: NamedNodeMap attributes = element.getAttributes();
448: for (int i = 0; i < attributes.getLength(); i++) {
449: Attr attribute = (Attr) attributes.item(i);
450: if (!"pagesize".equals(attribute.getName())
451: && !"threads".equals(attribute.getName())
452: && !"consumer".equals(attribute.getName()))
453: ctDescriptor.setDetail(attribute.getName(),
454: parseAttribute(attribute, context));
455: }
456: NodeList nodes = element.getChildNodes();
457: for (int i = 0; i < nodes.getLength(); i++) {
458: Node childNode = nodes.item(i);
459: if (!(childNode instanceof Element))
460: continue;
461: Element childElement = (Element) childNode;
462: String childType = childElement.getNodeName();
463: if ("attribute".equals(childType)) {
464: ComponentDescriptor ad = mapComponentDescriptor(
465: childElement, context);
466: ctDescriptor.setComponentDescriptor(ad);
467: } else if ("id".equals(childType)) {
468: ComponentDescriptor ad = mapComponentDescriptor(
469: childElement, context);
470: ctDescriptor.setComponentDescriptor(ad);
471: } else if (!"create-entities".equals(childType)
472: && !"consumer".equals(childType)
473: && !"variable".equals(childType))
474: throw new ConfigurationError("Unexpected element: "
475: + childType);
476: }
477: return ctDescriptor;
478: }
479:
480: private ComponentDescriptor mapComponentDescriptor(Element element,
481: Context context) {
482: String attributeName = parseAttribute(element, "name", context);
483: ComponentDescriptor descriptor;
484: String nodeName = element.getNodeName();
485: if ("attribute".equals(nodeName) || "variable".equals(nodeName))
486: descriptor = new AttributeDescriptor(attributeName);
487: else if ("id".equals(nodeName))
488: descriptor = new IdDescriptor(attributeName);
489: else
490: throw new UnsupportedOperationException(
491: "'attribute' or 'variable' element expected, found: "
492: + nodeName);
493: NamedNodeMap attributes = element.getAttributes();
494: for (int i = 0; i < attributes.getLength(); i++) {
495: Attr attribute = (Attr) attributes.item(i);
496: descriptor.setDetail(attribute.getName(), parseAttribute(
497: attribute, context));
498: }
499: return descriptor;
500: }
501:
502: // attribute parsing -----------------------------------------------------------------------------------------------
503:
504: private String parseAttribute(Attr attribute, Context context) {
505: String name = attribute.getName();
506: String value = attribute.getValue();
507: return renderAttribute(name, value, context);
508: }
509:
510: private String parseAttribute(Element element, String name,
511: Context context) {
512: String value = element.getAttribute(name);
513: return renderAttribute(name, value, context);
514: }
515:
516: private int parseIntAttribute(Element element, String name,
517: Context context, int defaultValue) {
518: String text = parseAttribute(element, name, context);
519: if (StringUtil.isEmpty(text))
520: return defaultValue;
521: text = ScriptUtil.render(text, context, defaultScript);
522: return Integer.parseInt(text);
523: }
524:
525: private String renderAttribute(String name, String value,
526: Context context) {
527: if ("script".equals(name))
528: return value;
529: else
530: return ScriptUtil.render(value, context, defaultScript);
531: }
532:
533: // java.lang.Object overrides --------------------------------------------------------------------------------------
534:
535: @Override
536: public String toString() {
537: return getClass().getSimpleName();
538: }
539:
540: // properties ------------------------------------------------------------------------------------------------------
541:
542: /**
543: * @return the defaultScript
544: */
545: public String getDefaultScript() {
546: return defaultScript;
547: }
548:
549: /**
550: * @param defaultScript the defaultScript to set
551: */
552: public void setDefaultScript(String defaultScript) {
553: this .defaultScript = defaultScript;
554: }
555:
556: /**
557: * @return the defaultNull
558: */
559: public boolean isDefaultNull() {
560: return defaultNull;
561: }
562:
563: /**
564: * @param defaultNull the defaultNull to set
565: */
566: public void setDefaultNull(boolean defaultNull) {
567: this .defaultNull = defaultNull;
568: }
569:
570: /**
571: * @return the defaultEncoding
572: */
573: public String getDefaultEncoding() {
574: return defaultEncoding;
575: }
576:
577: /**
578: * @param defaultEncoding the defaultEncoding to set
579: */
580: public void setDefaultEncoding(String defaultEncoding) {
581: this .defaultEncoding = defaultEncoding;
582: }
583:
584: /**
585: * @return the defaultPageSize
586: */
587: public int getDefaultPagesize() {
588: return defaultPagesize;
589: }
590:
591: /**
592: * @param defaultPageSize the defaultPageSize to set
593: */
594: public void setDefaultPagesize(int defaultPageSize) {
595: this.defaultPagesize = defaultPageSize;
596: }
597:
598: }
|