• Bug
  • Status: New
  • 2 Major
  • Resolution:
  • ehcache,ehcache-core
  • drb
  • Reporter: wlfshmn
  • April 15, 2015
  • 0
  • Watchers: 1
  • April 15, 2015

Description

Removing a RefreshAheadCache from the cache manager fails to remove the supprot cache. The localDispose method appears to not use the support caches name when attempting to remove the support cache.

The following code demonstrates the issue:

import net.sf.ehcache.*;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
import net.sf.ehcache.constructs.blocking.SelfPopulatingCache;
import net.sf.ehcache.constructs.refreshahead.RefreshAheadCache;
import net.sf.ehcache.constructs.refreshahead.RefreshAheadCacheConfiguration;
import net.sf.ehcache.loader.CacheLoader;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class RefreshAheadCacheRemovalMinimalTestCase_2_10_0 {

    private Ehcache cache;

    public static void main(String[] args) {

        RefreshAheadCacheRemovalMinimalTestCase_2_10_0 main = new RefreshAheadCacheRemovalMinimalTestCase_2_10_0();

        main.init();
        main.init();
        main.init();
    }

    private synchronized void init() {

        final CacheManager cacheManager = CacheManager.getInstance();

        final CacheConfiguration cacheConfiguration = new CacheConfiguration()
                .name(String.format("%s", this.getClass().getName() + UUID.randomUUID()))
                .maxEntriesLocalHeap(1);

        final Cache cache = new Cache(cacheConfiguration);
        cacheManager.addCache(cache);

        final CacheEntryFactory cacheEntryFactory = new CacheEntryFactory() {

            @Override
            public Object createEntry(final Object key) throws Exception {
                return System.currentTimeMillis();
            }
        };

        final SelfPopulatingCache selfPopulatingCache = new SelfPopulatingCache(cache, cacheEntryFactory);

        final RefreshAheadCacheConfiguration refreshConfig = new RefreshAheadCacheConfiguration()
                .maximumRefreshBacklogItems(10)
                .build();

        final RefreshAheadCache refreshAheadCache = new RefreshAheadCache(selfPopulatingCache, refreshConfig);

        cache.registerCacheLoader(new CacheLoaderAdapter(cacheEntryFactory));

        cacheManager.replaceCacheWithDecoratedCache(cache, refreshAheadCache);

        // save old cache in order for us to know what to dispose of
        final Ehcache oldCache = this.cache;

        // replace user visible cache
        this.cache = refreshAheadCache;

        // remove old caches
        if (oldCache != null) {
            cacheManager.removeCache(oldCache.getName());

/*
            // improper hack to compensate for RefreshAheadCache failing to dispose of it's support cache.
            cacheManager.removeCache(String.format("%s_net.sf.ehcache.constructs.refreshahead.RefreshAheadCache_refreshAheadSupport", oldCache.getName()));
*/
        }

        System.out.printf("after reinitializing cachemanager contains %d caches.%n", cacheManager.getCacheNames().length);

        for (String cacheName : cacheManager.getCacheNames()) {
            System.out.println("\t" + cacheName);
        }
    }

    private static class CacheLoaderAdapter implements CacheLoader {

        private final CacheEntryFactory factory;

        public CacheLoaderAdapter(final CacheEntryFactory factory) {
            this.factory = factory;
        }

        @Override
        public Object load(final Object key) throws CacheException {
            return loadInternal(key);
        }

        @Override
        public Map loadAll(final Collection keys) {
            return loadAllInternal(keys);
        }

        @Override
        public Object load(final Object key, final Object argument) {
            return loadInternal(key);
        }

        @Override
        public Map loadAll(final Collection keys, final Object argument) {
            return loadAllInternal(keys);
        }

        @Override
        public String getName() {
            return this.getClass().getName();
        }

        @Override
        public CacheLoader clone(final Ehcache cache) throws CloneNotSupportedException {
            throw new CloneNotSupportedException();
        }

        @Override
        public void init() {
        }

        @Override
        public void dispose() throws CacheException {
        }

        @Override
        public Status getStatus() {
            return Status.STATUS_ALIVE;
        }

        private Object loadInternal(final Object key) throws CacheException {
            try {
                return factory.createEntry(key);
            } catch (final Exception e) {
                throw new CacheException(e);
            }
        }

        private Map loadAllInternal(final Collection keys) {

            final Map<Object, Object> results = new HashMap<Object, Object>(keys.size());
            for (final Object key : keys) {
                try {
                    final Object object = loadInternal(key);
                    results.put(key, object);
                } catch (final CacheException e) {
                    e.printStackTrace();
                }
            }
            return results;
        }
    }
}

Comments