1 /***
2 * Copyright 2003-2010 Terracotta, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package net.sf.ehcache.exceptionhandler;
18
19 import net.sf.ehcache.Ehcache;
20
21 import java.lang.reflect.InvocationHandler;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Proxy;
24
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
27
28
29 /***
30 * A dynamic proxy which provides CacheException handling.
31 * <p/>
32 * The ehcache configuration will create and register in the <code>CacheManager</code> {@link Ehcache}s decorated
33 * with this dynamic proxy. See following for programmatic use.
34 * <p/>
35 * The createProxy factory method may be used to simply create a proxy. Otherwise the calling client
36 * will need code similar to:
37 * <pre>
38 * (Ehcache) Proxy.newProxyInstance(ehcache.getClass().getClassLoader(), new Class[]{ Ehcache.class },
39 * new ExceptionHandlingDynamicCacheProxy(ehcache));</pre>
40 * <p/>
41 * A common usage is to create a proxy and then register the proxy in <code>CacheManager</code> in place of the
42 * underlying cache. To do that create a proxy and then call
43 * <pre>
44 * cacheManager.replaceCacheWithDecoratedCache(Ehcache cache, Ehcache decoratedCache);
45 * </pre>
46 * All clients accessing the cache through<code>cacheManager.getEhcache()</code> will then receive proxy references.
47 * <p/>
48 * See CacheTest for a perf test.
49 *
50 * @author <a href="mailto:gluck@gregluck.com">Greg Luck</a>
51 * @version $Id: ExceptionHandlingDynamicCacheProxy.html 13146 2011-08-01 17:12:39Z oletizi $
52 */
53 public final class ExceptionHandlingDynamicCacheProxy implements InvocationHandler {
54
55 private static final Logger LOG = LoggerFactory.getLogger(ExceptionHandlingDynamicCacheProxy.class.getName());
56
57 private Ehcache ehcache;
58
59 /***
60 * Constructor: Use with something like:
61 * <pre>
62 * (Ehcache) Proxy.newProxyInstance(ehcache.getClass().getClassLoader(), new Class[]{ Ehcache.class },
63 * new ExceptionHandlingDynamicCacheProxy(ehcache));</pre>
64 * @param ehcache the backing ehcache
65 */
66 public ExceptionHandlingDynamicCacheProxy(Ehcache ehcache) {
67 this.ehcache = ehcache;
68 }
69
70 /***
71 * A simple factory method to hide the messiness of creating the proxy from clients.
72 *
73 * @param ehcache the target cache
74 * @return a proxied Ehcache
75 */
76 public static Ehcache createProxy(Ehcache ehcache) {
77 return (Ehcache) Proxy.newProxyInstance(ehcache.getClass().getClassLoader(), new Class[]{Ehcache.class},
78 new ExceptionHandlingDynamicCacheProxy(ehcache));
79 }
80
81
82
83 /***
84 * Processes a method invocation on a proxy instance and returns
85 * the result. This method will be invoked on an invocation handler
86 * when a method is invoked on a proxy instance that it is
87 * associated with.
88 *
89 * @param proxy the proxy instance that the method was invoked on
90 * @param method the <code>Method</code> instance corresponding to
91 * the interface method invoked on the proxy instance. The declaring
92 * class of the <code>Method</code> object will be the interface that
93 * the method was declared in, which may be a superinterface of the
94 * proxy interface that the proxy class inherits the method through.
95 * @param args an array of objects containing the values of the
96 * arguments passed in the method invocation on the proxy instance,
97 * or <code>null</code> if interface method takes no arguments.
98 * Arguments of primitive types are wrapped in instances of the
99 * appropriate primitive wrapper class, such as
100 * <code>java.lang.Integer</code> or <code>java.lang.Boolean</code>.
101 * @return the value to return from the method invocation on the
102 * proxy instance. If the declared return type of the interface
103 * method is a primitive type, then the value returned by
104 * this method must be an instance of the corresponding primitive
105 * wrapper class; otherwise, it must be a type assignable to the
106 * declared return type. If the value returned by this method is
107 * <code>null</code> and the interface method's return type is
108 * primitive, then a <code>NullPointerException</code> will be
109 * thrown by the method invocation on the proxy instance. If the
110 * value returned by this method is otherwise not compatible with
111 * the interface method's declared return type as described above,
112 * a <code>ClassCastException</code> will be thrown by the method
113 * invocation on the proxy instance.
114 * @throws Throwable the exception to throw from the method
115 * invocation on the proxy instance. The exception's type must be
116 * assignable either to any of the exception types declared in the
117 * <code>throws</code> clause of the interface method or to the
118 * unchecked exception types <code>java.lang.RuntimeException</code>
119 * or <code>java.lang.Error</code>. If a checked exception is
120 * thrown by this method that is not assignable to any of the
121 * exception types declared in the <code>throws</code> clause of
122 * the interface method, then an
123 * {@link java.lang.reflect.UndeclaredThrowableException} containing the
124 * exception that was thrown by this method will be thrown by the
125 * method invocation on the proxy instance.
126 * @see java.lang.reflect.UndeclaredThrowableException
127 *
128 */
129 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
130 Object invocationResult = null;
131 try {
132 invocationResult = method.invoke(ehcache, args);
133 } catch (Exception e) {
134 CacheExceptionHandler cacheExceptionHandler = ehcache.getCacheExceptionHandler();
135 if (cacheExceptionHandler != null) {
136 String keyAsString = null;
137
138 Throwable cause = e.getCause();
139 if (cause != null) {
140 keyAsString = extractKey(cause.getMessage());
141 }
142 Exception causeAsException = null;
143 try {
144 causeAsException = (Exception) cause;
145 } catch (ClassCastException cce) {
146
147 LOG.debug("Underlying cause was not an Exception: {}", cce);
148 }
149
150 cacheExceptionHandler.onException(ehcache, keyAsString, causeAsException);
151 } else {
152 throw e.getCause();
153 }
154
155 }
156 return invocationResult;
157 }
158
159 /***
160 * Extracts the key from the message, if any
161 */
162 static String extractKey(String message) {
163 if (message == null) {
164 return null;
165 }
166 int beginIndex = message.lastIndexOf("key ");
167 if (beginIndex < 0) {
168 return null;
169 }
170 beginIndex += "key ".length();
171 int endIndex = beginIndex;
172 for (int i = beginIndex; i < message.length(); i++) {
173 char character = message.charAt(i);
174 if (character == ' ') {
175 break;
176 }
177 endIndex = i;
178 }
179 endIndex++;
180 if (endIndex > message.length()) {
181 endIndex = message.length();
182 }
183 String key = message.substring(beginIndex, endIndex);
184 return key;
185 }
186 }