/*
* 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.jsr107;
import org.ehcache.config.Configuration;
import org.ehcache.core.config.DefaultConfiguration;
import org.ehcache.core.internal.util.ClassLoading;
import org.ehcache.core.spi.service.ServiceUtils;
import org.ehcache.impl.config.serializer.DefaultSerializationProviderConfiguration;
import org.ehcache.jsr107.config.Jsr107Configuration;
import org.ehcache.jsr107.config.Jsr107Service;
import org.ehcache.jsr107.internal.DefaultJsr107Service;
import org.ehcache.spi.service.Service;
import org.ehcache.xml.XmlConfiguration;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Properties;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.cache.CacheManager;
import javax.cache.configuration.OptionalFeature;
import javax.cache.spi.CachingProvider;
/**
* {@link CachingProvider} implementation for Ehcache.
*/
public class EhcacheCachingProvider implements CachingProvider {
private static final String DEFAULT_URI_STRING = "urn:X-ehcache:jsr107-default-config";
private static final URI URI_DEFAULT;
private final Map<ClassLoader, ConcurrentMap<URI, Eh107CacheManager>> cacheManagers = new WeakHashMap<ClassLoader, ConcurrentMap<URI, Eh107CacheManager>>();
static {
try {
URI_DEFAULT = new URI(DEFAULT_URI_STRING);
} catch (URISyntaxException e) {
throw new javax.cache.CacheException(e);
}
}
/**
* {@inheritDoc}
*/
@Override
public CacheManager getCacheManager(URI uri, ClassLoader classLoader, Properties properties) {
uri = uri == null ? getDefaultURI() : uri;
classLoader = classLoader == null ? getDefaultClassLoader() : classLoader;
properties = properties == null ? new Properties() : cloneProperties(properties);
if (URI_DEFAULT.equals(uri)) {
URI override = DefaultConfigurationResolver.resolveConfigURI(properties);
if (override != null) {
uri = override;
}
}
return getCacheManager(new ConfigSupplier(uri, classLoader), properties);
}
/**
* Enables to create a JSR-107 {@link CacheManager} based on the provided Ehcache {@link Configuration}.
*
* @param uri the URI identifying this cache manager
* @param config the Ehcache configuration to use
*
* @return a cache manager
*/
public CacheManager getCacheManager(URI uri, Configuration config) {
return getCacheManager(new ConfigSupplier(uri, config), new Properties());
}
/**
* Enables to create a JSR-107 {@link CacheManager} based on the provided Ehcache {@link Configuration} with the
* provided {@link Properties}.
*
* @param uri the URI identifying this cache manager
* @param config the Ehcache configuration to use
* @param properties extra properties
*
* @return a cache manager
*/
public CacheManager getCacheManager(URI uri, Configuration config, Properties properties) {
return getCacheManager(new ConfigSupplier(uri, config), properties);
}
Eh107CacheManager getCacheManager(ConfigSupplier configSupplier, Properties properties) {
Eh107CacheManager cacheManager;
ConcurrentMap<URI, Eh107CacheManager> byURI;
final ClassLoader classLoader = configSupplier.getClassLoader();
final URI uri = configSupplier.getUri();
synchronized (cacheManagers) {
byURI = cacheManagers.get(classLoader);
if (byURI == null) {
byURI = new ConcurrentHashMap<URI, Eh107CacheManager>();
cacheManagers.put(classLoader, byURI);
}
cacheManager = byURI.get(uri);
if (cacheManager == null || cacheManager.isClosed()) {
if(cacheManager != null) {
byURI.remove(uri, cacheManager);
}
cacheManager = createCacheManager(uri, configSupplier.getConfiguration(), properties);
byURI.put(uri, cacheManager);
}
}
return cacheManager;
}
private Eh107CacheManager createCacheManager(URI uri, Configuration config, Properties properties) {
Eh107CacheLoaderWriterProvider cacheLoaderWriterFactory = new Eh107CacheLoaderWriterProvider();
Object[] serviceCreationConfigurations = config.getServiceCreationConfigurations().toArray();
Jsr107Service jsr107Service = new DefaultJsr107Service(ServiceUtils.findSingletonAmongst(Jsr107Configuration.class, serviceCreationConfigurations));
Collection<Service> services = new ArrayList<Service>(4);
services.add(cacheLoaderWriterFactory);
services.add(jsr107Service);
if (ServiceUtils.findSingletonAmongst(DefaultSerializationProviderConfiguration.class, serviceCreationConfigurations) == null) {
services.add(new DefaultJsr107SerializationProvider());
}
Eh107InternalCacheManager ehcacheManager = new Eh107InternalCacheManager(config, services, !jsr107Service.jsr107CompliantAtomics());
ehcacheManager.init();
return new Eh107CacheManager(this, ehcacheManager, properties, config.getClassLoader(), uri,
new ConfigurationMerger(config, jsr107Service, cacheLoaderWriterFactory));
}
/**
* {@inheritDoc}
*/
@Override
public ClassLoader getDefaultClassLoader() {
return ClassLoading.getDefaultClassLoader();
}
/**
* {@inheritDoc}
*/
@Override
public URI getDefaultURI() {
return URI_DEFAULT;
}
/**
* {@inheritDoc}
*/
@Override
public Properties getDefaultProperties() {
return new Properties();
}
/**
* {@inheritDoc}
*/
@Override
public CacheManager getCacheManager(final URI uri, final ClassLoader classLoader) {
return getCacheManager(uri, classLoader, null);
}
/**
* {@inheritDoc}
*/
@Override
public CacheManager getCacheManager() {
return getCacheManager(getDefaultURI(), getDefaultClassLoader());
}
/**
* {@inheritDoc}
*/
@Override
public void close() {
synchronized (cacheManagers) {
for (Map.Entry<ClassLoader, ConcurrentMap<URI, Eh107CacheManager>> entry : cacheManagers.entrySet()) {
for (Eh107CacheManager cacheManager : entry.getValue().values()) {
cacheManager.close();
}
}
cacheManagers.clear();
}
}
/**
* {@inheritDoc}
*/
@Override
public void close(final ClassLoader classLoader) {
if (classLoader == null) {
throw new NullPointerException();
}
MultiCacheException closeException = new MultiCacheException();
synchronized (cacheManagers) {
final ConcurrentMap<URI, Eh107CacheManager> map = cacheManagers.remove(classLoader);
if (map != null) {
for (Eh107CacheManager cacheManager : map.values()) {
cacheManager.closeInternal(closeException);
}
}
}
closeException.throwIfNotEmpty();
}
/**
* {@inheritDoc}
*/
@Override
public void close(final URI uri, final ClassLoader classLoader) {
if (uri == null || classLoader == null) {
throw new NullPointerException();
}
MultiCacheException closeException = new MultiCacheException();
synchronized (cacheManagers) {
final ConcurrentMap<URI, Eh107CacheManager> map = cacheManagers.get(classLoader);
if (map != null) {
final Eh107CacheManager cacheManager = map.remove(uri);
if (cacheManager != null) {
cacheManager.closeInternal(closeException);
}
}
}
closeException.throwIfNotEmpty();
}
/**
* {@inheritDoc}
*/
@Override
public boolean isSupported(final OptionalFeature optionalFeature) {
if (optionalFeature == null) {
throw new NullPointerException();
}
// this switch statement written w/o "default:" to let use know if a new
// optional feature is added in the spec
switch (optionalFeature) {
case STORE_BY_REFERENCE:
return true;
}
throw new IllegalArgumentException("Unknown OptionalFeature: " + optionalFeature.name());
}
void close(Eh107CacheManager cacheManager, MultiCacheException closeException) {
try {
synchronized (cacheManagers) {
final ConcurrentMap<URI, Eh107CacheManager> map = cacheManagers.get(cacheManager.getClassLoader());
if (map != null && map.remove(cacheManager.getURI()) != null) {
cacheManager.closeInternal(closeException);
}
}
} catch (Throwable t) {
closeException.addThrowable(t);
}
}
private static Properties cloneProperties(Properties properties) {
Properties clone = new Properties();
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
clone.put(entry.getKey(), entry.getValue());
}
return clone;
}
static class ConfigSupplier {
private final URI uri;
private final ClassLoader classLoader;
private Configuration configuration;
public ConfigSupplier(URI uri, ClassLoader classLoader) {
this.uri = uri;
this.classLoader = classLoader;
this.configuration = null;
}
public ConfigSupplier(URI uri, Configuration configuration) {
this.uri = uri;
this.classLoader = configuration.getClassLoader();
this.configuration = configuration;
}
public URI getUri() {
return uri;
}
public ClassLoader getClassLoader() {
return classLoader;
}
public Configuration getConfiguration() {
if(configuration == null) {
try {
if (URI_DEFAULT.equals(uri)) {
configuration = new DefaultConfiguration(classLoader);
} else {
configuration = new XmlConfiguration(uri.toURL(), classLoader);
}
} catch (Exception e) {
throw new javax.cache.CacheException(e);
}
}
return configuration;
}
}
}