Search code examples
javatomcatstruts2ehcache-3

EHCache - org.ehcache.StateTransitionException: Persistence directory already locked by another process


I am running a struts2 application am I have to implement a cache to cache certain responses. I have used EHCache and created a helper class to initialize and configure the cache as follows:

import java.io.File;

import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.PersistentCacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.MemoryUnit;

import gate.util.Pair;
import net.ha.apserver.util.LoggerManager;

public class HACacheManager {
    private PersistentCacheManager pCacheManager;
    private Cache<String, Object> patientCache;

    public HACacheManager() {
        pCacheManager = CacheManagerBuilder
                            .newCacheManagerBuilder()
                            .with(CacheManagerBuilder
                                    .persistence(System.getProperty("java.io.tempdir")+File.separator+"haCache")).build();
        pCacheManager.init();
        pCacheManager.createCache("patientList-cache", CacheConfigurationBuilder
                                    .newCacheConfigurationBuilder(
                                            String.class, 
                                            Pair.class, 
                                            ResourcePoolsBuilder.heap(10).disk(100, MemoryUnit.MB, true)));
    }

    public CacheManager getHACacheManager() {
        return pCacheManager;
    }

    public Cache<String,Pair> getPatientCache() {
        return pCacheManager.getCache("patientList-cache", String.class, Pair.class);
    }
}

Then, I have used the cache as follows, elsewhere in my codebase:

// Check cache for existing data first
        HACacheManager cacheManager = new HACacheManager();
        Cache<String, Pair> patientCache = cacheManager.getPatientCache();
        String key = organizationId+","+startCount+","+recordCount; // Key based on method arguments
        if(patientCache.containsKey(key)) {
            return (Pair) patientCache.get(key);
        }

and then if the key is not found in the cache, then we create a new element in the cache:

    Pair returnPair = new Pair(patientCount, patientList); 
    patientCache.put(key, returnPair);

Now, the first time I call the action in struts2 in the browser, it displays the JSP correctly and the values that I have returned are displayed correctly, implying that they have also been set in the cache.

However, if I call the same action a second time, I get the following exception:

org.ehcache.StateTransitionException: Persistence directory already locked by another process: D:\tools\eclipse-oxygen\eclipse\null\haCache
org.ehcache.core.StatusTransitioner$Transition.failed(StatusTransitioner.java:235)
org.ehcache.core.EhcacheManager.init(EhcacheManager.java:603)
net.ha.apserver.controller.HACacheManager.(HACacheManager.java:26)
net.ha.apserver.controller.UserManager.getUserPatients(UserManager.java:472)
net.ha.apserver.view.CohortSearchAction.getPatient(CohortSearchAction.java:853)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:441)
com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:280)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:243)
com.opensymphony.xwork2.interceptor.DefaultWorkflowInterceptor.doIntercept(DefaultWorkflowInterceptor.java:165)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:87)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
com.opensymphony.xwork2.validator.ValidationInterceptor.doIntercept(ValidationInterceptor.java:252)
org.apache.struts2.interceptor.validation.AnnotationValidationInterceptor.doIntercept(AnnotationValidationInterceptor.java:68)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:87)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
com.opensymphony.xwork2.interceptor.ConversionErrorInterceptor.intercept(ConversionErrorInterceptor.java:122)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:195)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:87)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
com.opensymphony.xwork2.interceptor.ParametersInterceptor.doIntercept(ParametersInterceptor.java:195)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:87)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
com.opensymphony.xwork2.interceptor.StaticParametersInterceptor.intercept(StaticParametersInterceptor.java:179)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
org.apache.struts2.interceptor.MultiselectInterceptor.intercept(MultiselectInterceptor.java:75)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
org.apache.struts2.interceptor.CheckboxInterceptor.intercept(CheckboxInterceptor.java:94)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
org.apache.struts2.interceptor.FileUploadInterceptor.intercept(FileUploadInterceptor.java:235)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor.intercept(ModelDrivenInterceptor.java:89)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
com.opensymphony.xwork2.interceptor.ScopedModelDrivenInterceptor.intercept(ScopedModelDrivenInterceptor.java:130)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:267)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
com.opensymphony.xwork2.interceptor.ChainingInterceptor.intercept(ChainingInterceptor.java:126)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
com.opensymphony.xwork2.interceptor.PrepareInterceptor.doIntercept(PrepareInterceptor.java:138)
com.opensymphony.xwork2.interceptor.MethodFilterInterceptor.intercept(MethodFilterInterceptor.java:87)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
com.opensymphony.xwork2.interceptor.I18nInterceptor.intercept(I18nInterceptor.java:165)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
org.apache.struts2.interceptor.ServletConfigInterceptor.intercept(ServletConfigInterceptor.java:164)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
com.opensymphony.xwork2.interceptor.AliasInterceptor.intercept(AliasInterceptor.java:179)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
com.opensymphony.xwork2.interceptor.ExceptionMappingInterceptor.intercept(ExceptionMappingInterceptor.java:176)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:237)
org.apache.struts2.impl.StrutsActionProxy.execute(StrutsActionProxy.java:52)
org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:488)
org.apache.struts2.dispatcher.FilterDispatcher.doFilter(FilterDispatcher.java:395)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.apache.catalina.filters.CorsFilter.handleNonCORS(CorsFilter.java:352)
org.apache.catalina.filters.CorsFilter.doFilter(CorsFilter.java:171)
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:668)
org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:791)
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417)
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
java.lang.Thread.run(Unknown Source)

At this point I have no choice but to restart the server since subsequent calls give the same exception. Is there some configuration that I have missed?


Solution

  • You have initialised HACacheManager in your method i.e. called upon by the action. Every time you hit action, cacheManager is initialised. Ehcache won't allow creating the cache with same path twice. Initialize once, by creating a static reference & use that reference in your action/method.

    public static HACacheManager cacheManager = new HACacheManager();
    

    Or better create a singleton instance. Put following lines in your HACacheManager class:

    private static HACacheManager cacheManager = null;
    public static HACacheManager getInstance() { 
        if (cacheManager == null) 
            cacheManager = new HACacheManager();
        return cacheManager; 
    }
    

    and call following method from your action/method(s):

    HACacheManager cacheManager = HACacheManager.getInstance();