Java tutorial
/** * Copyright 2003-2007 Luck Consulting Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.sf.ehcache.exceptionhandler; import net.sf.ehcache.Ehcache; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * A dynamic proxy which provides CacheException handling. * <p/> * The ehcache configuration will create and register in the <code>CacheManager</code> {@link Ehcache}s decorated * with this dynamic proxy. See following for programmatic use. * <p/> * The createProxy factory method may be used to simply create a proxy. Otherwise the calling client * will need code similar to: * <pre> * (Ehcache) Proxy.newProxyInstance(ehcache.getClass().getClassLoader(), new Class[]{ Ehcache.class }, * new ExceptionHandlingDynamicCacheProxy(ehcache));</pre> * <p/> * A common usage is to create a proxy and then register the proxy in <code>CacheManager</code> in place of the * underlying cache. To do that create a proxy and then call * <pre> * cacheManager.replaceCacheWithDecoratedCache(Ehcache cache, Ehcache decoratedCache); * </pre> * All clients accessing the cache through<code>cacheManager.getEhcache()</code> will then receive proxy references. * <p/> * See CacheTest for a perf test. * * @author <a href="mailto:gluck@gregluck.com">Greg Luck</a> * @version $Id: ExceptionHandlingDynamicCacheProxy.java 604 2008-04-25 02:20:57Z gregluck $ */ public final class ExceptionHandlingDynamicCacheProxy implements InvocationHandler { private static final Log LOG = LogFactory.getLog(ExceptionHandlingDynamicCacheProxy.class.getName()); private Ehcache ehcache; /** * Constructor: Use with something like: * <pre> * (Ehcache) Proxy.newProxyInstance(ehcache.getClass().getClassLoader(), new Class[]{ Ehcache.class }, * new ExceptionHandlingDynamicCacheProxy(ehcache));</pre> * @param ehcache the backing ehcache */ public ExceptionHandlingDynamicCacheProxy(Ehcache ehcache) { this.ehcache = ehcache; } /** * A simple factory method to hide the messiness of creating the proxy from clients. * * @param ehcache the target cache * @return a proxied Ehcache */ public static Ehcache createProxy(Ehcache ehcache) { return (Ehcache) Proxy.newProxyInstance(ehcache.getClass().getClassLoader(), new Class[] { Ehcache.class }, new ExceptionHandlingDynamicCacheProxy(ehcache)); } /** * Processes a method invocation on a proxy instance and returns * the result. This method will be invoked on an invocation handler * when a method is invoked on a proxy instance that it is * associated with. * * @param proxy the proxy instance that the method was invoked on * @param method the <code>Method</code> instance corresponding to * the interface method invoked on the proxy instance. The declaring * class of the <code>Method</code> object will be the interface that * the method was declared in, which may be a superinterface of the * proxy interface that the proxy class inherits the method through. * @param args an array of objects containing the values of the * arguments passed in the method invocation on the proxy instance, * or <code>null</code> if interface method takes no arguments. * Arguments of primitive types are wrapped in instances of the * appropriate primitive wrapper class, such as * <code>java.lang.Integer</code> or <code>java.lang.Boolean</code>. * @return the value to return from the method invocation on the * proxy instance. If the declared return type of the interface * method is a primitive type, then the value returned by * this method must be an instance of the corresponding primitive * wrapper class; otherwise, it must be a type assignable to the * declared return type. If the value returned by this method is * <code>null</code> and the interface method's return type is * primitive, then a <code>NullPointerException</code> will be * thrown by the method invocation on the proxy instance. If the * value returned by this method is otherwise not compatible with * the interface method's declared return type as described above, * a <code>ClassCastException</code> will be thrown by the method * invocation on the proxy instance. * @throws Throwable the exception to throw from the method * invocation on the proxy instance. The exception's type must be * assignable either to any of the exception types declared in the * <code>throws</code> clause of the interface method or to the * unchecked exception types <code>java.lang.RuntimeException</code> * or <code>java.lang.Error</code>. If a checked exception is * thrown by this method that is not assignable to any of the * exception types declared in the <code>throws</code> clause of * the interface method, then an * {@link java.lang.reflect.UndeclaredThrowableException} containing the * exception that was thrown by this method will be thrown by the * method invocation on the proxy instance. * @see java.lang.reflect.UndeclaredThrowableException * */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object invocationResult = null; try { invocationResult = method.invoke(ehcache, args); } catch (Exception e) { CacheExceptionHandler cacheExceptionHandler = ehcache.getCacheExceptionHandler(); if (cacheExceptionHandler != null) { String keyAsString = null; //should be a CacheException Throwable cause = e.getCause(); if (cause != null) { keyAsString = extractKey(cause.getMessage()); } Exception causeAsException = null; try { causeAsException = (Exception) cause; } catch (ClassCastException cce) { //we only handle exceptions, not errors. if (LOG.isDebugEnabled()) { LOG.debug("Underlying cause was not an Exception: " + cce); } } cacheExceptionHandler.onException(ehcache, keyAsString, causeAsException); } else { throw e.getCause(); } } return invocationResult; } /** * Extracts the key from the message, if any */ static String extractKey(String message) { if (message == null) { return null; } int beginIndex = message.lastIndexOf("key "); if (beginIndex < 0) { return null; } beginIndex += "key ".length(); int endIndex = beginIndex; for (int i = beginIndex; i < message.length(); i++) { char character = message.charAt(i); if (character == ' ') { break; } endIndex = i; } endIndex++; if (endIndex > message.length()) { endIndex = message.length(); } String key = message.substring(beginIndex, endIndex); return key; } }