Transform EhCache XML to Java class using annotations and register in JMX

506 views Asked by At

In our project we have two modules: api and api-component, each has a cache.xml file which references another ehcache.xml file, both like this:

cache.xml (in both modules)

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="ehcache" />
    </bean>

    <bean id="ehcache"
        class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
        p:config-location="classpath:ehcache.xml" />

    <bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
        <property name="locateExistingServerIfPossible" value="true" />
    </bean>

    <bean class="net.sf.ehcache.management.ManagementService"
        init-method="init">
        <constructor-arg ref="ehcache" />
        <constructor-arg ref="mbeanServer" />
        <constructor-arg value="true" />
        <constructor-arg value="true" />
        <constructor-arg value="true" />
        <constructor-arg value="true" />
    </bean>

</beans>

ehcache.xml (in both modules)

<ehcache name="ApiCacheManager">
    <diskStore path="java.io.tmpdir"/>

    <cache name="myCache" -- Different name in each file
           maxElementsInMemory="1000"
           eternal="false"
           timeToIdleSeconds="500"
           timeToLiveSeconds="500"
           overflowToDisk="false"
           statistics="true"
    />
​
    <defaultCache
            maxElementsInMemory="1000"
            eternal="true"
            overflowToDisk="false"
    />
</ehcache>

In an attempt to transform these XML files to Java classes using annotations I came up with these configurations, again in both modules: ApiCacheConfiguration.java and ApiComponentCacheConfiguration.java

import net.sf.ehcache.Cache;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.DiskStoreConfiguration;
import net.sf.ehcache.management.ManagementService;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.interceptor.*;
import org.springframework.cache.support.CompositeCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jmx.support.MBeanServerFactoryBean;

import java.util.Collections;

@Configuration
@EnableCaching
public class ApiCacheConfiguration extends CachingConfigurerSupport {

    @Primary
    @Bean
    public CacheManager cacheManager() {
        CompositeCacheManager compositeCacheManager = new CompositeCacheManager();
        compositeCacheManager.setCacheManagers(Collections.singleton(ehCacheManager()));
        return compositeCacheManager;
    }

    @Bean
    public EhCacheCacheManager ehCacheManager() {
        EhCacheCacheManager cacheManager = new EhCacheCacheManager(ehCacheCacheManager());

        ManagementService.registerMBeans(
                cacheManager.getCacheManager(),
                mBeanServer().getObject(),
                true,
                true,
                true,
                true);

        return cacheManager;
    }

    @Bean
    public MBeanServerFactoryBean mBeanServer() {
        MBeanServerFactoryBean mBeanServerFactoryBean = new MBeanServerFactoryBean();
        mBeanServerFactoryBean.setLocateExistingServerIfPossible(true);
        return mBeanServerFactoryBean;
    }

    @Bean
    @Override
    public CacheResolver cacheResolver() {
        return new SimpleCacheResolver(cacheManager());
    }

    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return new SimpleKeyGenerator();
    }

    @Bean
    @Override
    public CacheErrorHandler errorHandler() {
        return new SimpleCacheErrorHandler();
    }

    private net.sf.ehcache.CacheManager ehCacheCacheManager() {
        net.sf.ehcache.CacheManager cacheManager = new net.sf.ehcache.CacheManager(ehCacheConfiguration());
        cacheManager.setName("ApiCacheManager"); // If name is not set I'm not getting 'InstanceAlreadyExistsException'
        cacheManager.addCache(myCache());
        return cacheManager;
    }

    private Cache myCache() {
        CacheConfiguration configuration = new CacheConfiguration("myCache", 100) // Different name in each file
                .eternal(false)
                .timeToIdleSeconds(600)
                .timeToLiveSeconds(3600)
                .overflowToDisk(false)
                .statistics(true);
        return new Cache(configuration);
    }

    private net.sf.ehcache.config.Configuration ehCacheConfiguration() {
        DiskStoreConfiguration diskStoreConfiguration = new DiskStoreConfiguration();
        diskStoreConfiguration.setPath("java.io.tmpdir");

        CacheConfiguration defaultCacheConfiguration = new CacheConfiguration();
        defaultCacheConfiguration.maxElementsInMemory(1000);
        defaultCacheConfiguration.eternal(true);
        defaultCacheConfiguration.overflowToDisk(false);

        net.sf.ehcache.config.Configuration configuration = new net.sf.ehcache.config.Configuration();
        configuration.setDefaultCacheConfiguration(defaultCacheConfiguration);
        configuration.addDiskStore(diskStoreConfiguration);
        return configuration;
    }

}

Everything is almost as it's written in the previous ehCache.xml but, I don't know how, multiple beans of ApiCacheManager and ApiComponentCacheManager are being created, throwing these errors:

Caused by: javax.management.InstanceAlreadyExistsException: net.sf.ehcache:type=CacheManager,name=ApiCacheManager

Caused by: javax.management.InstanceAlreadyExistsException: net.sf.ehcache:type=CacheManager,name=ApiComponentCacheManager

This error doesn't appear if the CacheManager names are not set, but multiple beans are registered into JMX:

enter image description here

How do I get only my two CacheManagers into JMX with the names I set? Is there something wrong with the creation of the CacheManager? Maybe is not creating a Singleton?

0

There are 0 answers