package org.cache2k.jcache.provider;
/*
* #%L
* cache2k JCache provider
* %%
* Copyright (C) 2000 - 2017 headissue GmbH, Munich
* %%
* 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.
* #L%
*/
import org.cache2k.core.spi.CacheLifeCycleListener;
import org.cache2k.core.CacheManagerImpl;
import org.cache2k.jcache.provider.generic.storeByValueSimulation.CopyCacheProxy;
import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.CacheManager;
import javax.cache.configuration.Configuration;
import javax.cache.spi.CachingProvider;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.WeakHashMap;
/**
* @author Jens Wilke; created: 2015-03-27
*/
public class JCacheManagerAdapter implements CacheManager {
private final static JCacheJmxSupport jmxSupport = findJCacheJmxSupportInstance();
/**
* The JMX support is already created via the serviceloader
*/
private static JCacheJmxSupport findJCacheJmxSupportInstance() {
for (CacheLifeCycleListener l : CacheManagerImpl.getCacheLifeCycleListeners()) {
if (l instanceof JCacheJmxSupport) {
return (JCacheJmxSupport) l;
}
}
return null;
}
private org.cache2k.CacheManager manager;
private JCacheProvider provider;
/**
* All caches. Currently closed caches stay in the map.
* Guarded by getLockObject().
*/
private Map<String, Cache> name2adapter = new HashMap<String, Cache>();
/**
* Needed for event delivery to find the corresponding JCache for a cache2k cache.
* Guarded by getLockObject().
*/
private volatile Map<org.cache2k.Cache, Cache> c2k2jCache = Collections.emptyMap();
public JCacheManagerAdapter(JCacheProvider p, org.cache2k.CacheManager cm) {
manager = cm;
provider = p;
}
@Override
public CachingProvider getCachingProvider() {
return provider;
}
@Override
public URI getURI() {
return provider.name2Uri(manager.getName());
}
@Override
public ClassLoader getClassLoader() {
return manager.getClassLoader();
}
@Override
public Properties getProperties() {
return manager.getProperties();
}
public <K, V, C extends Configuration<K, V>> Cache<K, V> createCache(String _cacheName, C cfg)
throws IllegalArgumentException {
checkClosed();
checkNonNullCacheName(_cacheName);
synchronized (getLockObject()) {
Cache _jsr107cache = name2adapter.get(_cacheName);
if (_jsr107cache != null && !_jsr107cache.isClosed()) {
throw new CacheException("cache already existing with name: " + _cacheName);
}
org.cache2k.Cache _existingCache = manager.getCache(_cacheName);
if (_existingCache != null && !_existingCache.isClosed()) {
throw new CacheException("A cache2k instance is already existing with name: " + _cacheName);
}
JCacheBuilder<K,V> _builder = new JCacheBuilder<K, V>(_cacheName, this);
_builder.setConfiguration(cfg);
Cache<K,V> _cache = _builder.build();
org.cache2k.Cache _cache2k = _cache.unwrap(org.cache2k.Cache.class);
Map<org.cache2k.Cache, Cache> _cloneC2k2jCache = new WeakHashMap<org.cache2k.Cache, Cache>();
_cloneC2k2jCache.putAll(c2k2jCache);
_cloneC2k2jCache.put(_cache2k, _cache);
c2k2jCache = _cloneC2k2jCache;
name2adapter.put(_cache.getName(), _cache);
if (_builder.getCompleteConfiguration().isStatisticsEnabled()) {
enableStatistics(_cacheName, true);
}
if (_builder.getCompleteConfiguration().isManagementEnabled()) {
enableManagement(_cacheName, true);
}
return _cache;
}
}
private Object getLockObject() {
return ((CacheManagerImpl) manager).getLockObject();
}
@Override @SuppressWarnings("unchecked")
public <K, V> Cache<K, V> getCache(String _cacheName, final Class<K> _keyType, final Class<V> _valueType) {
if (_keyType == null || _valueType == null) {
throw new NullPointerException();
}
Cache<K, V> c = getCache(_cacheName);
if (c == null) {
return null;
}
Configuration cfg = c.getConfiguration(Configuration.class);
if (!cfg.getKeyType().equals(_keyType)) {
throw new ClassCastException("key type mismatch, expected: " + cfg.getKeyType().getName());
}
if (!cfg.getValueType().equals(_valueType)) {
throw new ClassCastException("value type mismatch, expected: " + cfg.getValueType().getName());
}
return c;
}
private JCacheAdapter getAdapter(String _name) {
Cache ca = name2adapter.get(_name);
if (ca instanceof CopyCacheProxy) {
ca = ((CopyCacheProxy) ca).getWrappedCache();
}
if (ca instanceof TouchyJCacheAdapter) {
return ((TouchyJCacheAdapter) ca).cache;
}
return (JCacheAdapter) ca;
}
private void checkNonNullCacheName(String _cacheName) {
if (_cacheName == null) {
throw new NullPointerException("cache name is null");
}
}
@SuppressWarnings("unchecked")
@Override
public <K, V> Cache<K, V> getCache(String _cacheName) {
checkClosed();
checkNonNullCacheName(_cacheName);
synchronized (getLockObject()) {
Cache<K, V> c = name2adapter.get(_cacheName);
if (c != null && manager.getCache(_cacheName) == c.unwrap(org.cache2k.Cache.class) && !c.isClosed()) {
return c;
}
}
return null;
}
@Override
public Iterable<String> getCacheNames() {
checkClosed();
Set<String> _names = new HashSet<String>();
for (org.cache2k.Cache c : manager.getActiveCaches()) {
_names.add(c.getName());
}
return Collections.unmodifiableSet(_names);
}
@Override
public void destroyCache(String _cacheName) {
checkClosed();
checkNonNullCacheName(_cacheName);
org.cache2k.Cache c = manager.getCache(_cacheName);
if (c != null) {
c.close();
}
}
@Override
public void enableManagement(String _cacheName, boolean enabled) {
checkClosed();
checkNonNullCacheName(_cacheName);
synchronized (getLockObject()) {
JCacheAdapter ca = getAdapter(_cacheName);
if (ca == null) {
return;
}
Cache c = name2adapter.get(_cacheName);
if (enabled) {
if (!ca.configurationEnabled) {
jmxSupport.enableConfiguration(ca.cache, c);
ca.configurationEnabled = true;
}
} else {
if (ca.configurationEnabled) {
jmxSupport.disableConfiguration(ca.cache);
ca.configurationEnabled = false;
}
}
}
}
private void checkClosed() {
if (isClosed()) {
throw new IllegalStateException("cache manager is closed");
}
}
@Override
public void enableStatistics(String _cacheName, boolean enabled) {
checkClosed();
checkNonNullCacheName(_cacheName);
synchronized (getLockObject()) {
if (enabled) {
JCacheAdapter ca = getAdapter(_cacheName);
if (ca != null) {
synchronized (ca.cache) {
if (!ca.statisticsEnabled) {
jmxSupport.enableStatistics(ca);
ca.statisticsEnabled = true;
}
}
}
} else {
JCacheAdapter ca = getAdapter(_cacheName);
if (ca != null) {
synchronized (ca.cache) {
if (ca.statisticsEnabled) {
jmxSupport.disableStatistics(ca.cache);
ca.statisticsEnabled = false;
}
}
}
}
}
}
@Override
public void close() {
manager.close();
}
@Override
public boolean isClosed() {
return manager.isClosed();
}
@Override
public <T> T unwrap(Class<T> _class) {
if (org.cache2k.CacheManager.class.isAssignableFrom(_class)) {
return (T) manager;
}
throw new IllegalArgumentException("requested unwrap class not available");
}
public org.cache2k.CacheManager getCache2kManager() {
return manager;
}
/**
* Return the JCache wrapper for a c2k cache.
*/
public Cache resolveCacheWrapper(org.cache2k.Cache _c2kCache) {
synchronized (getLockObject()) {
return c2k2jCache.get(_c2kCache);
}
}
}