001: /**
002: * Redistribution and use of this software and associated documentation
003: * ("Software"), with or without modification, are permitted provided
004: * that the following conditions are met:
005: *
006: * 1. Redistributions of source code must retain copyright
007: * statements and notices. Redistributions must also contain a
008: * copy of this document.
009: *
010: * 2. Redistributions in binary form must reproduce the
011: * above copyright notice, this list of conditions and the
012: * following disclaimer in the documentation and/or other
013: * materials provided with the distribution.
014: *
015: * 3. The name "Exolab" must not be used to endorse or promote
016: * products derived from this Software without prior written
017: * permission of Intalio, Inc. For written permission,
018: * please contact info@exolab.org.
019: *
020: * 4. Products derived from this Software may not be called "Exolab"
021: * nor may "Exolab" appear in their names without prior written
022: * permission of Intalio, Inc. Exolab is a registered
023: * trademark of Intalio, Inc.
024: *
025: * 5. Due credit should be given to the Exolab Project
026: * (http://www.exolab.org/).
027: *
028: * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
029: * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
030: * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
031: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
032: * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
033: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
034: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
035: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
036: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
037: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
038: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
039: * OF THE POSSIBILITY OF SUCH DAMAGE.
040: *
041: * Copyright 1999-2003 (C) Intalio, Inc. All Rights Reserved.
042: *
043: * $Id: LocalConfiguration.java 6980 2007-05-06 15:05:25Z wguttmn $
044: */package org.exolab.castor.util;
045:
046: import java.io.File;
047: import java.io.FileInputStream;
048: import java.io.FileNotFoundException;
049: import java.io.IOException;
050: import java.io.InputStream;
051: import java.io.OutputStream;
052: import java.io.Writer;
053: import java.net.URL;
054: import java.util.Hashtable;
055: import java.util.Properties;
056: import java.util.StringTokenizer;
057:
058: import javax.xml.parsers.ParserConfigurationException;
059: import javax.xml.parsers.SAXParser;
060: import javax.xml.parsers.SAXParserFactory;
061:
062: import org.apache.commons.logging.Log;
063: import org.apache.commons.logging.LogFactory;
064: import org.castor.util.Messages;
065: import org.exolab.castor.xml.NodeType;
066: import org.exolab.castor.xml.OutputFormat;
067: import org.exolab.castor.xml.Serializer;
068: import org.exolab.castor.xml.XMLNaming;
069: import org.exolab.castor.xml.util.DefaultNaming;
070: import org.xml.sax.DocumentHandler;
071: import org.xml.sax.Parser;
072: import org.xml.sax.SAXException;
073: import org.xml.sax.XMLReader;
074:
075: /**
076: * Provides default configuration for Castor components from the
077: * <tt>castor.properties</tt> configuration file. All Castor features
078: * rely on the central configuration file.
079: * <p>
080: * The configuration file is loaded from the Java <tt>lib</tt>
081: * directory, the classpath and the Castor JAR. Properties set in the
082: * classpath file takes precedence over properties set in the Java library
083: * configuration file and properties set in the Castor JAR, allowing for
084: * each customization. All three files are named <tt>castor.properties</tt>.
085: * <p>
086: * For example, to change the parser in use, specify that all
087: * documents should be printed with identantion or turn debugging on,
088: * create a new configuration file in the current directory, instead
089: * of modifying the global one.
090: *
091: * @author <a href="mailto:kvisco@intalio.com">Keith Visco</a>
092: * @author <a href="mailto:arkin@intalio.com">Assaf Arkin</a>
093: * @version $Revision: 6980 $ $Date: 2006-04-25 15:08:23 -0600 (Tue, 25 Apr 2006) $
094: */
095: public final class LocalConfiguration extends Configuration {
096:
097: /**
098: * The <a href="http://jakarta.apache.org/commons/logging/">Jakarta
099: * Commons Logging</a> instance used for all logging.
100: */
101: private static final Log _log = LogFactory.getFactory()
102: .getInstance(LocalConfiguration.class);
103:
104: /**
105: * The properties loaded from the local configuration file.
106: */
107: private Properties _props;
108:
109: /**
110: * The local configuration values (retrieved from a local properties file)
111: */
112: private ConfigValues _values = new ConfigValues();
113:
114: /**
115: * Cache of loaded configurations
116: */
117: private static Hashtable _configurations = new Hashtable();
118:
119: /**
120: * The full url to the castor.properties file that was loaded
121: */
122: private String _resourceUrl = null;
123:
124: /**
125: * Creates a new instance of LocalConfiguration
126: */
127: public LocalConfiguration() {
128: super ();
129: load();
130: //-- Disable for now, not needed yet until we work on
131: //-- a way to differentiate different configurations
132: //-- throughout the framework...
133: //String key = (_resourceUrl == null) ? "" : _resourceUrl;
134: //_configurations.put(key, this);
135: } //-- LocalConfiguration
136:
137: /**
138: * Returns an instance of the LocalConfiguration, if a previous
139: * configuration has already been loaded for local resource,
140: * it will be returned.
141: *
142: * @return the LocalConfiguration
143: */
144: public static synchronized LocalConfiguration getInstance() {
145:
146: LocalConfiguration config = null;
147: URL url = null;
148:
149: //-- disable for now, it's a huge performance hit, for no gain,
150: //-- at the moment anyway. We need to come up with a clean, fast
151: //-- way to differentiate between different configurations
152: //-- throughout the framework...
153: //url = LocalConfiguration.class.getResource("/" + Property.CONFIG_FILENAME_PROPERTY);
154:
155: String key = "";
156: if (url != null) {
157: key = url.toString();
158: }
159: config = (LocalConfiguration) _configurations.get(key);
160: if (config == null) {
161: config = new LocalConfiguration();
162: _configurations.put(key, config);
163: }
164: return config;
165:
166: } //-- getInstance
167:
168: /**
169: * Returns true if the current configuration has enabled debugging.
170: *
171: * @return true if the current configuration has enabled debugging,
172: * otherwise false.
173: */
174: public boolean debug() {
175: getProperties();
176: return _values.debug;
177:
178: } //-- debug
179:
180: /**
181: * Access to the property specifying whether to apply strictness to elements when
182: * unmarshalling. Default is true which means that elements appearing in the
183: * XML Documnt which cannot be mapped to a class cause a SAXException to be thrown.
184: * If set to false, these 'unknown' elements are ignored
185: *
186: * @return true if element processing should be "strict".
187: */
188: public boolean strictElements() {
189: getProperties();
190: return _values.strictElements;
191: } //-- strictElements
192:
193: /**
194: * Returns true if the default configuration specified validation in
195: * the marshalling framework.
196: */
197: public boolean marshallingValidation() {
198: getProperties();
199: return _values.marshallingValidation;
200:
201: } //-- marshallingValidation
202:
203: /**
204: * Returns the current properties from the configuration file(s).
205: * The Properties returned may be empty, but never null.
206: *
207: * @return The current set of configuration properties.
208: */
209: public synchronized Properties getProperties() {
210: if (_props == null) {
211: load();
212: }
213: return _props;
214: }
215:
216: /**
217: * Returns the naming conventions to use for the XML framework
218: *
219: * @return the naming conventions to use for the XML framework
220: */
221: public XMLNaming getXMLNaming() {
222: return getXMLNaming(null);
223: }
224:
225: /**
226: * Returns the naming conventions to use for the XML framework
227: *
228: * @return the naming conventions to use for the XML framework
229: */
230: public XMLNaming getXMLNaming(ClassLoader classLoader) {
231:
232: if (_values.naming != null)
233: return _values.naming;
234:
235: String prop = getProperty(Property.Naming, null);
236: if ((prop == null) || (prop.equalsIgnoreCase("lower"))) {
237: _values.naming = new DefaultNaming();
238: } else if (prop.equalsIgnoreCase("mixed")) {
239: DefaultNaming dn = new DefaultNaming();
240: dn.setStyle(DefaultNaming.MIXED_CASE_STYLE);
241: _values.naming = dn;
242: } else {
243: try {
244: Class cls = null;
245: if (classLoader != null) {
246: cls = classLoader.loadClass(prop);
247: } else {
248: cls = Class.forName(prop);
249: }
250: _values.naming = (XMLNaming) cls.newInstance();
251: } catch (Exception except) {
252: throw new RuntimeException("Failed to load XMLNaming: "
253: + except);
254: }
255: }
256: return _values.naming;
257: } //-- getXMLNaming
258:
259: /**
260: * Return an XML document parser implementing the feature list
261: * specified in the configuration file.
262: *
263: * @return A suitable XML parser
264: */
265: public Parser getParser() {
266: return getParser(null);
267: }
268:
269: /**
270: * Returns an XML document parser implementing the requested
271: * set of features. The feature list is a comma separated list
272: * of features that parser may or may not support. No errors are
273: * generated for unsupported features. If the feature list is not
274: * null, it overrides the default feature list specified in the
275: * configuration file, including validation and Namespaces.
276: *
277: * @param features The requested feature list, null for the
278: * defaults
279: * @return A suitable XML parser
280: */
281: public Parser getParser(final String features) {
282: String prop;
283: Parser parser;
284:
285: //-- validation?
286: prop = getProperties().getProperty(Property.ParserValidation,
287: "false");
288: boolean validation = (prop.equalsIgnoreCase("true") || prop
289: .equalsIgnoreCase("on"));
290:
291: //-- namespaces?
292: prop = getProperties()
293: .getProperty(Property.Namespaces, "false");
294: boolean namespaces = (prop.equalsIgnoreCase("true") || prop
295: .equalsIgnoreCase("on"));
296:
297: //-- which parser?
298: prop = getProperties().getProperty(Property.Parser);
299: if ((prop == null) || (prop.length() == 0)) {
300: // If no parser class was specified, check for JAXP
301: // otherwise we default to Xerces.
302: SAXParserFactory factory = SAXParserFactory.newInstance();
303: factory.setNamespaceAware(namespaces);
304: factory.setValidating(validation);
305: try {
306: SAXParser saxParser = factory.newSAXParser();
307: return saxParser.getParser();
308: } catch (ParserConfigurationException pcx) {
309: _log.error(Messages.format("conf.configurationError",
310: pcx));
311: } catch (org.xml.sax.SAXException sx) {
312: _log.error(Messages.format("conf.configurationError",
313: sx));
314: }
315:
316: }
317:
318: if ((prop == null) || (prop.length() == 0)
319: || (prop.equalsIgnoreCase("xerces"))) {
320: prop = "org.apache.xerces.parsers.SAXParser";
321: }
322:
323: // If a parser class was specified, we try to create it and
324: // complain about creation error.
325: try {
326: Class cls;
327:
328: cls = Class.forName(prop);
329: parser = (Parser) cls.newInstance();
330: } catch (Exception except) {
331: throw new RuntimeException(Messages.format(
332: "conf.failedInstantiateParser", prop, except));
333: }
334:
335: if (parser instanceof XMLReader) {
336: XMLReader xmlReader = (XMLReader) parser;
337: setFeaturesOnXmlReader(getProperties(), features,
338: validation, namespaces, xmlReader);
339: }
340:
341: return parser;
342:
343: }
344:
345: /**
346: * Returns an XML document parser implementing the requested
347: * set of features. The feature list is a comma separated list
348: * of features that parser may or may not support. No errors are
349: * generated for unsupported features. If the feature list is not
350: * null, it overrides the default feature list specified in the
351: * configuration file, including validation and Namespaces.
352: *
353: * @return A suitable XML parser
354: */
355: public XMLReader getXMLReader() {
356: return getXMLReader(null);
357:
358: } //-- getXMLReader
359:
360: /**
361: * Returns an XML document parser implementing the requested
362: * set of features. The feature list is a comma separated list
363: * of features that parser may or may not support. No errors are
364: * generated for unsupported features. If the feature list is not
365: * null, it overrides the default feature list specified in the
366: * configuration file, including validation and Namespaces.
367: *
368: * @return A suitable XML parser
369: */
370: public XMLReader getXMLReader(String features) {
371:
372: String prop;
373: XMLReader reader = null;
374:
375: //-- validation?
376: prop = getProperties().getProperty(Property.ParserValidation,
377: "false");
378: boolean validation = (prop.equalsIgnoreCase("true") || prop
379: .equalsIgnoreCase("on"));
380:
381: //-- namespaces?
382: prop = getProperties()
383: .getProperty(Property.Namespaces, "false");
384: boolean namespaces = (prop.equalsIgnoreCase("true") || prop
385: .equalsIgnoreCase("on"));
386:
387: //-- which parser?
388: prop = getProperties().getProperty(Property.Parser);
389: if ((prop == null) || (prop.length() == 0)) {
390: // If no parser class was specified, check for JAXP
391: // otherwise we default to Xerces.
392: SAXParserFactory factory = SAXParserFactory.newInstance();
393: factory.setNamespaceAware(namespaces);
394: factory.setValidating(validation);
395: try {
396: SAXParser saxParser = factory.newSAXParser();
397: reader = saxParser.getXMLReader();
398: } catch (ParserConfigurationException pcx) {
399: _log.error(Messages.format("conf.configurationError",
400: pcx));
401: } catch (org.xml.sax.SAXException sx) {
402: _log.error(Messages.format("conf.configurationError",
403: sx));
404: }
405:
406: }
407:
408: if (reader == null) {
409: if ((prop == null) || (prop.length() == 0)
410: || (prop.equalsIgnoreCase("xerces"))) {
411: prop = "org.apache.xerces.parsers.SAXParser";
412: }
413:
414: // If a parser class was specified, we try to create it and
415: // complain about creation error.
416: try {
417: Class cls;
418:
419: cls = Class.forName(prop);
420: reader = (XMLReader) cls.newInstance();
421: } catch (Exception except) {
422: throw new RuntimeException(Messages.format(
423: "conf.failedInstantiateParser", prop, except));
424: }
425: }
426:
427: setFeaturesOnXmlReader(getProperties(), features, validation,
428: namespaces, reader);
429:
430: return reader;
431:
432: } //-- getXMLReader
433:
434: /**
435: * Returns the NodeType to use for Java primitives.
436: * A null value will be returned if no NodeType was specified,
437: * indicating the default NodeType should be used.
438: *
439: * @return the NodeType assigned to Java primitives, or null
440: * if no NodeType was specified.
441: **/
442: public NodeType getPrimitiveNodeType() {
443:
444: if (_values.primitiveNodeType != null)
445: return _values.primitiveNodeType;
446:
447: String prop = getProperty(Property.PrimitiveNodeType, null);
448: if (prop == null)
449: return null;
450: _values.primitiveNodeType = NodeType.getNodeType(prop);
451: return _values.primitiveNodeType;
452: } //-- getPrimitiveNodeType
453:
454: /**
455: * Returns a new instance of the specified Regular Expression
456: * Evaluator, or null if no validator was specified
457: *
458: * @return the regular expression evaluator,
459: *
460: **/
461: public RegExpEvaluator getRegExpEvaluator() {
462:
463: String prop = getProperties().getProperty(Property.RegExp);
464:
465: RegExpEvaluator regex = null;
466:
467: if (prop == null)
468: return null;
469: try {
470: if (_values.regExpEvalClass == null) {
471: _values.regExpEvalClass = Class.forName(prop);
472: }
473: regex = (RegExpEvaluator) _values.regExpEvalClass
474: .newInstance();
475: } catch (Exception except) {
476: throw new RuntimeException(Messages.format(
477: "conf.failedInstantiateRegExp", prop, except));
478: }
479:
480: return regex;
481: } //-- getRegExpEvaluator
482:
483: /**
484: * Returns a default serializer for producing an XML document.
485: * The caller can specify an alternative output format, may reuse
486: * this serializer across several streams, and may serialize both
487: * DOM and SAX events. If such control is not required, it is
488: * recommended to call one of the other two methods.
489: *
490: * @return A suitable serializer
491: */
492: public Serializer getSerializer() {
493: Serializer serializer = getSerializerFactory(_props)
494: .getSerializer();
495: serializer.setOutputFormat(getOutputFormat());
496: return serializer;
497: }
498:
499: /**
500: * Returns the default OutputFormat for use with a Serializer.
501: *
502: * @return the default OutputFormat
503: **/
504: public OutputFormat getOutputFormat() {
505:
506: boolean indent = false;
507:
508: String prop = _props.getProperty(Property.Indent, "");
509:
510: //-- get default indentation
511: indent = (prop.equalsIgnoreCase(TRUE_VALUE) || prop
512: .equalsIgnoreCase(ON_VALUE));
513:
514: OutputFormat format = getSerializerFactory(_props)
515: .getOutputFormat();
516: format.setMethod(OutputFormat.XML);
517: format.setIndenting(indent);
518:
519: // There is a bad interaction between the indentation and the
520: // setPreserveSpace option. The indentated output is strangely indented.
521: if (!indent)
522: format.setPreserveSpace(true);
523:
524: return format;
525: } //-- getOutputFormat
526:
527: /**
528: * Returns a default serializer for producing an XML document to
529: * the designated output stream using the default serialization
530: * format.
531: *
532: * @param output The output stream
533: * @return A suitable serializer
534: */
535: public DocumentHandler getSerializer(OutputStream output)
536: throws IOException {
537: Serializer serializer;
538: DocumentHandler docHandler;
539:
540: serializer = getSerializer();
541: serializer.setOutputByteStream(output);
542: docHandler = serializer.asDocumentHandler();
543: if (docHandler == null)
544: throw new RuntimeException(Messages.format(
545: "conf.serializerNotSaxCapable", serializer
546: .getClass().getName()));
547: return docHandler;
548: }
549:
550: /**
551: * Returns a default serializer for producing an XML document to
552: * the designated output stream using the default serialization
553: * format.
554: *
555: * @param output The output stream
556: * @return A suitable serializer
557: */
558: public DocumentHandler getSerializer(Writer output)
559: throws IOException {
560: Serializer serializer;
561: DocumentHandler docHandler;
562:
563: serializer = getSerializer();
564: serializer.setOutputCharStream(output);
565: docHandler = serializer.asDocumentHandler();
566: if (docHandler == null)
567: throw new RuntimeException(Messages.format(
568: "conf.serializerNotSaxCapable", serializer
569: .getClass().getName()));
570: return docHandler;
571: }
572:
573: /**
574: * Indicates whether validation for sequence order should be lenient.
575: *
576: * @return True if sequence order validation should be lenient.
577: */
578: public boolean getLenientSequenceOrder() {
579: return Boolean.valueOf(
580: getProperties().getProperty(
581: Property.LenientSequenceOrder, "false"))
582: .booleanValue();
583: }
584:
585: /**
586: * Indicates whether id/href validation should be lenient.
587: *
588: * @return True if id/href validation should be lenient.
589: */
590: public boolean getLenientIdValidation() {
591: return Boolean.valueOf(
592: getProperties().getProperty(
593: Property.LenientIdValidation, "false"))
594: .booleanValue();
595: }
596:
597: /**
598: * Calls {@link #getDefault()} to load the configuration the
599: * first time and then looks for a local configuration to
600: * merge in with the defaults. Will not complain about inability
601: * to load local configuration file from one of the default
602: * directories, but if it cannot find the JAR's configuration file,
603: * will throw a run time exception.
604: */
605: protected void load() {
606: //-- load default properties
607: _props = new Properties(getDefault());
608:
609: try {
610: loadProperties(Property.FileName);
611: } catch (FileNotFoundException fnfe) {
612: //-- Ignore, simply no user supplied castor.properties file.
613: //-- If no default castor.properties is found an exception
614: //-- will occur in the above call to super.load.
615: }
616:
617: String prop;
618: prop = _props.getProperty(Property.Debug, "");
619: if (prop.equalsIgnoreCase("true")
620: || prop.equalsIgnoreCase("on"))
621: _values.debug = true;
622: prop = _props.getProperty(Property.MarshallingValidation, "");
623: if (prop.equalsIgnoreCase("false")
624: || prop.equalsIgnoreCase("off"))
625: _values.marshallingValidation = false;
626:
627: prop = _props.getProperty(Property.StrictElements, "");
628: if (prop.equalsIgnoreCase("false")
629: || prop.equalsIgnoreCase("off"))
630: _values.strictElements = false;
631: else
632: _values.strictElements = true;
633:
634: prop = null;
635: }
636:
637: /**
638: * Load the configuration will not complain about inability to load
639: * configuration file from one of the default directories, but if
640: * it cannot find the JAR's configuration file, will throw a
641: * run time exception.
642: */
643: public void loadProperties(String fileOrResourceName)
644: throws FileNotFoundException {
645:
646: boolean found = false;
647:
648: // Get overriding configuration from the classpath,
649: // ignore if not found. If found, merge any existing
650: // properties.
651: try {
652: URL url = getClass().getResource("/" + fileOrResourceName);
653: if (url != null) {
654: _resourceUrl = url.toString();
655: if (_log.isDebugEnabled()) {
656: _log
657: .debug("Trying to load configuration file from "
658: + _resourceUrl);
659: }
660: _props.load(url.openStream());
661: //-- debug information
662: //System.out.println("merging local configuration: " + url.toExternalForm());
663: //-- end debug information
664: found = true;
665: }
666: } catch (Exception except) {
667: // Do nothing
668: }
669:
670: //-- if not found, either it doesn't exist, or "." is not part of the
671: //-- class path, try looking at local working directory
672: if (!found) {
673: try {
674: File file = new File(fileOrResourceName);
675: if (file.exists() && file.canRead()) {
676: InputStream is = new FileInputStream(file);
677: _props.load(is);
678: is.close();
679: found = true;
680: }
681: } catch (Exception except) {
682: //-- do nothing
683: }
684: }
685:
686: //-- Cannot find any castor.properties file(s).
687: //if (!found) {
688: // throw new FileNotFoundException(fileOrResourceName);
689: //}
690: }
691:
692: } //-- LocalConfiguration
|