Skip to content

Using Interceptors

Alexander Kochurov edited this page Jun 1, 2015 · 6 revisions

What's that?

'Cach provider interceptors' allow you to override MxCache default behavior during cache instantiation in a more flexible way than with caching strategy (see Caching Strategy article).

Interceptors give you to additional extension points:

  1. Overriding 'registerCache' allows you to replace CacheDescriptor. With that you can, for example... -- ...replace or wrap calculation of cache values (e.g. add logging on each calculation of cache value); -- ...add or remove new tags or groups to cache; -- ...alter cache strategy, and many more.

  2. Overriding 'createCache' allows you to wrap or replace the cache instance created by MxCache. For example, you can... -- ...wrap all caches in a soft reference; -- ...create your own cache implementation; -- ...wrap all or some caches and dynamically disable caching without writing your own strategy.

Example

Wrapping all caches in a soft reference (Enhancer from CGLIB is used):

    class SoftDispatcher<T> implements Dispatcher {
        RegistryEntry<T> descriptor;
        CacheContext cacheContext;
        T instance;
        volatile SoftReference<Cache> reference;

        public SoftDispatcher(Cache cache, RegistryEntry<T> descriptor, CacheContext cacheContext, T instance) {
            this.descriptor = descriptor;
            this.cacheContext = cacheContext;
            this.instance = instance;
            reference = new SoftReference<Cache>(cache);
        }

        @Override
        public Cache loadObject() throws Exception {
            Cache res = reference.get();
            if (res == null) {
                // if the reference was cleared, then recreate the cache.
                res = descriptor.createCache(cacheContext, instance);
                reference = new SoftReference<Cache>(res);
            }
            return res;
        }
    }

    class SoftCacheProviderInterceptor implements CacheProviderInterceptor {
        @Nullable
        @Override
        public <T> CacheDescriptor<T> registerCache(CacheDescriptor<T> descriptor) {
            // null means 'no need to alter descriptor'
            return null;
        }

        @Nullable
        @Override
        public <T> Cache createCache(RegistryEntry<T> entry, @Nullable T instance, CacheContext cacheContext, Cache cache) {
            Dispatcher dispatcher = new SoftDispatcher<T>(cache, entry, cacheContext, instance);
            return (Cache) Enhancer.create(entry.getDescriptor().getSignature().getCacheInterface(), dispatcher);
        }
    } 

To install your interceptor use

MxCache.intercept(new SoftCacheProviderInterceptor());

Congratulations, now all your caches will be cleared before OOM.

FAQ

Q. What is performance overhead of interceptors?

A. An empty interceptor has almost zero overhead.


Q. In which thread will the interceptor be invoked?

A. 'registerCache' is invoken in a thread in which the class that holds the cache is initialized for the first time.


Q. Why throwing an exception has no effect?

A. It's the policy MxCache uses. As interceptors are called only when corresponding cache is initialized, throwing an exception may have an unpredicted effect. For example, 'registerCache' may be called from static initialization section of your class. If an exception from interceptor will be thrown, there's no way for your application to continue work because the class will never be loaded by JVM. That's why all exceptions are ignored. (Yes, it's a bad thing, but it's the best we can do to keep you application running).

Clone this wiki locally