KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > barracuda > taskdefs > EventBuilder


1 /*
2  * Copyright (C) 2003 Christian Cryder [christianc@granitepeaks.com]
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * $Id: EventBuilder.java,v 1.16 2004/02/01 05:16:31 christianc Exp $
19  */

20 package org.enhydra.barracuda.taskdefs;
21
22 import java.io.*;
23 import java.util.*;
24
25 import org.apache.tools.ant.*;
26 import org.apache.tools.ant.taskdefs.*;
27 import org.apache.tools.ant.util.*;
28 import org.xml.sax.*;
29 import org.xml.sax.helpers.*;
30
31 /**
32  * <p>This Ant taskdef reads in an xml file that conforms to
33  * BarracudaEventBuilder.dtd and generates a set of event classes
34  * according to the specified structure. The primary benefit of
35  * this is that it makes it very easy to declaratively define event
36  * hierarchies that still get compiled to real Java classes (thereby
37  * retaining the benefits of strong typing which you get through the
38  * manual approach)</p>
39  *
40  * <p>This taskdef takes two required parameters: <strong>sourceout</strong> (which
41  * should point to a location to write generated .java event source files) and
42  * <strong>descriptor</strong> (which should refer to the location of a valid xml file
43  * describing the event hierarchy).<br>Second, there is a third optional parameter:
44  * <strong>template</strong> which should refer to the localtion of the template file
45  * for generating the Java classes. Specifying this parameter will overwrite the
46  * template attribute inside the "build-events" tag in the xml file.</p>
47  *
48  * <p>Look at the Barracuda build.xml file for further usage examples.</p>
49  *
50  * <p>csc_010404_1 - 2 minor but important enhancements:
51  *
52  * a) you can now use the template attribute in all parts of the event file, making it
53  * possible to specify different templates for individual events, and
54  *
55  * b) added the ability to replace any key values in the template, simply by including
56  * them in the attributes. For instance, if you use a custom attribute which is NOT defined
57  * in the dtd (ie. foo="blah"), the task will attempt to replace @foo@ in the specified template
58  * with the value 'blah'.
59  *
60  * For examples of both of these things, see src\org\enhydra\barracuda\examples\ex1\events.xml
61  *
62  * @author Christian Cryder [christianc@granitepeaks.com]
63  * @author Thorsten Möller - ThorstenMoeller(at)web.de
64  */

65 public class EventBuilder extends Task {
66
67     //private constants
68
private static final String JavaDoc DEFAULT_PARSER = "org.apache.xerces.parsers.SAXParser";
69
70     //private vars
71
protected String JavaDoc parserClass = null;
72     protected File xmlFile = null; //the xml file
73
protected File templateFile = null; //the Java class template
74
protected String JavaDoc sourceOutDir = null; //the directory where source is generated
75

76     /**
77      * Sets the xml event descriptor file.
78      */

79     public void setDescriptor(File xmlFile) {
80         this.xmlFile = xmlFile;
81     }
82
83     /**
84      * Sets directory where source is generated.
85      */

86     public void setSourceout(String JavaDoc sourceOutDir) {
87         this.sourceOutDir = sourceOutDir;
88     }
89
90     /**
91      * Sets the Java class template.
92      */

93     public void setTemplate(File templateFile) {
94         this.templateFile = templateFile;
95     }
96
97     /**
98      * Parse the specified event.xml file, generate event classes from
99      * it, and then compile the resulting classes.
100      */

101     public void execute() throws BuildException {
102         //instantiate the handler, entity resolver
103
LocalHandler handler = new LocalHandler();
104         LocalEntityResolver resolver = new LocalEntityResolver();
105
106         //now parse the event.xml file
107
try {
108             //instantiate the parser
109
if (parserClass==null) parserClass = DEFAULT_PARSER;
110             //log("Instantiating parser "+parserClass, Project.MSG_DEBUG);
111
//XMLReader parser = (XMLReader) Class.forName(parserClass, true, getClass().getClassLoader());
112
XMLReader parser = getXMLReader(parserClass);
113             parser.setContentHandler(handler);
114             parser.setErrorHandler(handler);
115             parser.setEntityResolver(resolver);
116
117             //get the source file
118
log("Generating events from "+xmlFile, Project.MSG_DEBUG);
119             InputSource source = new InputSource(new FileInputStream(xmlFile));
120
121             //parse the file
122
parser.parse(source);
123
124         } catch (org.xml.sax.SAXParseException JavaDoc spe) {
125             spe.printStackTrace(System.err);
126             throw new BuildException("SAX Error parsing event.xml!", spe);
127         } catch (org.xml.sax.SAXException JavaDoc se) {
128             if (se.getException()!=null) {
129                 se.getException().printStackTrace(System.err);
130                 throw new BuildException("SAX Error processing event.xml!", se.getException());
131             } else {
132                 se.printStackTrace(System.err);
133                 throw new BuildException("SAX Error processing event.xml!", se);
134            }
135         } catch (Exception JavaDoc e) {
136             e.printStackTrace(System.err);
137             throw new BuildException("Error processing event.xml!", e);
138         } finally {
139             //finally, make sure we clean up any accessed resources
140
if (resolver!=null) resolver.cleanup();
141         }
142     }
143
144     /**
145      * create SAX2 Parser (XMLReader) instance. Attempts to load the passed-in
146      * preferred parser first. If that fails, it falls back to loading the
147      * parser set by the org.xml.sax.driver property. Failing that, other known
148      * parsers are explicitly attempted for loading. If that fails, you are
149      * trying really hard to defeat this method!
150      *
151      * @param preferred the preferred parser name, may be null
152      * @return an XMLReader, guaranteed non-null
153      * @throws SAXException when no SAX2 Parser is available
154      */

155     private XMLReader getXMLReader(String JavaDoc preferred) throws SAXException {
156         XMLReader parser = null;
157         try { // Preferred
158
if (preferred != null) {
159                 parser = XMLReaderFactory.createXMLReader(preferred);
160             } else {
161                 throw new SAXException("Preferred SAX parser unavailable!");
162             }
163         }
164         catch (SAXException e1) {
165             try { // default
166
// obtain parser via system property...
167
// loads whatever parser was set in the system property
168
// org.xml.sax.driver and will also fall back to the SAX1
169
// system property org.xml.sax.parser
170
parser = XMLReaderFactory.createXMLReader();
171             }
172             catch (SAXException e2) {
173                 try { // Piccolo (speed demon: http://piccolo.sourceforge.net/bench.html -- non-validating, but can be wrapped by a validating parser)
174
parser = XMLReaderFactory.createXMLReader("com.bluecast.xml.Piccolo");
175                 }
176                 catch (SAXException e3) {
177                     try { // Xerces (the standard)
178
parser = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
179                     }
180                     catch (SAXException e4) {
181                         try { // Crimson (exists in j2sdk1.4.x)
182
parser = XMLReaderFactory.createXMLReader("org.apache.crimson.parser.XMLReaderImpl");
183                         }
184                         catch (SAXException e5) {
185                             try { // Ælfred (optionally validating)
186
parser = XMLReaderFactory.createXMLReader("gnu.xml.aelfred2.XmlReader");
187                             }
188                             catch (SAXException e6) {
189                                 try { // older Ælfred (non-validating)
190
parser = XMLReaderFactory.createXMLReader("net.sf.saxon.aelfred.SAXDriver");
191                                 }
192                                 catch (SAXException e7) {
193                                     // Oracle (well, why not?) ...last ditch attempt, let the exception go after this
194
parser = XMLReaderFactory.createXMLReader("oracle.xml.parser.v2.SAXParser");
195                                 }
196                             }
197                         }
198                     }
199                 }
200             }
201         }
202         return parser;
203     }
204
205     /**
206      * helper class to keep track of the various settings for a
207      * given event node
208      */

209     class CurrentSettings {
210         String JavaDoc template = null;
211         String JavaDoc eventPackage = "";
212         String JavaDoc eventName = null;
213         String JavaDoc baseClass = null;
214         Map templateKeys = new HashMap(); //csc_010404_1
215
CurrentSettings parent = null;
216
217         public CurrentSettings createChild() {
218             CurrentSettings cs = new CurrentSettings();
219             cs.template = template;
220             cs.baseClass = baseClass;
221             cs.eventName = eventName;
222             cs.eventPackage = eventPackage;
223             cs.parent = CurrentSettings.this;
224             return cs;
225         }
226
227         public CurrentSettings getRoot() {
228             if (parent==null) return CurrentSettings.this;
229             else return parent.getRoot();
230         }
231
232         public String JavaDoc getParentClassName() {
233             return (parent.eventName==null ? baseClass : parent.eventPackage+"."+parent.eventName);
234         }
235
236         public String JavaDoc toString() {
237             return "{"+eventName+" extends "+getParentClassName()+"}";
238         }
239     }
240
241     /**
242      * Helper class to actually parse the XML file (we use SAX)
243      */

244     class LocalHandler extends DefaultHandler {
245
246         //tags
247
String JavaDoc BUILD_EVENTS = "build-events";
248         String JavaDoc CONTROL_EVENTS = "control-events";
249         String JavaDoc REQ_EVENTS = "req-events";
250         String JavaDoc RESP_EVENTS = "resp-events";
251         String JavaDoc EVENT = "event";
252
253         //attributes
254
String JavaDoc PKG = "pkg";
255         String JavaDoc TEMPLATE = "template";
256         String JavaDoc BASE_CLASS = "base-class";
257         String JavaDoc NAME = "name";
258
259         //variables
260
CurrentSettings cs = null;
261         Map templateCache = new HashMap();
262         int fileCnt = 0;
263
264         public void startDocument() {
265             //set up the default settings
266
cs = new CurrentSettings();
267             cs.template = null;
268             cs.baseClass = null;
269             cs.eventName = null;
270             cs.eventPackage = ".";
271         }
272
273         public void startElement(String JavaDoc uri, String JavaDoc local, String JavaDoc raw, Attributes attrs) throws SAXException {
274             String JavaDoc curTag = local;
275
276             //whenever we start a new element, the first thing to do is to push
277
//a copy of the current settings onto the stack
278
cs = cs.createChild();
279
280             //unload the attributes
281
for (int i=0, max=attrs.getLength(); i<max; i++) {
282                 String JavaDoc name = attrs.getLocalName(i);
283                 String JavaDoc value = attrs.getValue(i);
284                 if (name.equals(PKG)) {
285                     cs.eventPackage = value;
286                 } else if (name.equals(TEMPLATE)) {
287                     cs.template = value;
288                 } else if (name.equals(BASE_CLASS)) {
289                     cs.baseClass = value;
290                 } else if (name.equals(NAME)) {
291                     cs.eventName = value;
292 //csc_010404_1_start
293
//treat any other attributes as custom replacement key/vals for template
294
} else {
295                     log("got custom template key: "+name, Project.MSG_DEBUG);
296                     cs.templateKeys.put("@"+name+"@", value);
297 //csc_010404_1_end
298
}
299             }
300
301             //control-events
302
if (curTag.equals(CONTROL_EVENTS)) {
303                 if (cs.baseClass==null) cs.baseClass = "org.enhydra.barracuda.core.event.ControlEvent";
304
305             //req-events
306
} else if (curTag.equals(REQ_EVENTS)) {
307                 if (cs.baseClass==null) cs.baseClass = "org.enhydra.barracuda.core.event.HttpRequestEvent";
308
309             //resp-events
310
} else if (curTag.equals(RESP_EVENTS)) {
311                 if (cs.baseClass==null) cs.baseClass = "org.enhydra.barracuda.core.event.HttpResponseEvent";
312
313             //event
314
} else if (curTag.equals(EVENT)) {
315                 boolean created = buildEventFile();
316                 if (created) fileCnt++;
317             }
318         }
319
320         public void endElement(String JavaDoc uri, String JavaDoc local, String JavaDoc raw) throws SAXException {
321             cs = cs.parent;
322         }
323
324         public void endDocument() {
325             if (fileCnt>0) log("Created "+fileCnt+" event files from: "+xmlFile+((templateFile!=null)? ", using template: "+templateFile : ""));
326         }
327
328         private String JavaDoc getLocationString(SAXParseException ex) {
329             StringBuffer JavaDoc str = new StringBuffer JavaDoc();
330             String JavaDoc systemId = ex.getSystemId();
331             if (systemId!=null) {
332                 int index = systemId.lastIndexOf(47);
333                 if (index!=-1) systemId = systemId.substring(index + 1);
334                 str.append(systemId);
335             }
336             str.append(':');
337             str.append(ex.getLineNumber());
338             str.append(':');
339             str.append(ex.getColumnNumber());
340             return str.toString();
341         }
342
343         protected byte[] loadTemplate(String JavaDoc targetTemplate) {
344             if (targetTemplate==null) targetTemplate = "~dflt~";
345             byte[] templateBytes = (byte[]) templateCache.get(targetTemplate);
346             if (templateBytes==null) {
347                 BufferedReader br = null;
348                 try {
349                     if (templateFile != null && templateFile.exists()) {
350                         br = new BufferedReader(new FileReader(templateFile));
351                     } else if (cs.template != null) {
352                         br = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream(cs.template)));
353                     } else {
354                         br = new BufferedReader(new InputStreamReader(this.getClass().getResourceAsStream("EventBuilder.template")));
355                     }
356                     templateBytes = FileUtils.readFully(br).getBytes();
357                     templateCache.put(targetTemplate, templateBytes);
358                 } catch (IOException e) {
359                     throw new BuildException("Error reading template", e);
360                 } finally {
361                     try {
362                         if (br != null) {
363                             br.close();
364                         }
365                     } catch (IOException e) {
366                         throw new BuildException("Error closing template", e);
367                     }
368                 }
369             }
370             return templateBytes;
371         }
372
373         protected boolean buildEventFile() {
374             //first of all, see if we need to create a new event file
375
String JavaDoc targetPackage = cs.eventPackage.replace('.','/');
376             File targetPath = new File(sourceOutDir+"/"+targetPackage);
377             File targetFile = new File(targetPath.toString(), cs.eventName+".java");
378
379             //if the target path does not exist create it
380
if (!targetPath.exists()) {
381 //csc_091801 - this doesn't compile in JDK 1.2.2...
382
// if (!targetPath.mkdirs()) throw new BuildException("Error creating path: "+targetPath, location);
383
if (!targetPath.mkdirs()) throw new BuildException("Error creating path: "+targetPath, getLocation());
384             }
385
386             //if the file exists and is newer than the events.xml timestamp,
387
//skip it
388
if (targetFile.exists() &&
389                 targetFile.lastModified()>xmlFile.lastModified()) return false;
390
391             //get the template
392
byte[] templateBytes = loadTemplate(cs.template);
393
394             //create the file from the template
395
try {
396                 targetFile.createNewFile();
397                 FileOutputStream out = new FileOutputStream(targetFile);
398                 out.write(templateBytes, 0, templateBytes.length);
399                 out.flush();
400                 out.close();
401             } catch (IOException e) {
402                 throw new BuildException("Error writing "+targetFile, e);
403             }
404
405             //now perform the replace
406
Replace replace = new Replace();
407 //csc_091801 - this doesn't compile in JDK 1.2.2...
408
// replace.setProject(project);
409
replace.setProject(getProject());
410             replace.setFile(targetFile);
411             Replace.Replacefilter rf = null;
412             //...package name
413
rf = replace.createReplacefilter();
414             rf.setToken("@event.package@");
415             rf.setValue(cs.eventPackage);
416             //...class name
417
rf = replace.createReplacefilter();
418             rf.setToken("@event.name@");
419             rf.setValue(cs.eventName);
420             //...parent class name
421
rf = replace.createReplacefilter();
422             rf.setToken("@event.parent@");
423             rf.setValue(cs.getParentClassName());
424 //csc_010404_1_start
425
//...template keys
426
Iterator it = cs.templateKeys.entrySet().iterator();
427             while (it.hasNext()) {
428                 Map.Entry me = (Map.Entry) it.next();
429                 rf = replace.createReplacefilter();
430                 rf.setToken((String JavaDoc) me.getKey());
431                 rf.setValue((String JavaDoc) me.getValue());
432             }
433 //csc_010404_1_end
434
//...execute it!
435
replace.execute();
436             return true;
437         }
438     }
439
440     /**
441      * local entity resolver to convert references to remote DTDs
442      * to local resources that can be loaded from the classpath
443      */

444     class LocalEntityResolver implements EntityResolver {
445         String JavaDoc id = "http://barracudamvc.org/Barracuda";
446         List resourceList = new ArrayList();
447
448         public InputSource resolveEntity (String JavaDoc publicId, String JavaDoc systemId) {
449             if (systemId.startsWith(id)) {
450                 // return a special input source
451
String JavaDoc altId = "/xlib"+systemId.substring(id.length());
452                 InputStream is = this.getClass().getResourceAsStream(altId);
453                 BufferedInputStream in = new BufferedInputStream(is);
454                 resourceList.add(in);
455                 return new InputSource(in);
456             } else {
457                 // use the default behaviour
458
return null;
459             }
460         }
461
462         public void cleanup() {
463             Iterator it = resourceList.iterator();
464             while (it.hasNext()) {
465                 try {
466                     ((InputStream) it.next()).close();
467                 } catch (Exception JavaDoc e) {
468                     System.out.println("unexpected error: "+e);
469                 }
470             }
471         }
472     }
473 }
474
Popular Tags