How to configure custom ehcaches in Play framework? - java

Setup:
play framework 2.4.0
built-in ehcache
java
I have followed the manual at https://www.playframework.com/documentation/2.4.0/JavaCache and to separate caches and use different configs (cache sizes, lifetimes etc) I configure in application.conf:
play.cache.bindCaches = ["mycache1-cache","mycache2-cache"]
Then, to configure them, I created the usual ehcache.xml file
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd" updateCheck="false">
<defaultCache
maxBytesLocalHeap="256000000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
maxElementsOnDisk="10000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<cache name="mycache1-cache"
maxBytesLocalHeap="256000000"
eternal="false"
timeToIdleSeconds="86400"
timeToLiveSeconds="86400"
overflowToDisk="true"
maxElementsOnDisk="1000000"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="1200"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>
It works when I only keep the defaultCache, but as soon as I add the custom cache, play throws with:
ProvisionException: Unable to provision, see the following errors: 1)
Error in custom provider, net.sf.ehcache.ObjectExistsException: Cache
mycache1-cache already exists
However, if I only define the cache in ehcache.xml but not in application.conf, play does not know about it and throws.

I have also faced the same issue a month ago and tried to search over internet and below solution that works for me. You can also try to do same.
/** Bind named caches using configuration in ehcache.xml. Define a named cache in ehcache.xml and Ehcache will automatically instantiate the cache at runtime. However,this cache is unknown to the Play cache plugin and is not accessible using {#code #NamedCache}. You must bind a reference to the named cache using {#link CacheModule#bindCustomCache(String)}. Do not add the named cache to {#code play.cache.bindCaches} in configuration. Caches bound in this manner are programmatically added by the Play cache plugin at runtime. The cache plugin will always copy settings from the default cache, limiting the usefulness of named caches. **/
public class CacheModule extends AbstractModule {
#Override
protected void configure() {
bindCustomCache("tenants");
bindCustomCache("user-agent");
bindCustomCache("geo-ip");
bindCustomCache("full-contact");
}
/**
* Bind a named cache that is defined in ehcache.xml.
*
* #param name The name of the cache.
*/
private void bindCustomCache(String name) {
bind(CacheApi.class)
.annotatedWith(new NamedCacheImpl(name))
.toProvider(new Provider<CacheApi>() {
#Inject
CacheManager cacheManager;
#Override
public CacheApi get() {
Cache cache = cacheManager.getCache(name);
if (cache == null) {
throw new RuntimeException("Cache named '" + name + "' must be defined in ehcache.xml");
}
return new DefaultCacheApi(new EhCacheApi(cache));
}
})
.asEagerSingleton();
}
}
I create my named caches in ehcache.xml and just have to add a one line bindCustomCache() to the configure method.

You will also need to set play.cache.createBoundCaches = false in application.conf.

Related

How to config a cache to point to local ehcache instead of inherited one?

I am trying to use a local ehcache created in my project declared through ehcache.xml file and configured through CacheManager, but am unable to do that and can only use the inherited cache.
Is there a way I can use the cache I declare in the local ehcache.xml file? Please help
I want to create a local Ehcache to store my method results.
My cache config file is below
#Configuration
#Component
#EnableAsync
#EnableCaching
#Primary
public class CacheConfig extends cacheProcessor {
#Bean
#Primary
public EhCacheManagerFactoryBean ehCacheCacheManager() {
EhCacheManagerFactoryBean cmfb = new EhCacheManagerFactoryBean();
cmfb.setConfigLocation(new ClassPathResource("ehcache.xml"));
cmfb.setShared(true);
return cmfb;
}
}
It is trying to override the cache config file defined in the project brought in through dependency because I want to point it to a local ehcache.xml with a new cache name.
My local ehcache.xml file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache.xsd"
name="timePass"
updateCheck="false"
monitoring="autodetect"
dynamicConfig="true">
<diskStore path="java.io.tmpdir" />
<cache name="newCache"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="1000"
eternal="false"
diskSpoolBufferSizeMB="20"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off">
<persistence strategy="localTempSwap" />
</cache>
The application class file has a component scan annotation to the inherited projects as well.
My caching service looks like this:
#Component
#Service
public class cacheableService {
#Cacheable(cacheNames = "newCache")
public Collection<ObjectDTO> getAllData(){
return a new list logic
}
}
I am receiving an error, that it cannot find the cache name I have mentioned in the Cacheable annotation, which is the one I declare in the local XML file. And when I use a cache name i.e. in declared in the inherited project ehcache.xml file, it finds the cache.
I have already set the following properties in the application.yml file
spring.cache.ehcache.config.classpath = ehcache.xml
spring.main.allow-bean-definition-overriding = true

EhCache in distributed mode

We are trying to use EhCache as a distributed cache in our application.
EhCache instance are embedded in our server and we used a terracota cluster.
All our server (and ehcache) instances successfully connect to this tc.
We successfully inserts, update and get into each of our caches.
But we cannot iterates over any cache.
Maybe we have configured our caches in the wrong way, but it seems that the iterator methods are not yet implemented (in org.ehcache.clustered.client.internal.store.ClusteredStore):
#Override
public Iterator<Cache.Entry<K, ValueHolder<V>>> iterator() {
// TODO: Make appropriate ServerStoreProxy call
throw new UnsupportedOperationException("Implement me");
}
Our cache configuration looks like the following:
<service>
<tc:cluster>
<tc:connection url="terracotta://10.23.69.20:9510/clustered"/>
<tc:server-side-config auto-create="true">
<tc:default-resource from="default-resource"/>
</tc:server-side-config>
</tc:cluster>
</service>
<cache-template name="CacheTemplate">
<resources>
<tc:clustered-dedicated unit="MB">16</tc:clustered-dedicated>
</resources>
<tc:clustered-store consistency="strong"/>
</cache-template>
<cache alias="CacheDaemon" uses-template="CacheTemplate">
<key-type>java.lang.String</key-type>
<value-type>com.server.cache.DaemonInstance</value-type>
</cache>
<cache alias="CacheProperty" uses-template="CacheTemplate">
<key-type>java.lang.String</key-type>
<value-type>java.lang.String</value-type>
</cache>
I don't found any other way, even getting the key list.
So did we made a mistake in the cache configuration ?
Or is it that the EhCache distributed mode is totally incompatible with this method (and so we won't use EhCache).

ehcache - restrict access

I use ehcache (not replicated or distributed) in my application and as I know it can be accessed only from the same JVM but all the applications in the same JVM (e.g. deployed in a app server) can get values from the cache. Am I right?
What I want is that only my application can get the cache and its values. Is it possible somehow? I checked the XML config file but I haven't found any configuration to control this. Or should I set something when I get the cache from CacheManager?
It is how I get the cache in code:
private static final String LOCAL_CACHE_NAME = "LOCAL_PROTNEUT_STORE";
private Cache getCache() {
// the name of the ehcache should be able to be configured in the general config XML
URL url = getClass().getResource("/protneut-local-ehcache.xml");
CacheManager manager = CacheManager.create(url);
Cache cache = manager.getCache(LOCAL_CACHE_NAME);
return cache;
}
The config file:
<ehcache>
<cache name="LOCAL_PROTNEUT_STORE" maxElementsInMemory="500" eternal="true" memoryStoreEvictionPolicy="LRU" />
</ehcache>
Is it possible to control the access at all?
Thanks for the help!
Regards,
V.
In general applications don't have access to each other as they are loaded with separe classpaths (you can read more about it here) so you shouldn't worry about it.
You would have to make extra effort to make simple cache manager avialable in all applications (like make it available via JNDI or put it in shared lib)

Spring Looking at Default Value Before Resolving Placeholder

I've been searching forever for an issue similar to mine, but wasn't able to find one. Thus, I hope it's not a duplicate post.
Well,
I'm using spring integration to search for documents in my mongodb. Once it finds one, it sends the payload to another method.
I have a property file that I want to be resolved on this find and send configuration, so I use placeholders instead of static values.
Here's my xml:
<int:inbound-channel-adapter id="sendClientMailInboundAdapter"
expression="#repository.findClient()"
channel="sendClientMailChannel"
auto-startup="${send.mail.active:false}">
<int:poller fixed-rate="${send.mail.poller.time:60}" time-unit="SECONDS" max-messages-per-poll="1" />
</int:inbound-channel-adapter>
<int:channel id="sendClientMailChannel" />
<int:service-activator input-channel="sendClientMailChannel" expression="#service.sendClientMail(payload)" />
Right.
Now.. I've got an AppConfig class which loads the property file.
#Configuration
#PropertySource("file://${appconfig.root}/appconfig.properties")
public class AppConfig {
public static Environment env;
...
#Bean
public static PropertySourcesPlaceholderConfigurer appConfigConfigurer(Environment env) {
AppConfig.env = env;
PropertySourcesPlaceholderConfigurer appConfigConfigurer = new PropertySourcesPlaceholderConfigurer();
appConfigConfigurer.setIgnoreUnresolvablePlaceholders(true);
appConfigConfigurer.setIgnoreResourceNotFound(true);
return appConfigConfigurer;
}
}
So.. My problem is the default value.
If I do NOT specify the default value, spring resolves the placeholder. If it's specified, though, it seems spring ignores the placeholder (maybe it doesn't wait for it to resolve) and set the default value instead!
I could use context:property-placeholder location. I've done that and it worked, but since I'm already loading the file on my configuration class, I'd rather have spring read properties from there so I would not have to remember to adjust two files in case property file changes its folder, for instance.
My question: Is there a way to tell spring to wait for the placeholder to resolve before looking at the default value?
edit: Just so people know.. I changed xml to java config, using placeholder / default value like before and it worked perfectly.
Here is the equivalent snippet:
#InboundChannelAdapter(value = "sendClientMailChannel", autoStartup="${send.mail.active:false}",
poller = #Poller(fixedRate="${send.mail.poller.time.in.millis:60000}", maxMessagesPerPoll="1"))
public Client findClient() {
return repository.findClient();
}
#ServiceActivator(inputChannel="sendClientMailChannel")
public void sendToClient(Client payload) {
service.sendClientMail(payload);
}
#Bean
public DirectChannel sendClientMailChannel() {
return new DirectChannel();
}
note: I didn't have to ref the property file.

Hibernate 2nd level cache without ehcache.xml

I am trying to configure Ehcache as a hibernate 2nd-level cache, and so far all the examples I've found instruct you to create an ehcache.xml file in the classpath like:
<ehcache updateCheck="false">
<diskStore path="java.io.tmpdir" />
<defaultCache maxElementsInMemory="10000" eternal="false"
statistics="true" timeToIdleSeconds="120" timeToLiveSeconds="120"
overflowToDisk="true" diskPersistent="false"
diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" />
<cache name="com.yourcompany.CachedEntity" eternal="true" maxElementsInMemory="1000" />
</ehcache>
And then configure Hibernate as follows:
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>
<property name="net.sf.ehcache.configurationResourceName" value="/ehcache.xml" />
<property name="hibernate.cache.use_query_cache" value="true" />
<property name="hibernate.cache.use_second_level_cache" value="true" />
In my workplace we are encouraged to use java config wherever possible and avoid XML config files. I'd appreciate any pointers on how this can be achieved.
The stackoverflow question using ehcache in spring 4 without xml mentioned by learningJava shows how to configure an ehcache CacheManager in java but you still need a way to tell hibernate that it should use your java configured CacheManager.
A possible way to this would be to configure a custom ehcache region factory via the hibernate.cache.region.factory_class property. If you look at the implementation of SingletonEhCacheRegionFactory you'll see that it will be quite easy to swap in your own CacheManager.
I suspect that the mapping between the java configuration and the xml configuration of a cache is quite straightforward, but if ehcache performs some 'magic stuff' behind the scenes when it parses the xml configuration, the job of getting your java configuration right might be a bit trickier.
I also could not find sample code for this problem so I thought that I would share my solution. #Pieter 's answer set me on the right track and you need to implement your own EhCacheRegionFactory class so that you can provide custom java Configuration objects. It appears that the stock library only supports xml. Here's what my solution looks like:
public class CustomEhCacheRegionFactory extends EhCacheRegionFactory{
private static final EhCacheMessageLogger LOG = Logger.getMessageLogger(
EhCacheMessageLogger.class,
CustomCacheRegionFactory.class.getName()
);
#Override
public void start(Settings settings, Properties properties) throws CacheException {
this.settings = settings;
if ( manager != null ) {
LOG.attemptToRestartAlreadyStartedEhCacheProvider();
return;
}
try {
final Configuration configuration = new Configuration();
configuration.addCache( getEntityCacheConfiguration() );
manager = new CacheManager( configuration );
mbeanRegistrationHelper.registerMBean( manager, properties );
}
catch (net.sf.ehcache.CacheException e) {
//handle
}
}
/**
* Create the basic second level cache configuration
* #return
*/
private CacheConfiguration getEntityCacheConfiguration()
{
CacheConfiguration config = new CacheConfiguration("entity", 50000);
config.setTimeToIdleSeconds(86400);
config.setTimeToLiveSeconds(86400);
return config;
}
}

Categories

Resources