EntityResolvers and DefaultNamespaceHandlerResolver support concurrent access now (SPR-5679)

This commit is contained in:
Juergen Hoeller 2009-04-20 12:07:00 +00:00
parent e54a44b577
commit 10d8abea3b
4 changed files with 83 additions and 40 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2007 the original author or authors. * Copyright 2002-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,6 +17,7 @@
package org.springframework.beans.factory.xml; package org.springframework.beans.factory.xml;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -56,8 +57,8 @@ public class BeansDtdResolver implements EntityResolver {
} }
if (systemId != null && systemId.endsWith(DTD_EXTENSION)) { if (systemId != null && systemId.endsWith(DTD_EXTENSION)) {
int lastPathSeparator = systemId.lastIndexOf("/"); int lastPathSeparator = systemId.lastIndexOf("/");
for (int i = 0; i < DTD_NAMES.length; ++i) { for (String DTD_NAME : DTD_NAMES) {
int dtdNameStart = systemId.indexOf(DTD_NAMES[i]); int dtdNameStart = systemId.indexOf(DTD_NAME);
if (dtdNameStart > lastPathSeparator) { if (dtdNameStart > lastPathSeparator) {
String dtdFile = systemId.substring(dtdNameStart); String dtdFile = systemId.substring(dtdNameStart);
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
@ -78,7 +79,7 @@ public class BeansDtdResolver implements EntityResolver {
logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in class path", ex); logger.debug("Could not resolve beans DTD [" + systemId + "]: not found in class path", ex);
} }
} }
} }
} }
} }
@ -87,4 +88,10 @@ public class BeansDtdResolver implements EntityResolver {
return null; return null;
} }
@Override
public String toString() {
return "EntityResolver for DTDs " + Arrays.toString(DTD_NAMES);
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2008 the original author or authors. * Copyright 2002-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,9 +17,9 @@
package org.springframework.beans.factory.xml; package org.springframework.beans.factory.xml;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -64,7 +64,7 @@ public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver
private final String handlerMappingsLocation; 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 */
private Map<String, NamespaceHandler> handlerMappings; private volatile Map<String, Object> handlerMappings;
/** /**
@ -110,7 +110,7 @@ public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver
* @return the located {@link NamespaceHandler}, or <code>null</code> if none found * @return the located {@link NamespaceHandler}, or <code>null</code> if none found
*/ */
public NamespaceHandler resolve(String namespaceUri) { public NamespaceHandler resolve(String namespaceUri) {
Map<String, NamespaceHandler> handlerMappings = getHandlerMappings(); Map<String, Object> handlerMappings = getHandlerMappings();
Object handlerOrClassName = handlerMappings.get(namespaceUri); Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) { if (handlerOrClassName == null) {
return null; return null;
@ -121,7 +121,7 @@ public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver
else { else {
String className = (String) handlerOrClassName; String className = (String) handlerOrClassName;
try { try {
Class handlerClass = ClassUtils.forName(className, this.classLoader); Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); "] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
@ -145,23 +145,34 @@ public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver
/** /**
* Load the specified NamespaceHandler mappings lazily. * Load the specified NamespaceHandler mappings lazily.
*/ */
private Map<String, NamespaceHandler> getHandlerMappings() { private Map<String, Object> getHandlerMappings() {
if (this.handlerMappings == null) { if (this.handlerMappings == null) {
try { synchronized (this) {
Properties mappings = if (this.handlerMappings == null) {
PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); try {
if (logger.isDebugEnabled()) { Properties mappings =
logger.debug("Loaded mappings [" + mappings + "]"); PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded NamespaceHandler mappings: " + mappings);
}
Map<String, Object> handlerMappings = new ConcurrentHashMap<String, Object>();
CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings);
this.handlerMappings = handlerMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
}
} }
this.handlerMappings = new HashMap<String, NamespaceHandler>();
CollectionUtils.mergePropertiesIntoMap(mappings, this.handlerMappings);
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex);
} }
} }
return this.handlerMappings; return this.handlerMappings;
} }
@Override
public String toString() {
return "NamespaceHandlerResolver using mappings " + getHandlerMappings();
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2007 the original author or authors. * Copyright 2002-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -88,4 +88,11 @@ public class DelegatingEntityResolver implements EntityResolver {
return null; return null;
} }
@Override
public String toString() {
return "EntityResolver delegating " + XSD_SUFFIX + " to " + this.schemaResolver +
" and " + DTD_SUFFIX + " to " + this.dtdResolver;
}
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2007 the original author or authors. * Copyright 2002-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,18 +17,20 @@
package org.springframework.beans.factory.xml; package org.springframework.beans.factory.xml;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.xml.sax.EntityResolver; import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.springframework.beans.FatalBeanException;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource; import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/** /**
* {@link EntityResolver} implementation that attempts to resolve schema URLs into * {@link EntityResolver} implementation that attempts to resolve schema URLs into
@ -67,7 +69,7 @@ public class PluggableSchemaResolver implements EntityResolver {
private final String schemaMappingsLocation; private final String schemaMappingsLocation;
/** Stores the mapping of schema URL -> local schema path */ /** Stores the mapping of schema URL -> local schema path */
private Properties schemaMappings; private volatile Map<String, String> schemaMappings;
/** /**
@ -104,7 +106,7 @@ public class PluggableSchemaResolver implements EntityResolver {
"] and system id [" + systemId + "]"); "] and system id [" + systemId + "]");
} }
if (systemId != null) { if (systemId != null) {
String resourceLocation = getSchemaMapping(systemId); String resourceLocation = getSchemaMappings().get(systemId);
if (resourceLocation != null) { if (resourceLocation != null) {
Resource resource = new ClassPathResource(resourceLocation, this.classLoader); Resource resource = new ClassPathResource(resourceLocation, this.classLoader);
InputSource source = new InputSource(resource.getInputStream()); InputSource source = new InputSource(resource.getInputStream());
@ -119,24 +121,40 @@ public class PluggableSchemaResolver implements EntityResolver {
return null; return null;
} }
protected String getSchemaMapping(String systemId) { /**
* Load the specified schema mappings lazily.
*/
private Map<String, String> getSchemaMappings() {
if (this.schemaMappings == null) { if (this.schemaMappings == null) {
if (logger.isDebugEnabled()) { synchronized (this) {
logger.debug("Loading schema mappings from [" + this.schemaMappingsLocation + "]"); if (this.schemaMappings == null) {
} if (logger.isDebugEnabled()) {
try { logger.debug("Loading schema mappings from [" + this.schemaMappingsLocation + "]");
this.schemaMappings = }
PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader); try {
if (logger.isDebugEnabled()) { Properties mappings =
logger.debug("Loaded schema mappings: " + this.schemaMappings); PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader);
if (logger.isDebugEnabled()) {
logger.debug("Loaded schema mappings: " + mappings);
}
Map<String, String> schemaMappings = new ConcurrentHashMap<String, String>();
CollectionUtils.mergePropertiesIntoMap(mappings, schemaMappings);
this.schemaMappings = schemaMappings;
}
catch (IOException ex) {
throw new IllegalStateException(
"Unable to load schema mappings from location [" + this.schemaMappingsLocation + "]", ex);
}
} }
} }
catch (IOException ex) {
throw new FatalBeanException(
"Unable to load schema mappings from location [" + this.schemaMappingsLocation + "]", ex);
}
} }
return this.schemaMappings.getProperty(systemId); return this.schemaMappings;
}
@Override
public String toString() {
return "EntityResolver using mappings " + getSchemaMappings();
} }
} }