001/*
002 * Copyright 2002-2012 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.beans.factory.xml;
018
019import javax.xml.parsers.DocumentBuilder;
020import javax.xml.parsers.DocumentBuilderFactory;
021import javax.xml.parsers.ParserConfigurationException;
022
023import org.apache.commons.logging.Log;
024import org.apache.commons.logging.LogFactory;
025import org.w3c.dom.Document;
026import org.xml.sax.EntityResolver;
027import org.xml.sax.ErrorHandler;
028import org.xml.sax.InputSource;
029
030import org.springframework.util.xml.XmlValidationModeDetector;
031
032/**
033 * Spring's default {@link DocumentLoader} implementation.
034 *
035 * <p>Simply loads {@link Document documents} using the standard JAXP-configured
036 * XML parser. If you want to change the {@link DocumentBuilder} that is used to
037 * load documents, then one strategy is to define a corresponding Java system property
038 * when starting your JVM. For example, to use the Oracle {@link DocumentBuilder},
039 * you might start your application like as follows:
040 *
041 * <pre code="class">java -Djavax.xml.parsers.DocumentBuilderFactory=oracle.xml.jaxp.JXDocumentBuilderFactory MyMainClass</pre>
042 *
043 * @author Rob Harrop
044 * @author Juergen Hoeller
045 * @since 2.0
046 */
047public class DefaultDocumentLoader implements DocumentLoader {
048
049        /**
050         * JAXP attribute used to configure the schema language for validation.
051         */
052        private static final String SCHEMA_LANGUAGE_ATTRIBUTE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
053
054        /**
055         * JAXP attribute value indicating the XSD schema language.
056         */
057        private static final String XSD_SCHEMA_LANGUAGE = "http://www.w3.org/2001/XMLSchema";
058
059
060        private static final Log logger = LogFactory.getLog(DefaultDocumentLoader.class);
061
062
063        /**
064         * Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
065         * XML parser.
066         */
067        @Override
068        public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
069                        ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
070
071                DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
072                if (logger.isDebugEnabled()) {
073                        logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
074                }
075                DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
076                return builder.parse(inputSource);
077        }
078
079        /**
080         * Create the {@link DocumentBuilderFactory} instance.
081         * @param validationMode the type of validation: {@link XmlValidationModeDetector#VALIDATION_DTD DTD}
082         * or {@link XmlValidationModeDetector#VALIDATION_XSD XSD})
083         * @param namespaceAware whether the returned factory is to provide support for XML namespaces
084         * @return the JAXP DocumentBuilderFactory
085         * @throws ParserConfigurationException if we failed to build a proper DocumentBuilderFactory
086         */
087        protected DocumentBuilderFactory createDocumentBuilderFactory(int validationMode, boolean namespaceAware)
088                        throws ParserConfigurationException {
089
090                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
091                factory.setNamespaceAware(namespaceAware);
092
093                if (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
094                        factory.setValidating(true);
095                        if (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
096                                // Enforce namespace aware for XSD...
097                                factory.setNamespaceAware(true);
098                                try {
099                                        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}