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 	 */
loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware)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 	 */
createDocumentBuilderFactory(int validationMode, boolean namespaceAware)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 	 */
createDocumentBuilder( DocumentBuilderFactory factory, EntityResolver entityResolver, ErrorHandler errorHandler)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 }
141