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 javax.xml.parsers.DocumentBuilder;
20 import javax.xml.parsers.DocumentBuilderFactory;
21 import javax.xml.parsers.ParserConfigurationException;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25 import org.w3c.dom.Document;
26 import org.xml.sax.EntityResolver;
27 import org.xml.sax.ErrorHandler;
28 import org.xml.sax.InputSource;
29
30 import org.springframework.util.xml.XmlValidationModeDetector;
31
32 /**
33 * Spring's default {@link DocumentLoader} implementation.
34 *
35 * <p>Simply loads {@link Document documents} using the standard JAXP-configured
36 * XML parser. If you want to change the {@link DocumentBuilder} that is used to
37 * load documents, then one strategy is to define a corresponding Java system property
38 * when starting your JVM. For example, to use the Oracle {@link DocumentBuilder},
39 * you might start your application like as follows:
40 *
41 * <pre code="class">java -Djavax.xml.parsers.DocumentBuilderFactory=oracle.xml.jaxp.JXDocumentBuilderFactory MyMainClass</pre>
42 *
43 * @author Rob Harrop
44 * @author Juergen Hoeller
45 * @since 2.0
46 */
47 public class DefaultDocumentLoader implements DocumentLoader {
48
49 /**
50 * JAXP attribute used to configure the schema language for validation.
51 */
52 private static final String SCHEMA_LANGUAGE_ATTRIBUTE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
53
54 /**
55 * JAXP attribute value indicating the XSD schema language.
56 */
57 private static final String XSD_SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";
58
59
60 private static final Log logger = LogFactory.getLog(DefaultDocumentLoader.class);
61
62
63 /**
64 * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
65 * XML parser.
66 */
67 public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
68 ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
69
70 DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
71 if (logger.isDebugEnabled()) {
72 logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
73 }
74 DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
75 return builder.parse(inputSource);
76 }
77
78 /**
79 * Create the {@link DocumentBuilderFactory} instance.
80 * @param validationMode the type of validation: {@link XmlValidationModeDetector#VALIDATION_DTD DTD}
81 * or {@link XmlValidationModeDetector#VALIDATION_XSD XSD})
82 * @param namespaceAware whether the returned factory is to provide support for XML namespaces
83 * @return the JAXP DocumentBuilderFactory
84 * @throws ParserConfigurationException if we failed to build a proper DocumentBuilderFactory
85 */
86 protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
87 throws ParserConfigurationException {
88
89 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
90 factory.setNamespaceAware(namespaceAware);
91
92 if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
93 factory.setValidating(true);
94
95 if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
96 // Enforce namespace aware for XSD...
97 factory.setNamespaceAware(true);
98 try {
99 factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
100 }
101 catch (IllegalArgumentException ex) {
102 ParserConfigurationException pcex = new ParserConfigurationException(
103 "Unable to validate using XSD: Your JAXP provider [" + factory +
104 "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? " +
105 "Upgrade to Apache Xerces (or Java 1.5) for full XSD support.");
106 pcex.initCause(ex);
107 throw pcex;
108 }
109 }
110 }
111
112 return factory;
113 }
114
115 /**
116 * Create a JAXP DocumentBuilder that this bean definition reader
117 * will use for parsing XML documents. Can be overridden in subclasses,
118 * adding further initialization of the builder.
119 * @param factory the JAXP DocumentBuilderFactory that the DocumentBuilder
120 * should be created with
121 * @param entityResolver the SAX EntityResolver to use
122 * @param errorHandler the SAX ErrorHandler to use
123 * @return the JAXP DocumentBuilder
124 * @throws ParserConfigurationException if thrown by JAXP methods
125 */
126 protected DocumentBuilder createDocumentBuilder(
127 DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler)
128 throws ParserConfigurationException {
129
130 DocumentBuilder docBuilder = factory.newDocumentBuilder();
131 if (entityResolver != null) {
132 docBuilder.setEntityResolver(entityResolver);
133 }
134 if (errorHandler != null) {
135 docBuilder.setErrorHandler(errorHandler);
136 }
137 return docBuilder;
138 }
139
140 }