/* * This file is part of l2jserver . * * l2jserver is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * l2jserver is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with l2jserver. If not, see . */ package com.l2jserver.service.cache; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Preconditions; import com.l2jserver.service.AbstractService; import com.l2jserver.service.ServiceStartException; import com.l2jserver.service.ServiceStopException; /** * This {@link Cache} service implementation uses a {@link WeakReference} to * store values. * * @author Rogiel */ public class WeakCacheService extends AbstractService implements CacheService { /** * The logger */ private final Logger log = LoggerFactory.getLogger(this.getClass()); /** * The interface cache */ private Cache interfaceCache; @Override protected void doStart() throws ServiceStartException { interfaceCache = createCache("interface-cache"); } @Override public T decorate(final Class interfaceType, final T instance) { Preconditions.checkNotNull(interfaceType, "interfaceType"); Preconditions.checkNotNull(instance, "instance"); Preconditions.checkArgument(interfaceType.isInterface(), "interfaceType is not an interface"); log.debug("Decorating {} with cache", interfaceType); @SuppressWarnings("unchecked") final T proxy = (T) Proxy.newProxyInstance(this.getClass() .getClassLoader(), new Class[] { interfaceType }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.isAnnotationPresent(IgnoreCaching.class)) return method.invoke(instance, args); final MethodInvocation invocation = new MethodInvocation( method, args); Object result = interfaceCache.get(invocation); if (result == null) return doInvoke(invocation, proxy, method, args); return result; } private Object doInvoke(MethodInvocation invocation, Object proxy, Method method, Object[] args) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { Object result = method.invoke(instance, args); interfaceCache.put(invocation, result); return result; } }); return proxy; } @Override public Cache createCache(String name, int size) { log.debug("Creating cache {} with minimum size of {}", name, size); return new WeakCache(name); } @Override public Cache createEternalCache(String name, int size) { log.debug("Creating eternal cache {} with minimum size of {}", name, size); return new EternalCache(name); } @Override public Cache createCache(String name) { return createCache(name, 200); } @Override public void dispose(Cache cache) { log.debug("Disposing {}", cache); cache.clear(); } @Override protected void doStop() throws ServiceStopException { dispose(interfaceCache); interfaceCache = null; } /** * This class is a simple map implementation for cache usage.
*
* Values from the map will be removed after the first garbage collector run * if there isn't any strong reference to the value object. * * @author Rogiel * @param * the key type * @param * the value type */ private class WeakCache extends AbstractReferenceCache implements Cache { /** * This class is a {@link WeakReference} with additional responsibility * of holding key object * * @author Rogiel */ private class Entry extends WeakReference { /** * The key */ private K key; /** * @param key * the key * @param referent * the value * @param q * the queue */ Entry(K key, V referent, ReferenceQueue q) { super(referent, q); this.key = key; } /** * @return the key */ K getKey() { return key; } } /** * @param cacheName * the cache name */ WeakCache(String cacheName) { super(cacheName); } @Override @SuppressWarnings("unchecked") protected synchronized void cleanQueue() { Entry en = null; while ((en = (Entry) refQueue.poll()) != null) { K key = en.getKey(); if (log.isDebugEnabled()) log.debug("{}: cleaned up for key: {}", cacheName, key); cacheMap.remove(key); } } @Override protected Reference newReference(K key, V value, ReferenceQueue vReferenceQueue) { return new Entry(key, value, vReferenceQueue); } } }