001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2003 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064: package com.jcorporate.expresso.kernel.digester;
065:
066: import com.jcorporate.expresso.kernel.metadata.ComponentMetadata;
067: import com.jcorporate.expresso.kernel.util.FastStringBuffer;
068: import org.apache.commons.digester.Digester;
069: import org.apache.log4j.Logger;
070: import org.xml.sax.SAXException;
071:
072: import javax.xml.parsers.FactoryConfigurationError;
073: import javax.xml.parsers.ParserConfigurationException;
074: import java.io.IOException;
075: import java.io.InputStream;
076: import java.net.URL;
077:
078: /**
079: * This class provides a digester interface to the ComponentMetadata
080: * information
081: *
082: * @author Michael Rimov
083: * @since Expresso 5.1
084: */
085:
086: public class ComponentMetadataConfig implements java.io.Serializable {
087: /**
088: * This constant tells the limit to which we allow nesting of components.
089: * Thanks to digester not being recursive, and not wanting to write our own
090: * SAX parser, we use this kludge instead.
091: */
092: private static final int MAX_NESTING = 20;
093:
094: private static final Logger log = Logger
095: .getLogger(ComponentMetadataConfig.class);
096:
097: private ComponentMetadata root;
098:
099: private SaxParserConfigurer saxParserCoonfig = new SaxParserConfigurer();
100:
101: /**
102: * Default constructor
103: */
104: public ComponentMetadataConfig() {
105: }
106:
107: /**
108: * <p>Load component metadata information given a String location.</p>
109: * <p>Code example</p>
110: * <p><code>ComponentMetadataConfig cf = new ComponentMetadataConfig()<br />
111: * cf.loadComponentMetadata("/com/jcorporate/expresso/core/SchemaMetadata.xml"<br/>
112: * </code></p>
113: * The above example would load the component metadata from the
114: * com.jcorporate.expresso.core package.
115: *
116: * @param resourceLocation the location of the xml file to parse
117: */
118: public void loadComponentMetadata(String resourceLocation) {
119: if (resourceLocation == null) {
120: throw new IllegalArgumentException(
121: "Parameter resourceLocation must not be null");
122: }
123:
124: InputStream is = null;
125: try {
126: if (log.isDebugEnabled()) {
127: log.debug("Loading metadata from location: "
128: + resourceLocation);
129: }
130:
131: saxParserCoonfig.setSAXParser();
132: javax.xml.parsers.SAXParserFactory spf = javax.xml.parsers.SAXParserFactory
133: .newInstance();
134: javax.xml.parsers.SAXParser sp = spf.newSAXParser();
135:
136: Digester digester = new Digester(sp);
137: setupResolvers(digester);
138: setDigesterRules(digester);
139:
140: is = this .getClass().getResourceAsStream(resourceLocation);
141: digester.parse(is);
142: } catch (FactoryConfigurationError ex) {
143: log
144: .error(
145: "Fatal error trying to find a suitable Digester compatible parser.",
146: ex);
147: } catch (SAXException ex) {
148: log
149: .error(
150: "Fatal error trying to digest expresso-services.xml file",
151: ex);
152: } catch (ParserConfigurationException ex) {
153: log
154: .error(
155: "Fatal error trying to find a suitable Digester compatible parser.",
156: ex);
157: } catch (java.io.IOException ex) {
158: log.error("Fatal IO error parsing input.", ex);
159: } finally {
160: try {
161: if (is != null) {
162: is.close();
163: }
164: } catch (IOException ex) {
165: log.error("Error closing resource stream: "
166: + resourceLocation, ex);
167: }
168: }
169:
170: }
171:
172: /**
173: * Sets up the appropriate locations to resolve the Doctypes
174: *
175: * @param digester the Apache digester to register the resolvers with.
176: */
177: private void setupResolvers(Digester digester) {
178: URL url = this
179: .getClass()
180: .getResource(
181: "/com/jcorporate/expresso/kernel/expresso-metadata_5_1.dtd");
182: if (url != null) {
183: digester
184: .register(
185: "-//Jcorporate Ltd//DTD Expresso Component Metadata Configuration 5.1//EN",
186: url.toString());
187: } else {
188: throw new IllegalArgumentException("Unable to locate "
189: + "expresso-services_5_1.dtd in component package");
190: }
191: }
192:
193: /**
194: * Loads the component metadata given a URL rather than a String location
195: *
196: * @param resourceLocation the URL pointing to the location of the resource
197: * to parse.
198: */
199: public void loadComponentMetadata(URL resourceLocation) {
200: if (resourceLocation == null) {
201: throw new IllegalArgumentException(
202: "Parameter resourceLocation must not be null");
203: }
204:
205: if (log.isDebugEnabled()) {
206: log.debug("Loading metadata from location: "
207: + resourceLocation.toString());
208: }
209:
210: try {
211: saxParserCoonfig.setSAXParser();
212: javax.xml.parsers.SAXParserFactory spf = javax.xml.parsers.SAXParserFactory
213: .newInstance();
214: javax.xml.parsers.SAXParser sp = spf.newSAXParser();
215:
216: Digester digester = new Digester(sp);
217: setupResolvers(digester);
218: setDigesterRules(digester);
219:
220: digester.parse(resourceLocation.openStream());
221: } catch (FactoryConfigurationError ex) {
222: log
223: .error(
224: "Fatal error trying to find a suitable Digester compatible parser.",
225: ex);
226: } catch (SAXException ex) {
227: log
228: .error(
229: "Fatal error trying to digest expresso-services.xml file",
230: ex);
231: } catch (ParserConfigurationException ex) {
232: log
233: .error(
234: "Fatal error trying to find a suitable Digester compatible parser.",
235: ex);
236: } catch (java.io.IOException ex) {
237: log.error("Fatal IO error parsing input.", ex);
238: }
239: }
240:
241: /**
242: * <p>Set the rules for the digester<p>
243: * <p><b>TOTAL HACK WARNING!</b> Since Digester doesn't seem to allow recursive
244: * rules, we dynamically write rules for up to a maximum component depth of
245: * 20.</p>
246: *
247: * @param digester the Apache Digester to manipulate
248: */
249: protected void setDigesterRules(Digester digester) {
250: root = new ComponentMetadata();
251:
252: digester.push(root);
253: for (int i = 0; i < MAX_NESTING; i++) {
254: String componentDepthString = getComponentDepthString(i);
255: if (i > 0) {
256: digester
257: .addObjectCreate(
258: "component-metadata"
259: + componentDepthString,
260: com.jcorporate.expresso.kernel.metadata.ComponentMetadata.class);
261: }
262:
263: digester.addSetProperties("component-metadata"
264: + componentDepthString);
265:
266: digester.addCallMethod("component-metadata"
267: + componentDepthString + "/description",
268: "setDescription", 1);
269: digester.addCallParam("component-metadata"
270: + componentDepthString + "/description", 0);
271:
272: digester.addCallMethod("component-metadata"
273: + componentDepthString + "/version-info",
274: "setVersionNumber", 3);
275: digester.addCallParam("component-metadata"
276: + componentDepthString
277: + "/version-info/major-version", 0);
278: digester.addCallParam("component-metadata"
279: + componentDepthString
280: + "/version-info/minor-version", 1);
281: digester.addCallParam("component-metadata"
282: + componentDepthString
283: + "/version-info/micro-version", 2);
284:
285: digester.addCallMethod("component-metadata"
286: + componentDepthString + "/message-bundle",
287: "setMessageBundle", 1);
288: digester.addCallParam("component-metadata"
289: + componentDepthString + "/message-bundle", 0);
290:
291: digester
292: .addObjectCreate(
293: "component-metadata" + componentDepthString
294: + "/property-list/property",
295: com.jcorporate.expresso.kernel.metadata.SimpleProperty.class);
296:
297: digester.addSetProperties("component-metadata"
298: + componentDepthString + "/property-list/property");
299: digester.addCallMethod("component-metadata"
300: + componentDepthString
301: + "/property-list/property/property-valid-value",
302: "addValidValue", 2);
303: digester.addCallParam("component-metadata"
304: + componentDepthString
305: + "/property-list/property/property-valid-value",
306: 0, "name");
307: digester.addCallParam("component-metadata"
308: + componentDepthString
309: + "/property-list/property/property-valid-value",
310: 1, "value");
311: digester.addSetNext("component-metadata"
312: + componentDepthString + "/property-list/property",
313: "addProperty");
314:
315: digester
316: .addObjectCreate(
317: "component-metadata" + componentDepthString
318: + "/property-list/mapped-property",
319: com.jcorporate.expresso.kernel.metadata.MappedProperty.class);
320: digester.addSetProperties("component-metadata"
321: + componentDepthString
322: + "/property-list/mapped-property");
323: digester.addCallMethod("component-metadata"
324: + componentDepthString
325: + "/property-list/mapped-property/mapped-value",
326: "setMappedValue", 2);
327: digester.addCallParam("component-metadata"
328: + componentDepthString
329: + "/property-list/mapped-property/mapped-value", 0,
330: "key");
331: digester.addCallParam("component-metadata"
332: + componentDepthString
333: + "/property-list/mapped-property/mapped-value", 1,
334: "value");
335: digester.addSetNext("component-metadata"
336: + componentDepthString
337: + "/property-list/mapped-property", "addProperty");
338:
339: digester
340: .addObjectCreate(
341: "component-metadata" + componentDepthString
342: + "/property-list/indexed-property",
343: com.jcorporate.expresso.kernel.metadata.IndexedProperty.class);
344: digester.addSetProperties("component-metadata"
345: + componentDepthString
346: + "/property-list/indexed-property");
347: digester
348: .addCallMethod(
349: "component-metadata"
350: + componentDepthString
351: + "/property-list/indexed-property/indexed-property-value",
352: "setIndexedValue", 2);
353: digester
354: .addCallParam(
355: "component-metadata"
356: + componentDepthString
357: + "/property-list/indexed-property/indexed-property-value",
358: 0, "index");
359: digester
360: .addCallParam(
361: "component-metadata"
362: + componentDepthString
363: + "/property-list/indexed-property/indexed-property-value",
364: 1, "value");
365: digester.addSetNext("component-metadata"
366: + componentDepthString
367: + "/property-list/indexed-property", "addProperty");
368:
369: digester
370: .addObjectCreate(
371: "component-metadata" + componentDepthString
372: + "/method-list/method",
373: com.jcorporate.expresso.kernel.metadata.Method.class);
374: digester
375: .addSetProperties("component-metadata"
376: + componentDepthString
377: + "/method-list/method", new String[] {
378: "name", "return-type", "description" },
379: new String[] { "name", "returnType",
380: "description" });
381: digester.addCallMethod("component-metadata"
382: + componentDepthString
383: + "/method-list/method/method-arg", "addArgument",
384: 2);
385: digester.addCallParam("component-metadata"
386: + componentDepthString
387: + "/method-list/method/method-arg", 0, "name");
388: digester.addCallParam("component-metadata"
389: + componentDepthString
390: + "/method-list/method/method-arg", 1, "type");
391: digester.addSetNext("component-metadata"
392: + componentDepthString + "/method-list/method",
393: "addMethod");
394:
395: //
396: //Schema data
397: //
398: digester
399: .addObjectCreate(
400: "component-metadata" + componentDepthString
401: + "/schema",
402: com.jcorporate.expresso.kernel.metadata.SchemaData.class);
403:
404: digester
405: .addObjectCreate(
406: "component-metadata" + componentDepthString
407: + "/schema/dbobject",
408: com.jcorporate.expresso.kernel.metadata.DBObjectData.class);
409: digester.addSetProperties("component-metadata"
410: + componentDepthString
411: + "/schema/dbobject/classname",
412: new String[] { "name" },
413: new String[] { "className" });
414: digester.addSetNext("component-metadata"
415: + componentDepthString + "/schema/dbobject",
416: "addDataObject");
417:
418: digester
419: .addObjectCreate(
420: "component-metadata" + componentDepthString
421: + "/schema/controller",
422: com.jcorporate.expresso.kernel.metadata.ControllerData.class);
423: digester.addSetProperties("component-metadata"
424: + componentDepthString
425: + "/schema/controller/classname",
426: new String[] { "name" },
427: new String[] { "className" });
428: digester.addSetNext("component-metadata"
429: + componentDepthString + "/schema/controller",
430: "addController");
431:
432: digester
433: .addObjectCreate(
434: "component-metadata" + componentDepthString
435: + "/schema/job",
436: com.jcorporate.expresso.kernel.metadata.JobData.class);
437: digester.addSetProperties("component-metadata"
438: + componentDepthString + "/schema/job/classname",
439: new String[] { "name" },
440: new String[] { "className" });
441: digester.addSetNext("component-metadata"
442: + componentDepthString + "/schema/job", "addJob");
443:
444: digester
445: .addObjectCreate(
446: "component-metadata" + componentDepthString
447: + "/schema/report",
448: com.jcorporate.expresso.kernel.metadata.ReportData.class);
449: digester.addSetProperties(
450: "component-metadata" + componentDepthString
451: + "/schema/report/classname",
452: new String[] { "name" },
453: new String[] { "className" });
454: digester.addSetNext("component-metadata"
455: + componentDepthString + "/schema/report",
456: "addReportObject");
457:
458: digester
459: .addSetNext("component-metadata"
460: + componentDepthString + "/schema",
461: "setSchemaData");
462:
463: if (i > 0) {
464: digester.addSetNext("component-metadata"
465: + componentDepthString, "addChildComponent");
466: }
467: }
468:
469: }
470:
471: /**
472: * Internal string to assist int hacking the digester rules....
473: *
474: * @param index how many times to repeat the rules string
475: * @return java.lang.String
476: */
477: private String getComponentDepthString(int index) {
478: FastStringBuffer fsb = FastStringBuffer.getInstance();
479: for (int i = 0; i < index; i++) {
480: if (i == 0) {
481: fsb.append("/embedded-components");
482: }
483: fsb.append("/component-metadata");
484: }
485:
486: String returnValue = fsb.toString();
487: fsb.release();
488:
489: return returnValue;
490: }
491:
492: /**
493: * Retrieve the componentMetadata that has been built via a call to
494: * loadComponentMetadata
495: *
496: * @return ComponentMetadata
497: */
498: public ComponentMetadata getMetadata() {
499: return root;
500: }
501:
502: }
|