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.Enumeration;
021: import java.util.HashMap;
022: import java.util.Map;
023: import java.util.Properties;
024:
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027:
028: import org.springframework.beans.BeanUtils;
029: import org.springframework.core.io.support.PropertiesLoaderUtils;
030: import org.springframework.util.Assert;
031: import org.springframework.util.ClassUtils;
032:
033: /**
034: * Default implementation of the {@link NamespaceHandler}. Resolves namespace URIs
035: * to implementation classes based on the mappings contained in mapping file.
036: *
037: * <p>By default, this implementation looks for the mapping file at
038: * <code>META-INF/spring.handlers</code>, but this can be changed using the
039: * {@link #DefaultNamespaceHandlerResolver(ClassLoader, String)} constructor.
040: *
041: * @author Rob Harrop
042: * @author Juergen Hoeller
043: * @since 2.0
044: * @see NamespaceHandler
045: * @see DefaultBeanDefinitionDocumentReader
046: */
047: public class DefaultNamespaceHandlerResolver implements
048: NamespaceHandlerResolver {
049:
050: /**
051: * The location to look for the mapping files. Can be present in multiple JAR files.
052: */
053: private static final String SPRING_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
054:
055: /** Logger available to subclasses */
056: protected final Log logger = LogFactory.getLog(getClass());
057:
058: /** Stores the mappings from namespace URI Strings to NamespaceHandler instances */
059: private Map handlerMappings;
060:
061: /**
062: * Create a new <code>DefaultNamespaceHandlerResolver</code> using the
063: * default mapping file location.
064: * <p>This constructor will result in the thread context ClassLoader being used
065: * to load resources.
066: * @see #SPRING_HANDLER_MAPPINGS_LOCATION
067: */
068: public DefaultNamespaceHandlerResolver() {
069: this (null, SPRING_HANDLER_MAPPINGS_LOCATION);
070: }
071:
072: /**
073: * Create a new <code>DefaultNamespaceHandlerResolver</code> using the
074: * default mapping file location.
075: * @param classLoader the {@link ClassLoader} instance used to load mapping resources (may be <code>null</code>, in
076: * which case the thread context ClassLoader will be used)
077: * @see #SPRING_HANDLER_MAPPINGS_LOCATION
078: */
079: public DefaultNamespaceHandlerResolver(ClassLoader classLoader) {
080: this (classLoader, SPRING_HANDLER_MAPPINGS_LOCATION);
081: }
082:
083: /**
084: * Create a new <code>DefaultNamespaceHandlerResolver</code> using the
085: * supplied mapping file location.
086: * @param classLoader the {@link ClassLoader} instance used to load mapping resources (may be <code>null</code>, in
087: * which case the thread context ClassLoader will be used)
088: * @param handlerMappingsLocation the mapping file location
089: * @see #SPRING_HANDLER_MAPPINGS_LOCATION
090: */
091: public DefaultNamespaceHandlerResolver(ClassLoader classLoader,
092: String handlerMappingsLocation) {
093: Assert.notNull(handlerMappingsLocation,
094: "Handler mappings location must not be null");
095: ClassLoader classLoaderToUse = (classLoader != null ? classLoader
096: : ClassUtils.getDefaultClassLoader());
097: initHandlerMappings(classLoaderToUse, handlerMappingsLocation);
098: }
099:
100: /**
101: * Load the namespace URI -> <code>NamespaceHandler</code> class mappings from the configured
102: * mapping file. Converts the class names into actual class instances and checks that
103: * they implement the <code>NamespaceHandler</code> interface. Pre-instantiates an instance
104: * of each <code>NamespaceHandler</code> and maps that instance to the corresponding
105: * namespace URI.
106: */
107: private void initHandlerMappings(ClassLoader classLoader,
108: String handlerMappingsLocation) {
109: Properties mappings = loadMappings(classLoader,
110: handlerMappingsLocation);
111: if (logger.isDebugEnabled()) {
112: logger.debug("Loaded mappings [" + mappings + "]");
113: }
114: this .handlerMappings = new HashMap(mappings.size());
115: for (Enumeration en = mappings.propertyNames(); en
116: .hasMoreElements();) {
117: String namespaceUri = (String) en.nextElement();
118: String className = mappings.getProperty(namespaceUri);
119: try {
120: Class handlerClass = ClassUtils.forName(className,
121: classLoader);
122: if (!NamespaceHandler.class
123: .isAssignableFrom(handlerClass)) {
124: throw new IllegalArgumentException(
125: "Class ["
126: + className
127: + "] does not implement the NamespaceHandler interface");
128: }
129: NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils
130: .instantiateClass(handlerClass);
131: namespaceHandler.init();
132: this .handlerMappings
133: .put(namespaceUri, namespaceHandler);
134: } catch (ClassNotFoundException ex) {
135: if (logger.isDebugEnabled()) {
136: logger.debug("Ignoring namespace handler ["
137: + className + "]: handler class not found",
138: ex);
139: }
140: } catch (LinkageError err) {
141: if (logger.isWarnEnabled()) {
142: logger
143: .warn(
144: "Ignoring namespace handler ["
145: + className
146: + "]: problem with handler class file or dependent class",
147: err);
148: }
149: }
150: }
151: }
152:
153: private Properties loadMappings(ClassLoader classLoader,
154: String handlerMappingsLocation) {
155: try {
156: return PropertiesLoaderUtils.loadAllProperties(
157: handlerMappingsLocation, classLoader);
158: } catch (IOException ex) {
159: throw new IllegalStateException(
160: "Unable to load NamespaceHandler mappings from location ["
161: + handlerMappingsLocation
162: + "]. Root cause: " + ex);
163: }
164: }
165:
166: /**
167: * Locate the {@link NamespaceHandler} for the supplied namespace URI
168: * from the configured mappings.
169: * @param namespaceUri the relevant namespace URI
170: * @return the located {@link NamespaceHandler}, or <code>null</code> if none found
171: */
172: public NamespaceHandler resolve(String namespaceUri) {
173: return (NamespaceHandler) this.handlerMappings
174: .get(namespaceUri);
175: }
176:
177: }
|