/*
* Copyright Terracotta, Inc.
*
* 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
*
* http://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.ehcache.xml;
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.Configuration;
import org.ehcache.config.ResourcePools;
import org.ehcache.config.Builder;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.core.util.ClassLoading;
import org.ehcache.spi.service.ServiceCreationConfiguration;
import org.ehcache.xml.exceptions.XmlConfigurationException;
import org.w3c.dom.Document;
import java.lang.reflect.Array;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import static java.lang.Class.forName;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
import static java.util.Objects.requireNonNull;
import static org.ehcache.xml.ConfigurationParser.documentToText;
import static org.ehcache.xml.XmlConfiguration.PrettyClassFormat.when;
Exposes Configuration
and CacheConfigurationBuilder
expressed in a XML file that obeys the core Ehcache schema.
Instances of this class are not thread-safe.
/**
* Exposes {@link org.ehcache.config.Configuration} and {@link CacheConfigurationBuilder} expressed
* in a XML file that obeys the <a href="http://www.ehcache.org/v3" target="_blank">core Ehcache schema</a>.
* <p>
* Instances of this class are not thread-safe.
*/
public class XmlConfiguration implements Configuration {
public static final URL CORE_SCHEMA_URL = XmlConfiguration.class.getResource("/ehcache-core.xsd");
private final URL source;
private final Document document;
private final String renderedDocument;
private final Configuration configuration;
private final Map<String, ClassLoader> cacheClassLoaders;
private final Map<String, Template> templates;
Constructs an instance of XmlConfiguration mapping to the XML file located at url
.
The default ClassLoader will first try to use the thread context class loader, followed by the ClassLoader that
loaded the Ehcache classes.
Params: - url – URL pointing to the XML file's location
Throws: - XmlConfigurationException – if anything went wrong parsing the XML
/**
* Constructs an instance of XmlConfiguration mapping to the XML file located at {@code url}.
* <p>
* The default ClassLoader will first try to use the thread context class loader, followed by the ClassLoader that
* loaded the Ehcache classes.
*
* @param url URL pointing to the XML file's location
*
* @throws XmlConfigurationException if anything went wrong parsing the XML
*/
public XmlConfiguration(URL url)
throws XmlConfigurationException {
this(url, ClassLoading.getDefaultClassLoader());
}
Constructs an instance of XmlConfiguration mapping to the XML file located at url
and using the provided classLoader
to load user types (e.g. key and value Class instances). Params: - url – URL pointing to the XML file's location
- classLoader – ClassLoader to use to load user types.
Throws: - XmlConfigurationException – if anything went wrong parsing the XML
/**
* Constructs an instance of XmlConfiguration mapping to the XML file located at {@code url} and using the provided
* {@code classLoader} to load user types (e.g. key and value Class instances).
*
* @param url URL pointing to the XML file's location
* @param classLoader ClassLoader to use to load user types.
*
* @throws XmlConfigurationException if anything went wrong parsing the XML
*/
public XmlConfiguration(URL url, final ClassLoader classLoader)
throws XmlConfigurationException {
this(url, classLoader, Collections.emptyMap());
}
Constructs an instance of XmlConfiguration mapping to the XML file located at url
and using the provided classLoader
to load user types (e.g. key and value Class instances). The cacheClassLoaders
will let you specify a different ClassLoader
to use for each Cache
managed by the CacheManager
configured using this XmlConfiguration
. Caches with aliases that do not appear in the map will use classLoader
as a default. Params: - url – URL pointing to the XML file's location
- classLoader – ClassLoader to use to load user types.
- cacheClassLoaders – the map with mappings between cache names and the corresponding class loaders
Throws: - XmlConfigurationException – if anything went wrong parsing the XML
/**
* Constructs an instance of XmlConfiguration mapping to the XML file located at {@code url} and using the provided
* {@code classLoader} to load user types (e.g. key and value Class instances). The {@code cacheClassLoaders} will
* let you specify a different {@link java.lang.ClassLoader} to use for each {@link org.ehcache.Cache} managed by
* the {@link org.ehcache.CacheManager} configured using this {@link org.ehcache.xml.XmlConfiguration}. Caches with
* aliases that do not appear in the map will use {@code classLoader} as a default.
*
* @param url URL pointing to the XML file's location
* @param classLoader ClassLoader to use to load user types.
* @param cacheClassLoaders the map with mappings between cache names and the corresponding class loaders
*
* @throws XmlConfigurationException if anything went wrong parsing the XML
*/
public XmlConfiguration(URL url, final ClassLoader classLoader, final Map<String, ClassLoader> cacheClassLoaders)
throws XmlConfigurationException {
this.source = requireNonNull(url, "The url can not be null");
requireNonNull(classLoader, "The classLoader can not be null");
this.cacheClassLoaders = requireNonNull(cacheClassLoaders, "The cacheClassLoaders map can not be null");
try {
ConfigurationParser parser = new ConfigurationParser();
this.document = parser.uriToDocument(source.toURI());
ConfigurationParser.XmlConfigurationWrapper configWrapper = parser.documentToConfig(document, classLoader, cacheClassLoaders);
this.configuration = configWrapper.getConfiguration();
this.templates = configWrapper.getTemplates();
this.renderedDocument = ConfigurationParser.urlToText(url, document.getInputEncoding());
} catch (XmlConfigurationException e) {
throw e;
} catch (Exception e) {
throw new XmlConfigurationException("Error parsing XML configuration at " + url, e);
}
}
Constructs an instance of XmlConfiguration from the given XML DOM.
The default ClassLoader will first try to use the thread context class loader, followed by the ClassLoader that
loaded the Ehcache classes.
Params: - xml – XML Document Object Model
Throws: - XmlConfigurationException – if anything went wrong parsing the XML
/**
* Constructs an instance of XmlConfiguration from the given XML DOM.
* <p>
* The default ClassLoader will first try to use the thread context class loader, followed by the ClassLoader that
* loaded the Ehcache classes.
*
* @param xml XML Document Object Model
*
* @throws XmlConfigurationException if anything went wrong parsing the XML
*/
public XmlConfiguration(Document xml) throws XmlConfigurationException {
this(xml, ClassLoading.getDefaultClassLoader());
}
Constructs an instance of XmlConfiguration from the given XML DOM and using the provided classLoader
to load user types (e.g. key and value Class instances). Params: - xml – XML Document Object Model
- classLoader – ClassLoader to use to load user types.
Throws: - XmlConfigurationException – if anything went wrong parsing the XML
/**
* Constructs an instance of XmlConfiguration from the given XML DOM and using the provided {@code classLoader} to
* load user types (e.g. key and value Class instances).
*
* @param xml XML Document Object Model
* @param classLoader ClassLoader to use to load user types.
*
* @throws XmlConfigurationException if anything went wrong parsing the XML
*/
public XmlConfiguration(Document xml, ClassLoader classLoader) throws XmlConfigurationException {
this(xml, classLoader, emptyMap());
}
Constructs an instance of XmlConfiguration from the given XML DOM and using the provided classLoader
to load user types (e.g. key and value Class instances). The cacheClassLoaders
will let you specify a different ClassLoader
to use for each Cache
managed by the CacheManager
configured using this XmlConfiguration
. Caches with aliases that do not appear in the map will use classLoader
as a default. Params: - xml – XML Document Object Model
- classLoader – ClassLoader to use to load user types.
- cacheClassLoaders – the map with mappings between cache names and the corresponding class loaders
Throws: - XmlConfigurationException – if anything went wrong parsing the XML
/**
* Constructs an instance of XmlConfiguration from the given XML DOM and using the provided {@code classLoader} to
* load user types (e.g. key and value Class instances). The {@code cacheClassLoaders} will let you specify a
* different {@link java.lang.ClassLoader} to use for each {@link org.ehcache.Cache} managed by the
* {@link org.ehcache.CacheManager} configured using this {@link org.ehcache.xml.XmlConfiguration}. Caches with
* aliases that do not appear in the map will use {@code classLoader} as a default.
*
* @param xml XML Document Object Model
* @param classLoader ClassLoader to use to load user types.
* @param cacheClassLoaders the map with mappings between cache names and the corresponding class loaders
*
* @throws XmlConfigurationException if anything went wrong parsing the XML
*/
public XmlConfiguration(Document xml, ClassLoader classLoader, Map<String, ClassLoader> cacheClassLoaders) throws XmlConfigurationException {
requireNonNull(xml, "The source-element cannot be null");
requireNonNull(classLoader, "The classLoader can not be null");
this.cacheClassLoaders = requireNonNull(cacheClassLoaders, "The cacheClassLoaders map can not be null");
this.source = null;
try {
ConfigurationParser parser = new ConfigurationParser();
this.document = xml;
ConfigurationParser.XmlConfigurationWrapper configWrapper = parser.documentToConfig(document, classLoader, cacheClassLoaders);
this.configuration = configWrapper.getConfiguration();
this.templates = configWrapper.getTemplates();
this.renderedDocument = documentToText(xml);
} catch (XmlConfigurationException e) {
throw e;
} catch (Exception e) {
throw new XmlConfigurationException("Error parsing XML configuration", e);
}
}
Constructs an instance of XmlConfiguration from an existing configuration object.
Params: - configuration – existing configuration
Throws: - XmlConfigurationException – if anything went wrong converting to XML
/**
* Constructs an instance of XmlConfiguration from an existing configuration object.
*
* @param configuration existing configuration
*
* @throws XmlConfigurationException if anything went wrong converting to XML
*/
public XmlConfiguration(Configuration configuration) throws XmlConfigurationException {
this.source = null;
this.cacheClassLoaders = emptyMap();
try {
ConfigurationParser parser = new ConfigurationParser();
this.configuration = configuration;
this.templates = emptyMap();
this.document = parser.configToDocument(configuration);
this.renderedDocument = documentToText(document);
} catch (XmlConfigurationException e) {
throw e;
} catch (Exception e) {
throw new XmlConfigurationException("Error unparsing configuration: " + configuration, e);
}
}
Return this configuration as an XML Document
. Returns: configuration XML DOM.
/**
* Return this configuration as an XML {@link org.w3c.dom.Document}.
*
* @return configuration XML DOM.
*/
public Document asDocument() {
return document;
}
Return this configuration as a rendered XML string.
Returns: configuration XML string
/**
* Return this configuration as a rendered XML string.
*
* @return configuration XML string
*/
public String asRenderedDocument() {
return renderedDocument;
}
@Override
public String toString() {
return asRenderedDocument();
}
Exposes the URL where the XML file parsed or yet to be parsed was or will be sourced from.
Returns: The URL provided at object instantiation
/**
* Exposes the URL where the XML file parsed or yet to be parsed was or will be sourced from.
* @return The URL provided at object instantiation
*/
public URL getURL() {
return source;
}
Creates a new CacheConfigurationBuilder
seeded with the cache-template configuration by the given name
in the parsed XML configuration. Note that this version does not specify resources, which are mandatory to create a CacheConfigurationBuilder
. So if the template does not define resources, this will throw.
Params: - name – the unique name identifying the cache-template element in the XML
- keyType – the type of keys for the
CacheConfigurationBuilder
to use, must match the key-type
declared in the template if declared in XML - valueType – the type of values for the
CacheConfigurationBuilder
to use, must match the value-type
declared in the template if declared in XML
Type parameters: Throws: - IllegalStateException – if the template does not configure resources.
- IllegalArgumentException – if
keyType
or valueType
don't match the declared type(s) of the template - ClassNotFoundException – if a
Class
declared in the XML couldn't be found - InstantiationException – if a user provided
Class
couldn't get instantiated - IllegalAccessException – if a method (including constructor) couldn't be invoked on a user provided type
Returns: the preconfigured CacheConfigurationBuilder
or null
if no cache-template for the provided name
/**
* Creates a new {@link CacheConfigurationBuilder} seeded with the cache-template configuration
* by the given {@code name} in the parsed XML configuration.
* <p>
* Note that this version does not specify resources, which are mandatory to create a
* {@link CacheConfigurationBuilder}. So if the template does not define resources, this will throw.
*
* @param name the unique name identifying the cache-template element in the XML
* @param keyType the type of keys for the {@link CacheConfigurationBuilder} to use, must
* match the {@code key-type} declared in the template if declared in XML
* @param valueType the type of values for the {@link CacheConfigurationBuilder} to use, must
* match the {@code value-type} declared in the template if declared in XML
* @param <K> type of keys
* @param <V> type of values
*
* @return the preconfigured {@link CacheConfigurationBuilder}
* or {@code null} if no cache-template for the provided {@code name}
*
* @throws IllegalStateException if the template does not configure resources.
* @throws IllegalArgumentException if {@code keyType} or {@code valueType} don't match the declared type(s) of the template
* @throws ClassNotFoundException if a {@link java.lang.Class} declared in the XML couldn't be found
* @throws InstantiationException if a user provided {@link java.lang.Class} couldn't get instantiated
* @throws IllegalAccessException if a method (including constructor) couldn't be invoked on a user provided type
*/
@SuppressWarnings("unchecked")
public <K, V> CacheConfigurationBuilder<K, V> newCacheConfigurationBuilderFromTemplate(final String name,
final Class<K> keyType,
final Class<V> valueType)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Template template = templates.get(name);
if (template == null) {
return null;
} else {
return template.builderFor(cacheClassLoaders.getOrDefault(name, getClassLoader()), keyType, valueType, null);
}
}
Creates a new CacheConfigurationBuilder
seeded with the cache-template configuration by the given name
in the parsed XML configuration. Params: - name – the unique name identifying the cache-template element in the XML
- keyType – the type of keys for the
CacheConfigurationBuilder
to use, must match the key-type
declared in the template if declared in XML - valueType – the type of values for the
CacheConfigurationBuilder
to use, must match the value-type
declared in the template if declared in XML - resourcePools – Resources definitions that will be used
Type parameters: Throws: - IllegalArgumentException – if
keyType
or valueType
don't match the declared type(s) of the template - ClassNotFoundException – if a
Class
declared in the XML couldn't be found - InstantiationException – if a user provided
Class
couldn't get instantiated - IllegalAccessException – if a method (including constructor) couldn't be invoked on a user provided type
Returns: the preconfigured CacheConfigurationBuilder
or null
if no cache-template for the provided name
/**
* Creates a new {@link CacheConfigurationBuilder} seeded with the cache-template configuration
* by the given {@code name} in the parsed XML configuration.
*
* @param name the unique name identifying the cache-template element in the XML
* @param keyType the type of keys for the {@link CacheConfigurationBuilder} to use, must
* match the {@code key-type} declared in the template if declared in XML
* @param valueType the type of values for the {@link CacheConfigurationBuilder} to use, must
* match the {@code value-type} declared in the template if declared in XML
* @param resourcePools Resources definitions that will be used
* @param <K> type of keys
* @param <V> type of values
*
* @return the preconfigured {@link CacheConfigurationBuilder}
* or {@code null} if no cache-template for the provided {@code name}
*
* @throws IllegalArgumentException if {@code keyType} or {@code valueType} don't match the declared type(s) of the template
* @throws ClassNotFoundException if a {@link java.lang.Class} declared in the XML couldn't be found
* @throws InstantiationException if a user provided {@link java.lang.Class} couldn't get instantiated
* @throws IllegalAccessException if a method (including constructor) couldn't be invoked on a user provided type
*/
@SuppressWarnings("unchecked")
public <K, V> CacheConfigurationBuilder<K, V> newCacheConfigurationBuilderFromTemplate(final String name,
final Class<K> keyType,
final Class<V> valueType,
final ResourcePools resourcePools)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Template template = templates.get(name);
if (template == null) {
return null;
} else {
return template.builderFor(cacheClassLoaders.getOrDefault(name, getClassLoader()), keyType, valueType, requireNonNull(resourcePools));
}
}
Creates a new CacheConfigurationBuilder
seeded with the cache-template configuration by the given name
in the parsed XML configuration. Params: - name – the unique name identifying the cache-template element in the XML
- keyType – the type of keys for the
CacheConfigurationBuilder
to use, must match the key-type
declared in the template if declared in XML - valueType – the type of values for the
CacheConfigurationBuilder
to use, must match the value-type
declared in the template if declared in XML - resourcePoolsBuilder – Resources definitions that will be used
Type parameters: Throws: - IllegalArgumentException – if
keyType
or valueType
don't match the declared type(s) of the template - ClassNotFoundException – if a
Class
declared in the XML couldn't be found - InstantiationException – if a user provided
Class
couldn't get instantiated - IllegalAccessException – if a method (including constructor) couldn't be invoked on a user provided type
Returns: the preconfigured CacheConfigurationBuilder
or null
if no cache-template for the provided name
/**
* Creates a new {@link CacheConfigurationBuilder} seeded with the cache-template configuration
* by the given {@code name} in the parsed XML configuration.
*
* @param name the unique name identifying the cache-template element in the XML
* @param keyType the type of keys for the {@link CacheConfigurationBuilder} to use, must
* match the {@code key-type} declared in the template if declared in XML
* @param valueType the type of values for the {@link CacheConfigurationBuilder} to use, must
* match the {@code value-type} declared in the template if declared in XML
* @param resourcePoolsBuilder Resources definitions that will be used
* @param <K> type of keys
* @param <V> type of values
*
* @return the preconfigured {@link CacheConfigurationBuilder}
* or {@code null} if no cache-template for the provided {@code name}
*
* @throws IllegalArgumentException if {@code keyType} or {@code valueType} don't match the declared type(s) of the template
* @throws ClassNotFoundException if a {@link java.lang.Class} declared in the XML couldn't be found
* @throws InstantiationException if a user provided {@link java.lang.Class} couldn't get instantiated
* @throws IllegalAccessException if a method (including constructor) couldn't be invoked on a user provided type
*/
@SuppressWarnings("unchecked")
public <K, V> CacheConfigurationBuilder<K, V> newCacheConfigurationBuilderFromTemplate(final String name,
final Class<K> keyType,
final Class<V> valueType,
final Builder<? extends ResourcePools> resourcePoolsBuilder)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return newCacheConfigurationBuilderFromTemplate(name, keyType, valueType, resourcePoolsBuilder.build());
}
@Override
public Map<String, CacheConfiguration<?, ?>> getCacheConfigurations() {
return configuration.getCacheConfigurations();
}
@Override
public Collection<ServiceCreationConfiguration<?>> getServiceCreationConfigurations() {
return configuration.getServiceCreationConfigurations();
}
@Override
public ClassLoader getClassLoader() {
return configuration.getClassLoader();
}
public interface Template {
<K, V> CacheConfigurationBuilder<K,V> builderFor(ClassLoader classLoader, Class<K> keyType, Class<V> valueType, ResourcePools resourcePools) throws ClassNotFoundException, InstantiationException, IllegalAccessException;
}
public static Class<?> getClassForName(String name, ClassLoader classLoader) throws ClassNotFoundException {
return PRETTY_FORMATS.stream().filter(p -> p.applies().test(name)).findFirst().map(PrettyClassFormat::lookup).orElseThrow(AssertionError::new).lookup(name, classLoader);
}
private static final List<PrettyClassFormat> PRETTY_FORMATS = asList(
//Primitive Types
when("boolean"::equals).then((n, l) -> Boolean.TYPE),
when("byte"::equals).then((n, l) -> Byte.TYPE),
when("short"::equals).then((n, l) -> Short.TYPE),
when("int"::equals).then((n, l) -> Integer.TYPE),
when("long"::equals).then((n, l) -> Long.TYPE),
when("char"::equals).then((n, l) -> Character.TYPE),
when("float"::equals).then((n, l) -> Float.TYPE),
when("double"::equals).then((n, l) -> Double.TYPE),
//Java Language Array Syntax
when(n -> n.endsWith("[]")).then((n, l) -> {
String component = n.split("(\\[\\])+$", 2)[0];
int dimensions = (n.length() - component.length()) >> 1;
return Array.newInstance(getClassForName(component, l), new int[dimensions]).getClass();
}),
//Inner Classes
when(n -> n.contains(".")).then((n, l) -> {
try {
return forName(n, false, l);
} catch (ClassNotFoundException e) {
int innerSeperator = n.lastIndexOf(".");
if (innerSeperator == -1) {
throw e;
} else {
return forName(n.substring(0, innerSeperator) + "$" + n.substring(innerSeperator + 1), false, l);
}
}
}),
//Everything Else
when(n -> true).then((n, l) -> forName(n, false, l))
);
interface PrettyClassFormat {
static Builder when(Predicate<String> predicate) {
return lookup -> new PrettyClassFormat() {
@Override
public Predicate<String> applies() {
return predicate;
}
@Override
public Lookup lookup() {
return lookup;
}
};
}
Predicate<String> applies();
Lookup lookup();
interface Builder {
PrettyClassFormat then(Lookup lookup);
}
}
private interface Lookup {
Class<?> lookup(String name, ClassLoader loader) throws ClassNotFoundException;
}
}