001: /*
002: * Copyright 2002-2007 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: * http://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:
017: package org.springframework.beans.factory.xml;
018:
019: import java.io.IOException;
020: import java.util.Properties;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024: import org.xml.sax.EntityResolver;
025: import org.xml.sax.InputSource;
026:
027: import org.springframework.beans.FatalBeanException;
028: import org.springframework.core.io.ClassPathResource;
029: import org.springframework.core.io.Resource;
030: import org.springframework.core.io.support.PropertiesLoaderUtils;
031: import org.springframework.util.Assert;
032:
033: /**
034: * {@link EntityResolver} implementation that attempts to resolve schema URLs into
035: * local {@link ClassPathResource classpath resources} using a set of mappings files.
036: *
037: * <p>By default, this class will look for mapping files in the classpath using the pattern:
038: * <code>META-INF/spring.schemas</code> allowing for multiple files to exist on the
039: * classpath at any one time.
040: *
041: * The format of <code>META-INF/spring.schemas</code> is a properties
042: * file where each line should be of the form <code>systemId=schema-location</code>
043: * where <code>schema-location</code> should also be a schema file in the classpath.
044: * Since systemId is commonly a URL, one must be careful to escape any ':' characters
045: * which are treated as delimiters in properties files.
046: *
047: * <p>The pattern for the mapping files can be overidden using the
048: * {@link #PluggableSchemaResolver(ClassLoader, String)} constructor
049: *
050: * @author Rob Harrop
051: * @author Juergen Hoeller
052: * @since 2.0
053: */
054: public class PluggableSchemaResolver implements EntityResolver {
055:
056: /**
057: * The location of the file that defines schema mappings.
058: * Can be present in multiple JAR files.
059: */
060: public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";
061:
062: private static final Log logger = LogFactory
063: .getLog(PluggableSchemaResolver.class);
064:
065: private final ClassLoader classLoader;
066:
067: /** Stores the mapping of schema URL -> local schema path */
068: private final Properties schemaMappings;
069:
070: /**
071: * Loads the schema URL -> schema file location mappings using the default
072: * mapping file pattern "META-INF/spring.schemas".
073: * @param classLoader the ClassLoader to use for loading
074: * (can be <code>null</code>) to use the default ClassLoader)
075: * @see PropertiesLoaderUtils#loadAllProperties(String, ClassLoader)
076: */
077: public PluggableSchemaResolver(ClassLoader classLoader) {
078: this (classLoader, DEFAULT_SCHEMA_MAPPINGS_LOCATION);
079: }
080:
081: /**
082: * Loads the schema URL -> schema file location mappings using the given
083: * mapping file pattern.
084: * @param classLoader the ClassLoader to use for loading
085: * (can be <code>null</code>) to use the default ClassLoader)
086: * @param schemaMappingsLocation the location of the file that defines schema mappings
087: * (must not be empty)
088: * @see PropertiesLoaderUtils#loadAllProperties(String, ClassLoader)
089: */
090: public PluggableSchemaResolver(ClassLoader classLoader,
091: String schemaMappingsLocation) {
092: Assert.hasText(schemaMappingsLocation,
093: "'schemaMappingsLocation' must not be empty");
094: this .classLoader = classLoader;
095: if (logger.isDebugEnabled()) {
096: logger.debug("Loading schema mappings from ["
097: + schemaMappingsLocation + "]");
098: }
099: try {
100: this .schemaMappings = PropertiesLoaderUtils
101: .loadAllProperties(schemaMappingsLocation,
102: classLoader);
103: if (logger.isDebugEnabled()) {
104: logger.debug("Loaded schema mappings: "
105: + this .schemaMappings);
106: }
107: } catch (IOException e) {
108: throw new FatalBeanException(
109: "Unable to load schema mappings from location ["
110: + schemaMappingsLocation + "]", e);
111: }
112: }
113:
114: public InputSource resolveEntity(String publicId, String systemId)
115: throws IOException {
116: if (logger.isTraceEnabled()) {
117: logger
118: .trace("Trying to resolve XML entity with public id ["
119: + publicId
120: + "] and system id ["
121: + systemId
122: + "]");
123: }
124: if (systemId != null) {
125: String resourceLocation = this .schemaMappings
126: .getProperty(systemId);
127: if (resourceLocation != null) {
128: Resource resource = new ClassPathResource(
129: resourceLocation, this .classLoader);
130: InputSource source = new InputSource(resource
131: .getInputStream());
132: source.setPublicId(publicId);
133: source.setSystemId(systemId);
134: if (logger.isDebugEnabled()) {
135: logger.debug("Found XML schema [" + systemId
136: + "] in classpath: " + resourceLocation);
137: }
138: return source;
139: }
140: }
141: return null;
142: }
143:
144: }
|