blob: 13a94ed939d673dd31023bb2ec6acb8dd0355913 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.geronimo.jcache.simple;
import static java.util.Collections.unmodifiableSet;
import static org.apache.geronimo.jcache.simple.Asserts.assertNotNull;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.CacheManager;
import javax.cache.configuration.Configuration;
import javax.cache.spi.CachingProvider;
public class SimpleManager implements CacheManager {
private final CachingProvider provider;
private final URI uri;
private final ClassLoader loader;
private final Properties properties;
private final ConcurrentMap<String, Cache<?, ?>> caches = new ConcurrentHashMap<>();
private final Properties configProperties;
private final ExecutorService executorService;
private volatile boolean closed = false;
SimpleManager(final CachingProvider provider, final URI uri, final ClassLoader loader, final Properties properties) {
this.provider = provider;
this.uri = uri;
this.loader = loader;
this.properties = readConfig(uri, loader, properties);
this.configProperties = properties;
ExecutorService executorService = rawProperty("geronimo.pool");
if (executorService == null) {
final int poolSize = Integer.parseInt(this.properties.getProperty("pool.size", "16"));
final SimpleThreadFactory threadFactory = new SimpleThreadFactory("geronimo-simple-jcache-[" + uri.toASCIIString() + "]-");
executorService = poolSize > 0 ? Executors.newFixedThreadPool(poolSize, threadFactory)
: Executors.newCachedThreadPool(threadFactory);
}
this.executorService = executorService;
}
private <T> T rawProperty(final String name) {
final Object value = this.properties.get(name);
if (value == null) {
return (T) this.properties.get(name);
}
return (T) value;
}
private Properties readConfig(final URI uri, final ClassLoader loader, final Properties properties) {
final Properties props = new Properties();
try {
if (SimpleProvider.DEFAULT_URI.toString().equals(uri.toString()) || uri.getScheme().equals("geronimo")
|| uri.getScheme().equals("classpath")) {
final Enumeration<URL> resources = loader.getResources(uri.toASCIIString().substring((uri.getScheme() + "://").length()));
while (resources.hasMoreElements()) {
do {
addProperties(resources.nextElement(), props);
} while (resources.hasMoreElements());
}
} else {
props.load(uri.toURL().openStream());
}
} catch (final IOException e) {
throw new IllegalStateException(e);
}
if (properties != null) {
props.putAll(properties);
}
return props;
}
private void addProperties(final URL url, final Properties aggregator) {
try (final InputStream inputStream = url.openStream()) {
aggregator.load(inputStream);
} catch (final IOException e) {
throw new IllegalArgumentException(e);
}
}
private void assertNotClosed() {
if (isClosed()) {
throw new IllegalStateException("cache manager closed");
}
}
@Override
public <K, V, C extends Configuration<K, V>> Cache<K, V> createCache(final String cacheName, final C configuration)
throws IllegalArgumentException {
assertNotClosed();
assertNotNull(cacheName, "cacheName");
assertNotNull(configuration, "configuration");
final Class<K> keyType = configuration.getKeyType();
final Class<V> valueType = configuration.getValueType();
if (!caches.containsKey(cacheName)) {
final Cache<K, V> cache = ClassLoaderAwareCache.wrap(loader, new SimpleCache<K, V>(loader, this, cacheName,
new SimpleConfiguration<>(configuration, keyType, valueType), properties, executorService));
caches.putIfAbsent(cacheName, cache);
} else {
throw new CacheException("cache " + cacheName + " already exists");
}
return (Cache<K, V>) getCache(cacheName, keyType, valueType);
}
@Override
public void destroyCache(final String cacheName) {
assertNotClosed();
assertNotNull(cacheName, "cacheName");
final Cache<?, ?> cache = caches.remove(cacheName);
if (cache != null && !cache.isClosed()) {
cache.clear();
cache.close();
}
}
@Override
public void enableManagement(final String cacheName, final boolean enabled) {
assertNotClosed();
assertNotNull(cacheName, "cacheName");
final SimpleCache<?, ?> cache = of(cacheName);
if (cache != null) {
if (enabled) {
cache.enableManagement();
} else {
cache.disableManagement();
}
}
}
private SimpleCache<?, ?> of(final String cacheName) {
return SimpleCache.class.cast(ClassLoaderAwareCache.getDelegate(caches.get(cacheName)));
}
@Override
public void enableStatistics(final String cacheName, final boolean enabled) {
assertNotClosed();
assertNotNull(cacheName, "cacheName");
final SimpleCache<?, ?> cache = of(cacheName);
if (cache != null) {
if (enabled) {
cache.enableStatistics();
} else {
cache.disableStatistics();
}
}
}
@Override
public synchronized void close() {
if (isClosed()) {
return;
}
assertNotClosed();
for (final Cache<?, ?> c : caches.values()) {
c.close();
}
caches.clear();
for (final Runnable task : executorService.shutdownNow()) {
task.run();
}
closed = true;
if (SimpleProvider.class.isInstance(provider)) {
SimpleProvider.class.cast(provider).remove(this);
} // else throw?
}
@Override
public <T> T unwrap(final Class<T> clazz) {
if (clazz.isInstance(this)) {
return clazz.cast(this);
}
throw new IllegalArgumentException(clazz.getName() + " not supported in unwrap");
}
@Override
public boolean isClosed() {
return closed;
}
@Override
public <K, V> Cache<K, V> getCache(final String cacheName) {
assertNotClosed();
assertNotNull(cacheName, "cacheName");
return (Cache<K, V>) doGetCache(cacheName, null, null);
}
@Override
public Iterable<String> getCacheNames() {
assertNotClosed();
return unmodifiableSet(new HashSet<>(caches.keySet()));
}
@Override
public <K, V> Cache<K, V> getCache(final String cacheName, final Class<K> keyType, final Class<V> valueType) {
assertNotClosed();
assertNotNull(cacheName, "cacheName");
assertNotNull(keyType, "keyType");
assertNotNull(valueType, "valueType");
try {
return doGetCache(cacheName, keyType, valueType);
} catch (final IllegalArgumentException iae) {
throw new ClassCastException(iae.getMessage());
}
}
private <K, V> Cache<K, V> doGetCache(final String cacheName, final Class<K> keyType, final Class<V> valueType) {
final Cache<K, V> cache = (Cache<K, V>) caches.get(cacheName);
if (keyType == null && valueType == null) {
return cache;
}
if (cache == null) {
return null;
}
final Configuration<K, V> config = cache.getConfiguration(Configuration.class);
if ((keyType != null && !config.getKeyType().isAssignableFrom(keyType))
|| (valueType != null && !config.getValueType().isAssignableFrom(valueType))) {
throw new IllegalArgumentException(
"this cache is <" + config.getKeyType().getName() + ", " + config.getValueType().getName() + "> "
+ " and not <" + keyType.getName() + ", " + valueType.getName() + ">");
}
return cache;
}
@Override
public CachingProvider getCachingProvider() {
return provider;
}
@Override
public URI getURI() {
return uri;
}
@Override
public ClassLoader getClassLoader() {
return loader;
}
@Override
public Properties getProperties() {
return configProperties;
}
public void release(final String name) {
caches.remove(name);
}
}