KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > beans > factory > xml > XmlBeanDefinitionReader


1 /*
2  * Copyright 2002-2007 the original author or authors.
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
17 package org.springframework.beans.factory.xml;
18
19 import java.io.IOException JavaDoc;
20 import java.io.InputStream JavaDoc;
21
22 import javax.xml.parsers.ParserConfigurationException JavaDoc;
23
24 import org.w3c.dom.Document JavaDoc;
25 import org.xml.sax.EntityResolver JavaDoc;
26 import org.xml.sax.ErrorHandler JavaDoc;
27 import org.xml.sax.InputSource JavaDoc;
28 import org.xml.sax.SAXException JavaDoc;
29 import org.xml.sax.SAXParseException JavaDoc;
30
31 import org.springframework.beans.BeanUtils;
32 import org.springframework.beans.factory.BeanDefinitionStoreException;
33 import org.springframework.beans.factory.parsing.EmptyReaderEventListener;
34 import org.springframework.beans.factory.parsing.FailFastProblemReporter;
35 import org.springframework.beans.factory.parsing.NullSourceExtractor;
36 import org.springframework.beans.factory.parsing.ProblemReporter;
37 import org.springframework.beans.factory.parsing.ReaderEventListener;
38 import org.springframework.beans.factory.parsing.SourceExtractor;
39 import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
40 import org.springframework.beans.factory.support.BeanDefinitionRegistry;
41 import org.springframework.core.Constants;
42 import org.springframework.core.io.DescriptiveResource;
43 import org.springframework.core.io.Resource;
44 import org.springframework.core.io.support.EncodedResource;
45 import org.springframework.util.Assert;
46 import org.springframework.util.ClassUtils;
47 import org.springframework.util.xml.SimpleSaxErrorHandler;
48 import org.springframework.util.xml.XmlValidationModeDetector;
49
50 /**
51  * Bean definition reader for XML bean definitions.
52  * Delegates the actual XML document reading to an implementation
53  * of the {@link BeanDefinitionDocumentReader} interface.
54  *
55  * <p>Typically applied to a
56  * {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
57  * or a {@link org.springframework.context.support.GenericApplicationContext}.
58  *
59  * <p>This class loads a DOM document and applies the BeanDefinitionDocumentReader to it.
60  * The document reader will register each bean definition with the given bean factory,
61  * talking to the latter's implementation of the
62  * {@link org.springframework.beans.factory.support.BeanDefinitionRegistry} interface.
63  *
64  * @author Juergen Hoeller
65  * @author Rob Harrop
66  * @since 26.11.2003
67  * @see #setDocumentReaderClass
68  * @see BeanDefinitionDocumentReader
69  * @see DefaultBeanDefinitionDocumentReader
70  * @see BeanDefinitionRegistry
71  * @see org.springframework.beans.factory.support.DefaultListableBeanFactory
72  * @see org.springframework.context.support.GenericApplicationContext
73  */

74 public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
75
76     /**
77      * Indicates that the validation should be disabled.
78      */

79     public static final int VALIDATION_NONE = 0;
80
81     /**
82      * Indicates that the validation mode should be detected automatically.
83      */

84     public static final int VALIDATION_AUTO = XmlValidationModeDetector.VALIDATION_AUTO;
85
86     /**
87      * Indicates that DTD validation should be used.
88      */

89     public static final int VALIDATION_DTD = XmlValidationModeDetector.VALIDATION_DTD;
90
91     /**
92      * Indicates that XSD validation should be used.
93      */

94     public static final int VALIDATION_XSD = XmlValidationModeDetector.VALIDATION_XSD;
95
96
97     /** Constants instance for this class */
98     private static final Constants constants = new Constants(XmlBeanDefinitionReader.class);
99
100     private boolean namespaceAware;
101
102     private int validationMode = VALIDATION_AUTO;
103
104     private Class JavaDoc parserClass;
105
106     private Class JavaDoc documentReaderClass = DefaultBeanDefinitionDocumentReader.class;
107
108     private ProblemReporter problemReporter = new FailFastProblemReporter();
109
110     private ReaderEventListener eventListener = new EmptyReaderEventListener();
111
112     private SourceExtractor sourceExtractor = new NullSourceExtractor();
113
114     private NamespaceHandlerResolver namespaceHandlerResolver;
115
116     private DocumentLoader documentLoader = new DefaultDocumentLoader();
117
118     private EntityResolver JavaDoc entityResolver;
119
120     private ErrorHandler JavaDoc errorHandler = new SimpleSaxErrorHandler(logger);
121
122     private final XmlValidationModeDetector validationModeDetector = new XmlValidationModeDetector();
123
124
125     /**
126      * Create new XmlBeanDefinitionReader for the given bean factory.
127      * @param beanFactory the BeanFactory to load bean definitions into,
128      * in the form of a BeanDefinitionRegistry
129      */

130     public XmlBeanDefinitionReader(BeanDefinitionRegistry beanFactory) {
131         super(beanFactory);
132
133         // Determine EntityResolver to use.
134
if (getResourceLoader() != null) {
135             this.entityResolver = new ResourceEntityResolver(getResourceLoader());
136         }
137         else {
138             this.entityResolver = new DelegatingEntityResolver(ClassUtils.getDefaultClassLoader());
139         }
140     }
141
142
143     /**
144      * Set whether or not the XML parser should be XML namespace aware.
145      * Default is "false".
146      */

147     public void setNamespaceAware(boolean namespaceAware) {
148         this.namespaceAware = namespaceAware;
149     }
150
151     /**
152      * Set if the XML parser should validate the document and thus enforce a DTD.
153      * @deprecated as of Spring 2.0: superseded by "validationMode"
154      * @see #setValidationMode
155      */

156     public void setValidating(boolean validating) {
157         this.validationMode = (validating ? VALIDATION_AUTO : VALIDATION_NONE);
158     }
159
160     /**
161      * Set the validation mode to use by name. Defaults to {@link #VALIDATION_AUTO}.
162      */

163     public void setValidationModeName(String JavaDoc validationModeName) {
164         setValidationMode(constants.asNumber(validationModeName).intValue());
165     }
166
167     /**
168      * Set the validation mode to use. Defaults to {@link #VALIDATION_AUTO}.
169      */

170     public void setValidationMode(int validationMode) {
171         this.validationMode = validationMode;
172     }
173
174     /**
175      * Specify which {@link org.springframework.beans.factory.parsing.ProblemReporter} to use. Default implementation is
176      * {@link org.springframework.beans.factory.parsing.FailFastProblemReporter} which exhibits fail fast behaviour. External tools
177      * can provide an alternative implementation that collates errors and warnings for
178      * display in the tool UI.
179      */

180     public void setProblemReporter(ProblemReporter problemReporter) {
181         this.problemReporter = (problemReporter != null ? problemReporter : new FailFastProblemReporter());
182     }
183
184     /**
185      * Specify which {@link ReaderEventListener} to use. Default implementation is
186      * EmptyReaderEventListener which discards every event notification. External tools
187      * can provide an alternative implementation to monitor the components being registered
188      * in the BeanFactory.
189      */

190     public void setEventListener(ReaderEventListener eventListener) {
191         this.eventListener = (eventListener != null ? eventListener : new EmptyReaderEventListener());
192     }
193
194     /**
195      * Specify the {@link SourceExtractor} to use. The default implementation is
196      * {@link NullSourceExtractor} which simply returns <code>null</code> as the source object.
197      * This means that during normal runtime execution no additional source metadata is attached
198      * to the bean configuration metadata.
199      */

200     public void setSourceExtractor(SourceExtractor sourceExtractor) {
201         this.sourceExtractor = (sourceExtractor != null ? sourceExtractor : new NullSourceExtractor());
202     }
203
204     /**
205      * Specify the {@link NamespaceHandlerResolver} to use. If none is specified a default
206      * instance will be created by {@link #createDefaultNamespaceHandlerResolver()}.
207      */

208     public void setNamespaceHandlerResolver(NamespaceHandlerResolver namespaceHandlerResolver) {
209         this.namespaceHandlerResolver = namespaceHandlerResolver;
210     }
211
212     /**
213      * Specify the {@link DocumentLoader} to use. The default implementation is
214      * {@link DefaultDocumentLoader} which loads {@link Document} instances using JAXP.
215      */

216     public void setDocumentLoader(DocumentLoader documentLoader) {
217         this.documentLoader = (documentLoader != null ? documentLoader : new DefaultDocumentLoader());
218     }
219
220     /**
221      * Set a SAX entity resolver to be used for parsing. By default,
222      * BeansDtdResolver will be used. Can be overridden for custom entity
223      * resolution, for example relative to some specific base path.
224      * @see BeansDtdResolver
225      */

226     public void setEntityResolver(EntityResolver JavaDoc entityResolver) {
227         this.entityResolver = entityResolver;
228     }
229
230     /**
231      * Set an implementation of the <code>org.xml.sax.ErrorHandler</code>
232      * interface for custom handling of XML parsing errors and warnings.
233      * <p>If not set, a default SimpleSaxErrorHandler is used that simply
234      * logs warnings using the logger instance of the view class,
235      * and rethrows errors to discontinue the XML transformation.
236      * @see SimpleSaxErrorHandler
237      */

238     public void setErrorHandler(ErrorHandler JavaDoc errorHandler) {
239         this.errorHandler = errorHandler;
240     }
241
242     /**
243      * Set the XmlBeanDefinitionParser implementation to use,
244      * responsible for the actual parsing of XML bean definitions.
245      * @deprecated as of Spring 2.0: superseded by "documentReaderClass"
246      * @see #setDocumentReaderClass
247      * @see XmlBeanDefinitionParser
248      */

249     public void setParserClass(Class JavaDoc parserClass) {
250         if (this.parserClass == null || !XmlBeanDefinitionParser.class.isAssignableFrom(parserClass)) {
251             throw new IllegalArgumentException JavaDoc("'parserClass' must be an XmlBeanDefinitionParser");
252         }
253         this.parserClass = parserClass;
254     }
255
256     /**
257      * Specify the BeanDefinitionDocumentReader implementation to use,
258      * responsible for the actual reading of the XML bean definition document.
259      * <p>Default is DefaultBeanDefinitionDocumentReader.
260      * @param documentReaderClass the desired BeanDefinitionDocumentReader implementation class
261      * @see BeanDefinitionDocumentReader
262      * @see DefaultBeanDefinitionDocumentReader
263      */

264     public void setDocumentReaderClass(Class JavaDoc documentReaderClass) {
265         if (documentReaderClass == null || !BeanDefinitionDocumentReader.class.isAssignableFrom(documentReaderClass)) {
266             throw new IllegalArgumentException JavaDoc(
267                     "documentReaderClass must be an implementation of the BeanDefinitionDocumentReader interface");
268         }
269         this.documentReaderClass = documentReaderClass;
270     }
271
272
273     /**
274      * Load bean definitions from the specified XML file.
275      * @param resource the resource descriptor for the XML file
276      * @return the number of bean definitions found
277      * @throws BeanDefinitionStoreException in case of loading or parsing errors
278      */

279     public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
280         return loadBeanDefinitions(new EncodedResource(resource));
281     }
282
283     /**
284      * Load bean definitions from the specified XML file.
285      * @param encodedResource the resource descriptor for the XML file,
286      * allowing to specify an encoding to use for parsing the file
287      * @return the number of bean definitions found
288      * @throws BeanDefinitionStoreException in case of loading or parsing errors
289      */

290     public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
291         Assert.notNull(encodedResource, "EncodedResource must not be null");
292         if (logger.isInfoEnabled()) {
293             logger.info("Loading XML bean definitions from " + encodedResource.getResource());
294         }
295
296         try {
297             InputStream JavaDoc inputStream = encodedResource.getResource().getInputStream();
298             try {
299                 InputSource JavaDoc inputSource = new InputSource JavaDoc(inputStream);
300                 if (encodedResource.getEncoding() != null) {
301                     inputSource.setEncoding(encodedResource.getEncoding());
302                 }
303                 return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
304             }
305             finally {
306                 inputStream.close();
307             }
308         }
309         catch (IOException JavaDoc ex) {
310             throw new BeanDefinitionStoreException(
311                     "IOException parsing XML document from " + encodedResource.getResource(), ex);
312         }
313     }
314
315     /**
316      * Load bean definitions from the specified XML file.
317      * @param inputSource the SAX InputSource to read from
318      * @return the number of bean definitions found
319      * @throws BeanDefinitionStoreException in case of loading or parsing errors
320      */

321     public int loadBeanDefinitions(InputSource JavaDoc inputSource) throws BeanDefinitionStoreException {
322         return loadBeanDefinitions(inputSource, "resource loaded through SAX InputSource");
323     }
324
325     /**
326      * Load bean definitions from the specified XML file.
327      * @param inputSource the SAX InputSource to read from
328      * @param resourceDescription a description of the resource
329      * (can be <code>null</code> or empty)
330      * @return the number of bean definitions found
331      * @throws BeanDefinitionStoreException in case of loading or parsing errors
332      */

333     public int loadBeanDefinitions(InputSource JavaDoc inputSource, String JavaDoc resourceDescription)
334             throws BeanDefinitionStoreException {
335
336         return doLoadBeanDefinitions(inputSource, new DescriptiveResource(resourceDescription));
337     }
338
339
340     /**
341      * Actually load bean definitions from the specified XML file.
342      * @param inputSource the SAX InputSource to read from
343      * @param resource the resource descriptor for the XML file
344      * @return the number of bean definitions found
345      * @throws BeanDefinitionStoreException in case of loading or parsing errors
346      */

347     protected int doLoadBeanDefinitions(InputSource JavaDoc inputSource, Resource resource)
348             throws BeanDefinitionStoreException {
349         try {
350             int validationMode = getValidationModeForResource(resource);
351             Document JavaDoc doc = this.documentLoader.loadDocument(
352                     inputSource, this.entityResolver, this.errorHandler, validationMode, this.namespaceAware);
353             return registerBeanDefinitions(doc, resource);
354         }
355         catch (BeanDefinitionStoreException ex) {
356             throw ex;
357         }
358         catch (SAXParseException JavaDoc ex) {
359             throw new XmlBeanDefinitionStoreException(resource.getDescription(),
360                     "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
361         }
362         catch (SAXException JavaDoc ex) {
363             throw new XmlBeanDefinitionStoreException(resource.getDescription(),
364                     "XML document from " + resource + " is invalid", ex);
365         }
366         catch (ParserConfigurationException JavaDoc ex) {
367             throw new BeanDefinitionStoreException(resource.getDescription(),
368                     "Parser configuration exception parsing XML from " + resource, ex);
369         }
370         catch (IOException JavaDoc ex) {
371             throw new BeanDefinitionStoreException(resource.getDescription(),
372                     "IOException parsing XML document from " + resource, ex);
373         }
374         catch (Throwable JavaDoc ex) {
375             throw new BeanDefinitionStoreException(resource.getDescription(),
376                     "Unexpected exception parsing XML document from " + resource, ex);
377         }
378     }
379
380
381     /**
382      * Gets the validation mode for the specified {@link Resource}. If no explicit
383      * validation mode has been configured then the validation mode is
384      * {@link #detectValidationMode detected}.
385      */

386     private int getValidationModeForResource(Resource resource) {
387         if (this.validationMode != VALIDATION_AUTO) {
388             return this.validationMode;
389         }
390         int detectedMode = detectValidationMode(resource);
391         if (detectedMode != VALIDATION_AUTO) {
392             return detectedMode;
393         }
394         // Hmm, we didn't get a clear indication... Let's assume XSD,
395
// since apparently no DTD declaration has been found up until
396
// detection stopped (before finding the document's root tag).
397
return VALIDATION_XSD;
398     }
399
400     /**
401      * Detects which kind of validation to perform on the XML file identified
402      * by the supplied {@link Resource}. If the
403      * file has a <code>DOCTYPE</code> definition then DTD validation is used
404      * otherwise XSD validation is assumed.
405      */

406     protected int detectValidationMode(Resource resource) {
407         if (resource.isOpen()) {
408             throw new BeanDefinitionStoreException(
409                     "Passed-in Resource [" + resource + "] contains an open stream: " +
410                     "cannot determine validation mode automatically. Either pass in a Resource " +
411                     "that is able to create fresh streams, or explicitly specify the validationMode " +
412                     "on your XmlBeanDefinitionReader instance.");
413         }
414
415         InputStream JavaDoc inputStream;
416         try {
417             inputStream = resource.getInputStream();
418         }
419         catch (IOException JavaDoc ex) {
420             throw new BeanDefinitionStoreException(
421                     "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
422                     "Did you attempt to load directly from a SAX InputSource without specifying the " +
423                     "validationMode on your XmlBeanDefinitionReader instance?", ex);
424         }
425
426         try {
427             return this.validationModeDetector.detectValidationMode(inputStream);
428         }
429         catch (IOException JavaDoc ex) {
430             throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
431                     resource + "]: an error occurred whilst reading from the InputStream.", ex);
432         }
433     }
434
435     /**
436      * Register the bean definitions contained in the given DOM document.
437      * Called by <code>loadBeanDefinitions</code>.
438      * <p>Creates a new instance of the parser class and invokes
439      * <code>registerBeanDefinitions</code> on it.
440      * @param doc the DOM document
441      * @param resource the resource descriptor (for context information)
442      * @return the number of bean definitions found
443      * @throws BeanDefinitionStoreException in case of parsing errors
444      * @see #loadBeanDefinitions
445      * @see #setDocumentReaderClass
446      * @see BeanDefinitionDocumentReader#registerBeanDefinitions
447      */

448     public int registerBeanDefinitions(Document JavaDoc doc, Resource resource) throws BeanDefinitionStoreException {
449         // Support old XmlBeanDefinitionParser SPI for backwards-compatibility.
450
if (this.parserClass != null) {
451             XmlBeanDefinitionParser parser =
452                     (XmlBeanDefinitionParser) BeanUtils.instantiateClass(this.parserClass);
453             return parser.registerBeanDefinitions(this, doc, resource);
454         }
455         // Read document based on new BeanDefinitionDocumentReader SPI.
456
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
457         int countBefore = getBeanFactory().getBeanDefinitionCount();
458         documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
459         return getBeanFactory().getBeanDefinitionCount() - countBefore;
460     }
461
462     /**
463      * Create the {@link BeanDefinitionDocumentReader} to use for actually
464      * reading bean definitions from an XML document.
465      * <p>Default implementation instantiates the specified "documentReaderClass".
466      * @see #setDocumentReaderClass
467      */

468     protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
469         return (BeanDefinitionDocumentReader) BeanUtils.instantiateClass(this.documentReaderClass);
470     }
471
472     /**
473      * Create the {@link XmlReaderContext} to pass over to the document reader.
474      */

475     protected XmlReaderContext createReaderContext(Resource resource) {
476         NamespaceHandlerResolver resolver = this.namespaceHandlerResolver;
477         if (resolver == null) {
478             resolver = createDefaultNamespaceHandlerResolver();
479         }
480         return new XmlReaderContext(
481                 resource, this.problemReporter, this.eventListener, this.sourceExtractor, this, resolver);
482     }
483
484     /**
485      * Create the default implementation of {@link NamespaceHandlerResolver} used if none is specified.
486      * Default implementation returns an instance of {@link DefaultNamespaceHandlerResolver}.
487      */

488     protected NamespaceHandlerResolver createDefaultNamespaceHandlerResolver() {
489         return new DefaultNamespaceHandlerResolver(getResourceLoader().getClassLoader());
490     }
491
492 }
493
Popular Tags