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