/*
* 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.
Status;
import org.ehcache.config.
CacheConfiguration;
import org.ehcache.core.
InternalCache;
import org.ehcache.impl.config.copy.
DefaultCopierConfiguration;
import org.ehcache.impl.copy.
IdentityCopier;
import org.ehcache.jsr107.internal.
Jsr107CacheLoaderWriter;
import org.ehcache.jsr107.internal.
WrappedCacheLoaderWriter;
import org.ehcache.spi.loaderwriter.
CacheLoaderWriter;
import org.ehcache.spi.service.
ServiceConfiguration;
import org.slf4j.
Logger;
import org.slf4j.
LoggerFactory;
import java.lang.management.
ManagementFactory;
import java.net.
URI;
import java.util.
ArrayList;
import java.util.
Collections;
import java.util.
Map;
import java.util.
Properties;
import java.util.concurrent.
ConcurrentHashMap;
import java.util.concurrent.
ConcurrentMap;
import javax.cache.
Cache;
import javax.cache.
CacheException;
import javax.cache.
CacheManager;
import javax.cache.configuration.
Configuration;
import javax.cache.spi.
CachingProvider;
import javax.management.
InstanceAlreadyExistsException;
import javax.management.
InstanceNotFoundException;
import javax.management.
MBeanServer;
/**
* @author teck
*/
class
Eh107CacheManager implements
CacheManager {
private static final
Logger LOG =
LoggerFactory.
getLogger(
Eh107CacheManager.class);
private static
MBeanServer MBEAN_SERVER =
ManagementFactory.
getPlatformMBeanServer();
private final
Object cachesLock = new
Object();
private final
ConcurrentMap<
String,
Eh107Cache<?, ?>>
caches = new
ConcurrentHashMap<
String,
Eh107Cache<?, ?>>();
private final
Eh107InternalCacheManager ehCacheManager;
private final
EhcacheCachingProvider cachingProvider;
private final
ClassLoader classLoader;
private final
URI uri;
private final
Properties props;
private final
ConfigurationMerger configurationMerger;
Eh107CacheManager(
EhcacheCachingProvider cachingProvider,
Eh107InternalCacheManager ehCacheManager,
Properties props,
ClassLoader classLoader,
URI uri,
ConfigurationMerger configurationMerger) {
this.
cachingProvider =
cachingProvider;
this.
ehCacheManager =
ehCacheManager;
this.
props =
props;
this.
classLoader =
classLoader;
this.
uri =
uri;
this.
configurationMerger =
configurationMerger;
refreshAllCaches();
}
Eh107InternalCacheManager getEhCacheManager() {
return
ehCacheManager;
}
private void
refreshAllCaches() {
for (
Map.
Entry<
String,
CacheConfiguration<?, ?>>
entry :
ehCacheManager.
getRuntimeConfiguration().
getCacheConfigurations().
entrySet()) {
String name =
entry.
getKey();
CacheConfiguration<?, ?>
config =
entry.
getValue();
caches.
putIfAbsent(
name,
wrapEhcacheCache(
name,
config));
}
for (
Map.
Entry<
String,
Eh107Cache<?, ?>>
namedCacheEntry :
caches.
entrySet()) {
Eh107Cache<?, ?>
cache =
namedCacheEntry.
getValue();
if (!
cache.
isClosed()) {
@
SuppressWarnings("unchecked")
Eh107Configuration<?, ?>
configuration =
cache.
getConfiguration(
Eh107Configuration.class);
if (
configuration.
isManagementEnabled()) {
enableManagement(
cache, true);
}
if (
configuration.
isStatisticsEnabled()) {
enableStatistics(
cache, true);
}
}
}
}
private <K, V>
Eh107Cache<K, V>
wrapEhcacheCache(
String alias,
CacheConfiguration<K, V>
ehConfig) {
org.ehcache.
Cache<K, V>
cache =
ehCacheManager.
getCache(
alias,
ehConfig.
getKeyType(),
ehConfig.
getValueType());
return
wrapEhcacheCache(
alias, (
InternalCache<K, V>)
cache);
}
private <K, V>
Eh107Cache<K, V>
wrapEhcacheCache(
String alias,
InternalCache<K, V>
cache) {
CacheLoaderWriter<? super K, V>
cacheLoaderWriter =
cache.
getCacheLoaderWriter();
boolean
storeByValueOnHeap = false;
for (
ServiceConfiguration<?>
serviceConfiguration :
cache.
getRuntimeConfiguration().
getServiceConfigurations()) {
if (
serviceConfiguration instanceof
DefaultCopierConfiguration) {
DefaultCopierConfiguration<?>
copierConfig = (
DefaultCopierConfiguration)
serviceConfiguration;
if(!
copierConfig.
getClazz().
isAssignableFrom(
IdentityCopier.class))
storeByValueOnHeap = true;
break;
}
}
Eh107Configuration<K, V>
config = new
Eh107ReverseConfiguration<K, V>(
cache,
cacheLoaderWriter != null,
cacheLoaderWriter != null,
storeByValueOnHeap);
configurationMerger.
setUpManagementAndStats(
cache,
config);
Eh107Expiry<K, V>
expiry = new
EhcacheExpiryWrapper<K, V>(
cache.
getRuntimeConfiguration().
getExpiry());
CacheResources<K, V>
resources = new
CacheResources<K, V>(
alias,
wrapCacheLoaderWriter(
cacheLoaderWriter),
expiry);
return new
Eh107Cache<K, V>(
alias,
config,
resources,
cache, this);
}
private <K, V>
Jsr107CacheLoaderWriter<K, V>
wrapCacheLoaderWriter(
CacheLoaderWriter<K, V>
cacheLoaderWriter) {
return new
WrappedCacheLoaderWriter<K, V>(
cacheLoaderWriter);
}
@
Override
public
CachingProvider getCachingProvider() {
return this.
cachingProvider;
}
@
Override
public
URI getURI() {
return this.
uri;
}
@
Override
public
ClassLoader getClassLoader() {
return this.
classLoader;
}
@
Override
public
Properties getProperties() {
return new
Properties(
props);
}
@
Override
public <K, V, C extends
Configuration<K, V>>
Cache<K, V> createCache(
String cacheName, C
config)
throws
IllegalArgumentException {
checkClosed();
// TCK expects the "closed" check before these null checks
if (
cacheName == null ||
config == null) {
throw new
NullPointerException();
}
synchronized (
cachesLock) {
if (
config instanceof
Eh107Configuration.
Eh107ConfigurationWrapper) {
Eh107Configuration.
Eh107ConfigurationWrapper<K, V>
configurationWrapper = (
Eh107Configuration.
Eh107ConfigurationWrapper<K, V>)
config;
CacheConfiguration<K, V>
unwrap =
configurationWrapper.
getCacheConfiguration();
final org.ehcache.
Cache<K, V>
ehcache;
try {
ehcache =
ehCacheManager.
createCache(
cacheName,
unwrap);
} catch (
IllegalArgumentException e) {
throw new
CacheException("A Cache named [" +
cacheName + "] already exists");
}
Eh107Cache<K, V>
cache =
wrapEhcacheCache(
cacheName, (
InternalCache<K, V>)
ehcache);
assert
safeCacheRetrieval(
cacheName) == null;
caches.
put(
cacheName,
cache);
@
SuppressWarnings("unchecked")
Eh107Configuration<?, ?>
configuration =
cache.
getConfiguration(
Eh107Configuration.class);
if (
configuration.
isManagementEnabled()) {
enableManagement(
cacheName, true);
}
if (
configuration.
isStatisticsEnabled()) {
enableStatistics(
cacheName, true);
}
return
cache;
}
ConfigurationMerger.
ConfigHolder<K, V>
configHolder =
configurationMerger.
mergeConfigurations(
cacheName,
config);
final
InternalCache<K, V>
ehCache;
try {
ehCache = (
InternalCache<K, V>)
ehCacheManager.
createCache(
cacheName,
configHolder.
cacheConfiguration);
} catch (
IllegalArgumentException e) {
MultiCacheException mce = new
MultiCacheException(
e);
configHolder.
cacheResources.
closeResources(
mce);
throw new
CacheException("A Cache named [" +
cacheName + "] already exists",
mce);
} catch (
Throwable t) {
// something went wrong in ehcache land, make sure to clean up our stuff
MultiCacheException mce = new
MultiCacheException(
t);
configHolder.
cacheResources.
closeResources(
mce);
throw
mce;
}
Eh107Cache<K, V>
cache = null;
CacheResources<K, V>
cacheResources =
configHolder.
cacheResources;
try {
if (
configHolder.
useEhcacheLoaderWriter) {
cacheResources = new
CacheResources<K, V>(
cacheName,
wrapCacheLoaderWriter(
ehCache.
getCacheLoaderWriter()),
cacheResources.
getExpiryPolicy(),
cacheResources.
getListenerResources());
}
cache = new
Eh107Cache<K, V>(
cacheName, new
Eh107CompleteConfiguration<K, V>(
configHolder.
jsr107Configuration,
ehCache
.
getRuntimeConfiguration()),
cacheResources,
ehCache, this);
caches.
put(
cacheName,
cache);
if (
configHolder.
jsr107Configuration.
isManagementEnabled()) {
enableManagement(
cacheName, true);
}
if (
configHolder.
jsr107Configuration.
isStatisticsEnabled()) {
enableStatistics(
cacheName, true);
}
return
cache;
} catch (
Throwable t) {
MultiCacheException mce = new
MultiCacheException(
t);
if (
cache != null) {
cache.
closeInternal(
mce);
} else {
cacheResources.
closeResources(
mce);
}
throw
mce;
}
}
}
private void
checkClosed() {
if (
isClosed()) {
throw new
IllegalStateException(this.
toString() + " is closed");
}
}
@
Override
public
String toString() {
return
getClass().
getSimpleName() + "[" +
uri + "]";
}
@
SuppressWarnings("unchecked")
@
Override
public <K, V>
Cache<K, V> getCache(
String cacheName,
Class<K>
keyType,
Class<V>
valueType) {
checkClosed();
if (
cacheName == null ||
keyType == null ||
valueType == null) {
throw new
NullPointerException();
}
Eh107Cache<K, V>
cache =
safeCacheRetrieval(
cacheName);
if (
cache == null) {
return null;
}
Class<?>
actualKeyType =
cache.
getConfiguration(
Configuration.class).
getKeyType();
Class<?>
actualValueType =
cache.
getConfiguration(
Configuration.class).
getValueType();
if (
keyType !=
actualKeyType) {
throw new
ClassCastException("Cache has key type " +
actualKeyType.
getName()
+ ", but getCache() called with key type " +
keyType.
getName());
}
if (
valueType !=
actualValueType) {
throw new
ClassCastException("Cache has value type " +
actualValueType.
getName()
+ ", but getCache() called with value type " +
valueType.
getName());
}
return
cache;
}
@
SuppressWarnings("unchecked")
@
Override
public <K, V>
Cache<K, V> getCache(
String cacheName) {
checkClosed();
if (
cacheName == null) {
throw new
NullPointerException();
}
Eh107Cache<K, V>
cache =
safeCacheRetrieval(
cacheName);
if (
cache == null) {
return null;
}
if (
cache.
getConfiguration(
Configuration.class).
getKeyType() !=
Object.class
||
cache.
getConfiguration(
Configuration.class).
getValueType() !=
Object.class) {
throw new
IllegalArgumentException("Cache [" +
cacheName
+ "] specifies key/value types. Use getCache(String, Class, Class)");
}
return
cache;
}
@
SuppressWarnings("unchecked")
private <K, V>
Eh107Cache<K, V>
safeCacheRetrieval(final
String cacheName) {
final
Eh107Cache<?, ?>
eh107Cache =
caches.
get(
cacheName);
if(
eh107Cache != null &&
eh107Cache.
isClosed()) {
return null;
}
return (
Eh107Cache<K, V>)
eh107Cache;
}
@
Override
public
Iterable<
String>
getCacheNames() {
refreshAllCaches();
return
Collections.
unmodifiableList(new
ArrayList<
String>(
caches.
keySet()));
}
@
Override
public void
destroyCache(
String cacheName) {
if (
cacheName == null) {
throw new
NullPointerException();
}
MultiCacheException destroyException = new
MultiCacheException();
synchronized (
cachesLock) {
checkClosed();
Eh107Cache<?, ?>
cache =
caches.
remove(
cacheName);
if (
cache == null) {
// TCK expects this method to return w/o exception if named cache does
// not exist
return;
}
try {
enableManagement(
cache, false);
} catch (
Throwable t) {
destroyException.
addThrowable(
t);
}
try {
enableStatistics(
cache, false);
} catch (
Throwable t) {
destroyException.
addThrowable(
t);
}
cache.
destroy(
destroyException);
try {
ehCacheManager.
removeCache(
cache.
getName());
} catch (
Throwable t) {
destroyException.
addThrowable(
t);
}
}
destroyException.
throwIfNotEmpty();
}
@
Override
public void
enableManagement(
String cacheName, boolean
enabled) {
checkClosed();
if (
cacheName == null) {
throw new
NullPointerException();
}
Eh107Cache<?, ?>
cache =
safeCacheRetrieval(
cacheName);
if (
cache == null) {
throw new
IllegalArgumentException("No such Cache named " +
cacheName);
}
enableManagement(
cache,
enabled);
}
private void
enableManagement(
Eh107Cache<?, ?>
cache, boolean
enabled) {
synchronized (
cachesLock) {
checkClosed();
if (
enabled) {
registerObject(
cache.
getManagementMBean());
} else {
unregisterObject(
cache.
getManagementMBean());
}
cache.
setManagementEnabled(
enabled);
}
}
private void
unregisterObject(
Eh107MXBean bean) {
try {
MBEAN_SERVER.
unregisterMBean(
bean.
getObjectName());
} catch (
InstanceNotFoundException e) {
// ignore
} catch (
Exception e) {
throw new
CacheException(
e);
}
}
private void
registerObject(
Eh107MXBean bean) {
try {
LOG.
info("Registering Ehcache MBean {}",
bean.
getObjectName());
MBEAN_SERVER.
registerMBean(
bean,
bean.
getObjectName());
} catch (
InstanceAlreadyExistsException e) {
// ignore
} catch (
Exception e) {
throw new
CacheException(
e);
}
}
@
Override
public void
enableStatistics(
String cacheName, boolean
enabled) {
checkClosed();
if (
cacheName == null) {
throw new
NullPointerException();
}
Eh107Cache<?, ?>
cache =
safeCacheRetrieval(
cacheName);
if (
cache == null) {
throw new
IllegalArgumentException("No such Cache named " +
cacheName);
}
enableStatistics(
cache,
enabled);
}
private void
enableStatistics(
Eh107Cache<?, ?>
cache, boolean
enabled) {
synchronized (
cachesLock) {
checkClosed();
if (
enabled) {
registerObject(
cache.
getStatisticsMBean());
} else {
unregisterObject(
cache.
getStatisticsMBean());
}
cache.
setStatisticsEnabled(
enabled);
}
}
@
Override
public boolean
isClosed() {
return
ehCacheManager.
getStatus() ==
Status.
UNINITIALIZED;
}
@
Override
public <T> T
unwrap(
Class<T>
clazz) {
return
Unwrap.
unwrap(
clazz, this,
ehCacheManager);
}
@
Override
public void
close() {
MultiCacheException closeException = new
MultiCacheException();
cachingProvider.
close(this,
closeException);
closeException.
throwIfNotEmpty();
}
void
closeInternal(
MultiCacheException closeException) {
try {
synchronized (
cachesLock) {
for (
Eh107Cache<?, ?>
cache :
caches.
values()) {
try {
close(
cache,
closeException);
} catch (
Throwable t) {
closeException.
addThrowable(
t);
}
}
try {
caches.
clear();
} catch (
Throwable t) {
closeException.
addThrowable(
t);
}
try {
ehCacheManager.
close();
} catch (
Throwable t) {
closeException.
addThrowable(
t);
}
}
} catch (
Throwable t) {
closeException.
addThrowable(
t);
}
}
void
close(
Eh107Cache<?, ?>
cache,
MultiCacheException closeException) {
try {
if (
caches.
remove(
cache.
getName(),
cache)) {
try {
unregisterObject(
cache.
getManagementMBean());
} catch (
Throwable t) {
closeException.
addThrowable(
t);
}
try {
unregisterObject(
cache.
getStatisticsMBean());
} catch (
Throwable t) {
closeException.
addThrowable(
t);
}
try {
cache.
closeInternal(
closeException);
} catch (
Throwable t) {
closeException.
addThrowable(
t);
}
try {
ehCacheManager.
removeCache(
cache.
getName());
} catch (
Throwable t) {
closeException.
addThrowable(
t);
}
}
} catch (
Throwable t) {
closeException.
addThrowable(
t);
}
}
}