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;
}
}
Related
I am making caching solution for a method located in a servlet.
Servlet looks like following:
public class DataOptimizedServlet extends DataServlet {
...
#Override
#Cacheable(value = "dataOptimized", key = "#req.getRequestURI()")
public byte[] getData(HttpServletRequest req) {
// data retrieval logic
}
...
}
in ehcache.xml I have following configuration:
<cache alias="dataOptimized">
<expiry>
<ttl unit="hours">30</ttl>
</expiry>
<resources>
<heap unit="entries">20000</heap>
<offheap unit="MB">200</offheap>
</resources>
</cache>
Also in ctx-cache.xml file is following configuration (and this file is imported in web.xml):
<cache:annotation-driven cache-manager="ehCacheManager" />
<bean id="ehCacheManager" class="org.springframework.cache.jcache.JCacheCacheManager">
<property name="cacheManager">
<bean class="org.springframework.cache.jcache.JCacheManagerFactoryBean" p:cacheManagerUri="classpath:ehcache.xml" />
</property>
</bean>
But it does not work, method getData still gets hit for the same URLs. I have many #Cacheables working in a project and configured in the same manner, but none is in servlet directly (they're in services).
What am I missing?
are you using Spring?
IF yes, consider that spring documentation spring doc says:
"When using proxies, you should apply the #Cache* annotations only to methods with public visibility. If you do annotate protected, private or package-visible methods with these annotations, no error is raised, but the annotated method does not exhibit the configured caching settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods as it changes the bytecode itself."
So you should change your method visibility to protected to public
According to this answer, you can use the Spring Batch class org.springframework.batch.support.SystemPropertyInitializer to set a System Property during startup of a Spring Context.
In particular, I was hoping to be able to use it to set ENVIRONMENT because part of Spring Batch config reads:
<bean id="placeholderProperties" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/org/springframework/batch/admin/bootstrap/batch.properties</value>
<value>classpath:batch-default.properties</value>
<value>classpath:batch-${ENVIRONMENT:hsql}.properties</value>
</list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="ignoreResourceNotFound" value="true" />
<property name="ignoreUnresolvablePlaceholders" value="false" />
<property name="order" value="1" />
</bean>
But SystemPropertyInitializer uses afterPropertiesSet() to set the System Property, and apparently this happens after the configuration of PropertyPlaceholderConfigurer.
Is it possible to achieve this?
The easiest solution would be to pass in the environment property as a command-line argument, so it can be resolved as a system property.
If that's not an option you can implement a ApplicationContextInitializer that promotes environment properties to system properties.
public class EnvironmentPropertyInitializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
boolean override = false; //change if you prefer envionment over command line args
#Override
public void initialize(final ConfigurableApplicationContext applicationContext) {
for (Entry<String, String> environmentProp : System.getenv().entrySet()) {
String key = environmentProp.getKey();
if (override || System.getProperty(key) == null) {
System.setProperty(key, environmentProp.getValue());
}
}
}
}
Here it looks like you're using Spring Batch Admin, so you can register your initializer with a slight addition to the web.xml file:
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>org.your.package.EnvironmentPropertyInitializer</param-value>
</context-param>
Adding Background Since a Comment Didn't Seem Sufficient: Here's the relevant classes and the order in which they are called/evaluated.
The ApplicationContextInitializer tells the Spring Application how to load an application context and can be used to set bean profiles, and change other aspects of the context. This gets executed before the context gets completely created.
The PropertyPlaceholderConfigurer is a BeanFactoryPostProcessor and calls postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory). This modifies the BeanFactory to allow for resolution of properties like ${my.property:some.default} when setting the properties of a bean as it is created by the BeanFactory.
The SystemPropertyInitializer implements InitializingBean and calls afterPropertiesSet(). This method runs after a bean is instantiated and the properties have been set.
So you're right that the SystemPropertyInitializer will not help here since it evaluates after the properties are set on the PropertyPlaceholderConfigurer. The ApplicationContextInitializer, however, will be able to promote those environment properties to system properties so they can be interpreted by the XML.
And one more note that I forgot to mention, one of the first declared beans will need to be:
<context:property-placeholder/>
Though it seems redundant, it will allow your PropertyPlaceholderConfigurer bean to evaluate ${ENVIRONMENT:hsql} correctly by using the environment properties you just promoted.
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.
I am using the HDIV Web Application Security Framework for a java web application. Every new web-page-request generates hdiv-internal security information that is cached and used for security checks.
I have the following szenario:
I have one order page that pops up a confirmation-page for 2 seconds when something was added to or removed from the cart.
after 50 popups the the underlaying order page is removed from the cache and therefor an error occurs in the app.
does anybody know how to influence the hdiv cache-removal strategy to keep the basepage alive?
One way around is to increase org.hdiv.session.StateCache.maxSize from 50 to 500.
but this would only cure the symptoms not the underlying cause.
Update:
using #rbelasko solution
I succeded to use the original org.hdiv.session.StateCache to change the maxSize to 20 and verified in the debug-log that the cachentries are dismissed after 20 entries.
When I changed it to use my own implementation it didn-t work
Bean definition
<bean id="cache" class="com.mycompany.session.StateCacheTest" singleton="false"
init-method="init">
<property name="maxSize">
<value>20</value>
</property>
</bean>
My own class
public class StateCacheTest extends StateCache
{
private static final Log log = LogFactory.getLog(StateCacheTest.class);
public StateCacheTest()
{
log.debug("StateCache()");
}
#Override
public void setMaxSize(final int maxSize)
{
super.setMaxSize(maxSize);
if (log.isDebugEnabled())
{
log.debug("setMaxSize to " + maxSize);
}
}
}
In the debug-log were no entries from StateCacheTest
Any ideas?
Update 2:
While i was not able to load a different IStateCache implementation via spring i was able to make this error less likely using
<hdiv:config ... maxPagesPerSession="200" ... />
the bean-settings definition
<property name="maxSize">
<value>20</value>
</property>
had no effect on the cachesize in my system.
You could create a custom IStateCache interface implementation.
Using the HDIV explicit configuration (not using HDIV's new custom schema) this is the default configuration for "cache" bean:
<bean id="cache" class="org.hdiv.session.StateCache" singleton="false"
init-method="init">
<property name="maxSize">
<value>200</value>
</property>
</bean>
You could create your own implementation and implement the strategy that fits your requirements.
Regards,
Roberto
I need a quick help from you to fix a small problem.
In one of my project (using spring as core container), i am using ehcache to cache data. I am using spring ehcache annotations project (http://code.google.com/p/ehcache-spring-annotations/) for the same.
I want to have flexibility to enable and disable ehcache based on a external property. I read ehcache documentation and found that it reads system property net.sf.ehcache.disabled internally and if it set to true cache will be disabled and they recommend to pass this as -Dnet.sf.ehcache.disabled=true in the command line
I wanted to control it through externalized spring property file.
Then i thought of setting this system property in my spring application context file using MethodInvokingFactoryBean based on a externalized property.
Here is the code
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="java.lang.System"/>
<property name="targetMethod" value="getProperties"/>
</bean>
</property>
<property name="targetMethod" value="putAll"/>
<property name="arguments">
<util:properties>
<prop key="net.sf.ehcache.disabled">"${ehcache.disabled}"</prop>
</util:properties>
</property>
</bean>
Obviously ehcache.disabled is controlled via my externalized property file.
Plumbing works great but i guess order is not coming into place. By the time Application context is setting system property, cache is initialized and when cache was being initialized there was no property net.sf.ehcache.disabled, hence cache is not getting disabled. When i run application in debug mode and try to get System.getProperty("net.sf.ehcache.disabled") after application context is initialized, it is giving me the right value. (I tried both true and false).
One more thing i want to mentioned i am using spring wrapper to initialize my cache.
<ehcache:annotation-driven self-populating-cache-scope="method"/>
<ehcache:config cache-manager="cacheManager">
<ehcache:evict-expired-elements interval="20"/>
</ehcache:config>
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
p:configLocation="classpath:ehcache.xml"/>
I believe that disabling cache can not be that hard.
What is it that i am missing? Is there any easy way to do it in spring except command line?
As of Spring 3.1 it is possible to use bean profiles which can read a property from a external property. You could wrap the declared bean inside a beans element:
<beans profile="cache-enabled">
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configLocation="classpath:ehcache.xml"/>
</beans>
And then activate that using an external property as mentioned in this blog post.
The easiest way (pre-Spring 3.1) would be to add a bean id to your MethodInvokingFactoryBean declaration, then add a depends-on relationship.
You can return NoOpCacheManager object if your external property is not true
#Value("${cache.enabled}")
private Boolean cacheEnabled;
#Bean(destroyMethod="shutdown")
public net.sf.ehcache.CacheManager ehCacheManager() {
net.sf.ehcache.config.Configuration config = new net.sf.ehcache.config.Configuration();
CacheConfiguration cacheConfiguration = new CacheConfiguration();
cacheConfiguration.setName("mycache");
cacheConfiguration.setMemoryStoreEvictionPolicy("LRU");
cacheConfiguration.setMaxEntriesLocalHeap(1000);
cacheConfiguration.setTimeToIdleSeconds(0);
cacheConfiguration.setTimeToLiveSeconds(3600);
cacheConfiguration.persistence(new PersistenceConfiguration().strategy(PersistenceConfiguration.Strategy.NONE));
config.addCache(cacheConfiguration);
cacheMaxEntriesLocalHeap, cacheEvictionPolicy);
return net.sf.ehcache.CacheManager.newInstance(config);
}
#Bean
#Override
public CacheManager cacheManager() {
if(cacheEnabled) {
log.info("Cache is enabled");
return new EhCacheCacheManager(ehCacheManager());
}else{
log.info("Cache is disabled");
return new NoOpCacheManager();
}
}