/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.beans.factory.xml;

import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.FatalBeanException;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;

Default implementation of the NamespaceHandlerResolver interface. Resolves namespace URIs to implementation classes based on the mappings contained in mapping file.

By default, this implementation looks for the mapping file at META-INF/spring.handlers, but this can be changed using the DefaultNamespaceHandlerResolver(ClassLoader, String) constructor.

Author:Rob Harrop, Juergen Hoeller
See Also:
Since:2.0
/** * Default implementation of the {@link NamespaceHandlerResolver} interface. * Resolves namespace URIs to implementation classes based on the mappings * contained in mapping file. * * <p>By default, this implementation looks for the mapping file at * {@code META-INF/spring.handlers}, but this can be changed using the * {@link #DefaultNamespaceHandlerResolver(ClassLoader, String)} constructor. * * @author Rob Harrop * @author Juergen Hoeller * @since 2.0 * @see NamespaceHandler * @see DefaultBeanDefinitionDocumentReader */
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
The location to look for the mapping files. Can be present in multiple JAR files.
/** * The location to look for the mapping files. Can be present in multiple JAR files. */
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
Logger available to subclasses.
/** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
ClassLoader to use for NamespaceHandler classes.
/** ClassLoader to use for NamespaceHandler classes. */
@Nullable private final ClassLoader classLoader;
Resource location to search for.
/** Resource location to search for. */
private final String handlerMappingsLocation;
Stores the mappings from namespace URI to NamespaceHandler class name / instance.
/** Stores the mappings from namespace URI to NamespaceHandler class name / instance. */
@Nullable private volatile Map<String, Object> handlerMappings;
Create a new DefaultNamespaceHandlerResolver using the default mapping file location.

This constructor will result in the thread context ClassLoader being used to load resources.

See Also:
/** * Create a new {@code DefaultNamespaceHandlerResolver} using the * default mapping file location. * <p>This constructor will result in the thread context ClassLoader being used * to load resources. * @see #DEFAULT_HANDLER_MAPPINGS_LOCATION */
public DefaultNamespaceHandlerResolver() { this(null, DEFAULT_HANDLER_MAPPINGS_LOCATION); }
Create a new DefaultNamespaceHandlerResolver using the default mapping file location.
Params:
  • classLoader – the ClassLoader instance used to load mapping resources (may be null, in which case the thread context ClassLoader will be used)
See Also:
/** * Create a new {@code DefaultNamespaceHandlerResolver} using the * default mapping file location. * @param classLoader the {@link ClassLoader} instance used to load mapping resources * (may be {@code null}, in which case the thread context ClassLoader will be used) * @see #DEFAULT_HANDLER_MAPPINGS_LOCATION */
public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader) { this(classLoader, DEFAULT_HANDLER_MAPPINGS_LOCATION); }
Create a new DefaultNamespaceHandlerResolver using the supplied mapping file location.
Params:
  • classLoader – the ClassLoader instance used to load mapping resources may be null, in which case the thread context ClassLoader will be used)
  • handlerMappingsLocation – the mapping file location
/** * Create a new {@code DefaultNamespaceHandlerResolver} using the * supplied mapping file location. * @param classLoader the {@link ClassLoader} instance used to load mapping resources * may be {@code null}, in which case the thread context ClassLoader will be used) * @param handlerMappingsLocation the mapping file location */
public DefaultNamespaceHandlerResolver(@Nullable ClassLoader classLoader, String handlerMappingsLocation) { Assert.notNull(handlerMappingsLocation, "Handler mappings location must not be null"); this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); this.handlerMappingsLocation = handlerMappingsLocation; }
Locate the NamespaceHandler for the supplied namespace URI from the configured mappings.
Params:
  • namespaceUri – the relevant namespace URI
Returns:the located NamespaceHandler, or null if none found
/** * Locate the {@link NamespaceHandler} for the supplied namespace URI * from the configured mappings. * @param namespaceUri the relevant namespace URI * @return the located {@link NamespaceHandler}, or {@code null} if none found */
@Override @Nullable public NamespaceHandler resolve(String namespaceUri) { Map<String, Object> handlerMappings = getHandlerMappings(); Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler) handlerOrClassName; } else { String className = (String) handlerOrClassName; try { Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); namespaceHandler.init(); handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } catch (ClassNotFoundException ex) { throw new FatalBeanException("Could not find NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", ex); } catch (LinkageError err) { throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", err); } } }
Load the specified NamespaceHandler mappings lazily.
/** * Load the specified NamespaceHandler mappings lazily. */
private Map<String, Object> getHandlerMappings() { Map<String, Object> handlerMappings = this.handlerMappings; if (handlerMappings == null) { synchronized (this) { handlerMappings = this.handlerMappings; if (handlerMappings == null) { if (logger.isTraceEnabled()) { logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]"); } try { Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isTraceEnabled()) { logger.trace("Loaded NamespaceHandler mappings: " + mappings); } handlerMappings = new ConcurrentHashMap<>(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); this.handlerMappings = handlerMappings; } catch (IOException ex) { throw new IllegalStateException( "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex); } } } } return handlerMappings; } @Override public String toString() { return "NamespaceHandlerResolver using mappings " + getHandlerMappings(); } }