/*
* 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.
CacheConfiguration;
import org.ehcache.config.builders.
CacheConfigurationBuilder;
import org.ehcache.core.
InternalCache;
import org.ehcache.core.spi.service.
ServiceUtils;
import org.ehcache.impl.config.copy.
DefaultCopierConfiguration;
import org.ehcache.impl.config.copy.
DefaultCopyProviderConfiguration;
import org.ehcache.impl.config.loaderwriter.
DefaultCacheLoaderWriterConfiguration;
import org.ehcache.impl.internal.classes.
ClassInstanceConfiguration;
import org.ehcache.impl.copy.
SerializingCopier;
import org.ehcache.jsr107.config.
ConfigurationElementState;
import org.ehcache.jsr107.config.
Jsr107CacheConfiguration;
import org.ehcache.jsr107.config.
Jsr107Service;
import org.ehcache.jsr107.internal.
Jsr107CacheLoaderWriter;
import org.ehcache.spi.copy.
Copier;
import org.ehcache.xml.
XmlConfiguration;
import org.slf4j.
Logger;
import org.slf4j.
LoggerFactory;
import java.util.
HashSet;
import java.util.
Map;
import java.util.
Set;
import java.util.concurrent.
ConcurrentHashMap;
import javax.cache.configuration.
CacheEntryListenerConfiguration;
import javax.cache.configuration.
CompleteConfiguration;
import javax.cache.configuration.
Configuration;
import javax.cache.configuration.
Factory;
import javax.cache.integration.
CacheLoader;
import javax.cache.integration.
CacheWriter;
import static org.ehcache.config.builders.
CacheConfigurationBuilder.newCacheConfigurationBuilder;
import static org.ehcache.config.builders.
ResourcePoolsBuilder.heap;
import static org.ehcache.core.spi.service.
ServiceUtils.findSingletonAmongst;
/**
* ConfigurationMerger
*/
class
ConfigurationMerger {
private static final
Logger LOG =
LoggerFactory.
getLogger(
ConfigurationMerger.class);
private final
XmlConfiguration xmlConfiguration;
private final
Jsr107Service jsr107Service;
private final
Eh107CacheLoaderWriterProvider cacheLoaderWriterFactory;
ConfigurationMerger(org.ehcache.config.
Configuration ehConfig,
Jsr107Service jsr107Service,
Eh107CacheLoaderWriterProvider cacheLoaderWriterFactory) {
if (
ehConfig instanceof
XmlConfiguration) {
xmlConfiguration = (
XmlConfiguration)
ehConfig;
} else {
xmlConfiguration = null;
}
this.
jsr107Service =
jsr107Service;
this.
cacheLoaderWriterFactory =
cacheLoaderWriterFactory;
}
<K, V>
ConfigHolder<K, V> mergeConfigurations(
String cacheName,
Configuration<K, V>
configuration) {
final
Eh107CompleteConfiguration<K, V>
jsr107Configuration = new
Eh107CompleteConfiguration<K, V>(
configuration);
Eh107Expiry<K, V>
expiryPolicy = null;
Jsr107CacheLoaderWriter<? super K, V>
loaderWriter = null;
try {
CacheConfigurationBuilder<K, V>
builder =
newCacheConfigurationBuilder(
configuration.
getKeyType(),
configuration.
getValueType(),
heap(
Long.
MAX_VALUE));
String templateName =
jsr107Service.
getTemplateNameForCache(
cacheName);
if (
xmlConfiguration != null &&
templateName != null) {
CacheConfigurationBuilder<K, V>
templateBuilder = null;
try {
templateBuilder =
xmlConfiguration.
newCacheConfigurationBuilderFromTemplate(
templateName,
jsr107Configuration.
getKeyType(),
jsr107Configuration.
getValueType());
} catch (
IllegalStateException e) {
templateBuilder =
xmlConfiguration.
newCacheConfigurationBuilderFromTemplate(
templateName,
jsr107Configuration.
getKeyType(),
jsr107Configuration.
getValueType(),
heap(
Long.
MAX_VALUE));
}
if (
templateBuilder != null) {
builder =
templateBuilder;
LOG.
info("Configuration of cache {} will be supplemented by template {}",
cacheName,
templateName);
}
}
builder =
handleStoreByValue(
jsr107Configuration,
builder,
cacheName);
final boolean
hasConfiguredExpiry =
builder.
hasConfiguredExpiry();
if (
hasConfiguredExpiry) {
LOG.
info("Cache {} will use expiry configuration from template {}",
cacheName,
templateName);
} else {
expiryPolicy =
initExpiryPolicy(
jsr107Configuration);
builder =
builder.
withExpiry(
expiryPolicy);
}
boolean
useEhcacheLoaderWriter;
DefaultCacheLoaderWriterConfiguration ehcacheLoaderWriterConfiguration =
builder.
getExistingServiceConfiguration(
DefaultCacheLoaderWriterConfiguration.class);
if (
ehcacheLoaderWriterConfiguration == null) {
useEhcacheLoaderWriter = false;
// No template loader/writer - let's activate the JSR-107 one if any
loaderWriter =
initCacheLoaderWriter(
jsr107Configuration, new
MultiCacheException());
if (
loaderWriter != null && (
jsr107Configuration.
isReadThrough() ||
jsr107Configuration.
isWriteThrough())) {
cacheLoaderWriterFactory.
registerJsr107Loader(
cacheName,
loaderWriter);
}
} else {
useEhcacheLoaderWriter = true;
if (!
jsr107Configuration.
isReadThrough() && !
jsr107Configuration.
isWriteThrough()) {
LOG.
warn("Activating Ehcache loader/writer for JSR-107 cache {} which was neither read-through nor write-through",
cacheName);
}
LOG.
info("Cache {} will use loader/writer configuration from template {}",
cacheName,
templateName);
}
CacheConfiguration<K, V>
cacheConfiguration =
builder.
build();
setupManagementAndStatsInternal(
jsr107Configuration,
findSingletonAmongst(
Jsr107CacheConfiguration.class,
cacheConfiguration.
getServiceConfigurations()));
if (
hasConfiguredExpiry) {
expiryPolicy = new
EhcacheExpiryWrapper<K, V>(
cacheConfiguration.
getExpiry());
}
return new
ConfigHolder<K, V>(
new
CacheResources<K, V>(
cacheName,
loaderWriter,
expiryPolicy,
initCacheEventListeners(
jsr107Configuration)),
new
Eh107CompleteConfiguration<K, V>(
jsr107Configuration,
cacheConfiguration,
hasConfiguredExpiry,
useEhcacheLoaderWriter),
cacheConfiguration,
useEhcacheLoaderWriter);
} catch (
Throwable throwable) {
MultiCacheException mce = new
MultiCacheException();
CacheResources.
close(
expiryPolicy,
mce);
CacheResources.
close(
loaderWriter,
mce);
if (
throwable instanceof
IllegalArgumentException) {
String message =
throwable.
getMessage();
if (
mce.
getMessage() != null) {
message =
message + "\nSuppressed " +
mce.
getMessage();
}
throw new
IllegalArgumentException(
message,
throwable);
}
mce.
addFirstThrowable(
throwable);
throw
mce;
}
}
private <K, V>
CacheConfigurationBuilder<K, V>
handleStoreByValue(
Eh107CompleteConfiguration<K, V>
jsr107Configuration,
CacheConfigurationBuilder<K, V>
builder,
String cacheName) {
DefaultCopierConfiguration copierConfig =
builder.
getExistingServiceConfiguration(
DefaultCopierConfiguration.class);
if(
copierConfig == null) {
if(
jsr107Configuration.
isStoreByValue()) {
if (
xmlConfiguration != null) {
DefaultCopyProviderConfiguration defaultCopyProviderConfiguration =
findSingletonAmongst(
DefaultCopyProviderConfiguration.class,
xmlConfiguration.
getServiceCreationConfigurations().
toArray());
if (
defaultCopyProviderConfiguration != null) {
Map<
Class<?>,
ClassInstanceConfiguration<
Copier<?>>>
defaults =
defaultCopyProviderConfiguration.
getDefaults();
handleCopierDefaultsforImmutableTypes(
defaults);
boolean
matchingDefault = false;
if (
defaults.
containsKey(
jsr107Configuration.
getKeyType())) {
matchingDefault = true;
} else {
builder =
builder.
add(new
DefaultCopierConfiguration<K>(
SerializingCopier.<K>
asCopierClass(),
DefaultCopierConfiguration.
Type.
KEY));
}
if (
defaults.
containsKey(
jsr107Configuration.
getValueType())) {
matchingDefault = true;
} else {
builder =
builder.
add(new
DefaultCopierConfiguration<K>(
SerializingCopier.<K>
asCopierClass(),
DefaultCopierConfiguration.
Type.
VALUE));
}
if (
matchingDefault) {
LOG.
info("CacheManager level copier configuration overwriting JSR-107 by-value semantics for cache {}",
cacheName);
}
return
builder;
}
}
builder =
addDefaultCopiers(
builder,
jsr107Configuration.
getKeyType(),
jsr107Configuration.
getValueType());
LOG.
debug("Using default Copier for JSR-107 store-by-value cache {}",
cacheName);
}
} else {
LOG.
info("Cache level copier configuration overwriting JSR-107 by-value semantics for cache {}",
cacheName);
}
return
builder;
}
@
SuppressWarnings("unchecked")
private static <K, V>
CacheConfigurationBuilder<K, V>
addDefaultCopiers(
CacheConfigurationBuilder<K, V>
builder,
Class keyType,
Class valueType ) {
Set<
Class>
immutableTypes = new
HashSet<
Class>();
immutableTypes.
add(
String.class);
immutableTypes.
add(
Long.class);
immutableTypes.
add(
Float.class);
immutableTypes.
add(
Double.class);
immutableTypes.
add(
Character.class);
immutableTypes.
add(
Integer.class);
if (
immutableTypes.
contains(
keyType)) {
builder =
builder.
add(new
DefaultCopierConfiguration<K>((
Class)
Eh107IdentityCopier.class,
DefaultCopierConfiguration.
Type.
KEY));
} else {
builder =
builder.
add(new
DefaultCopierConfiguration<K>(
SerializingCopier.<K>
asCopierClass(),
DefaultCopierConfiguration.
Type.
KEY));
}
if (
immutableTypes.
contains(
valueType)) {
builder =
builder.
add(new
DefaultCopierConfiguration<K>((
Class)
Eh107IdentityCopier.class,
DefaultCopierConfiguration.
Type.
VALUE));
} else {
builder =
builder.
add(new
DefaultCopierConfiguration<K>(
SerializingCopier.<K>
asCopierClass(),
DefaultCopierConfiguration.
Type.
VALUE));
}
return
builder;
}
private static void
handleCopierDefaultsforImmutableTypes(
Map<
Class<?>,
ClassInstanceConfiguration<
Copier<?>>>
defaults) {
addIdentityCopierIfNoneRegistered(
defaults,
Long.class);
addIdentityCopierIfNoneRegistered(
defaults,
Integer.class);
addIdentityCopierIfNoneRegistered(
defaults,
String.class);
addIdentityCopierIfNoneRegistered(
defaults,
Float.class);
addIdentityCopierIfNoneRegistered(
defaults,
Double.class);
addIdentityCopierIfNoneRegistered(
defaults,
Character.class);
}
@
SuppressWarnings("unchecked")
private static void
addIdentityCopierIfNoneRegistered(
Map<
Class<?>,
ClassInstanceConfiguration<
Copier<?>>>
defaults,
Class<?>
clazz) {
if (!
defaults.
containsKey(
clazz)) {
defaults.
put(
clazz, new
DefaultCopierConfiguration(
Eh107IdentityCopier.class,
DefaultCopierConfiguration.
Type.
VALUE));
}
}
private <K, V>
Map<
CacheEntryListenerConfiguration<K, V>,
ListenerResources<K, V>> initCacheEventListeners(
CompleteConfiguration<K, V>
config) {
Map<
CacheEntryListenerConfiguration<K, V>,
ListenerResources<K, V>>
listenerResources = new
ConcurrentHashMap<
CacheEntryListenerConfiguration<K, V>,
ListenerResources<K, V>>();
MultiCacheException mce = new
MultiCacheException();
for (
CacheEntryListenerConfiguration<K, V>
listenerConfig :
config.
getCacheEntryListenerConfigurations()) {
listenerResources.
put(
listenerConfig,
ListenerResources.
createListenerResources(
listenerConfig,
mce));
}
return
listenerResources;
}
private <K, V>
Eh107Expiry<K, V> initExpiryPolicy(
CompleteConfiguration<K, V>
config) {
return new
ExpiryPolicyToEhcacheExpiry<K, V>(
config.
getExpiryPolicyFactory().
create());
}
private <K, V>
Jsr107CacheLoaderWriter<K, V> initCacheLoaderWriter(
CompleteConfiguration<K, V>
config,
MultiCacheException mce) {
Factory<
CacheLoader<K, V>>
cacheLoaderFactory =
config.
getCacheLoaderFactory();
@
SuppressWarnings("unchecked")
Factory<
CacheWriter<K, V>>
cacheWriterFactory = (
Factory<
CacheWriter<K, V>>) (
Object)
config.
getCacheWriterFactory();
if (
config.
isReadThrough() &&
cacheLoaderFactory == null) {
throw new
IllegalArgumentException("read-through enabled without a CacheLoader factory provided");
}
if (
config.
isWriteThrough() &&
cacheWriterFactory == null) {
throw new
IllegalArgumentException("write-through enabled without a CacheWriter factory provided");
}
CacheLoader<K, V>
cacheLoader =
cacheLoaderFactory == null ? null :
cacheLoaderFactory.
create();
CacheWriter<K, V>
cacheWriter;
try {
cacheWriter =
cacheWriterFactory == null ? null :
cacheWriterFactory.
create();
} catch (
Throwable t) {
if (
t !=
mce) {
mce.
addThrowable(
t);
}
CacheResources.
close(
cacheLoader,
mce);
throw
mce;
}
if (
cacheLoader == null &&
cacheWriter == null) {
return null;
} else {
return new
Eh107CacheLoaderWriter<K, V>(
cacheLoader,
config.
isReadThrough(),
cacheWriter,
config.
isWriteThrough());
}
}
void
setUpManagementAndStats(
InternalCache<?, ?>
cache,
Eh107Configuration<?, ?>
configuration) {
Jsr107CacheConfiguration cacheConfiguration =
ServiceUtils.
findSingletonAmongst(
Jsr107CacheConfiguration.class,
cache
.
getRuntimeConfiguration().
getServiceConfigurations());
setupManagementAndStatsInternal(
configuration,
cacheConfiguration);
}
private void
setupManagementAndStatsInternal(
Eh107Configuration<?, ?>
configuration,
Jsr107CacheConfiguration cacheConfiguration) {
ConfigurationElementState enableManagement =
jsr107Service.
isManagementEnabledOnAllCaches();
ConfigurationElementState enableStatistics =
jsr107Service.
isStatisticsEnabledOnAllCaches();
if (
cacheConfiguration != null) {
ConfigurationElementState managementEnabled =
cacheConfiguration.
isManagementEnabled();
if (
managementEnabled != null &&
managementEnabled !=
ConfigurationElementState.
UNSPECIFIED) {
enableManagement =
managementEnabled;
}
ConfigurationElementState statisticsEnabled =
cacheConfiguration.
isStatisticsEnabled();
if (
statisticsEnabled != null &&
statisticsEnabled !=
ConfigurationElementState.
UNSPECIFIED) {
enableStatistics =
statisticsEnabled;
}
}
if (
enableManagement != null &&
enableManagement !=
ConfigurationElementState.
UNSPECIFIED) {
configuration.
setManagementEnabled(
enableManagement.
asBoolean());
}
if (
enableStatistics != null &&
enableStatistics !=
ConfigurationElementState.
UNSPECIFIED) {
configuration.
setStatisticsEnabled(
enableStatistics.
asBoolean());
}
}
static class
ConfigHolder<K, V> {
final
CacheResources<K, V>
cacheResources;
final
CacheConfiguration<K, V>
cacheConfiguration;
final
Eh107CompleteConfiguration<K, V>
jsr107Configuration;
final boolean
useEhcacheLoaderWriter;
public
ConfigHolder(
CacheResources<K, V>
cacheResources,
Eh107CompleteConfiguration<K, V>
jsr107Configuration,
CacheConfiguration<K, V>
cacheConfiguration, boolean
useEhcacheLoaderWriter) {
this.
cacheResources =
cacheResources;
this.
jsr107Configuration =
jsr107Configuration;
this.
cacheConfiguration =
cacheConfiguration;
this.
useEhcacheLoaderWriter =
useEhcacheLoaderWriter;
}
}
}