001: /**
002: * Copyright 2003-2007 Luck Consulting Pty Ltd
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */package net.sf.ehcache.exceptionhandler;
016:
017: import net.sf.ehcache.Ehcache;
018:
019: import java.lang.reflect.InvocationHandler;
020: import java.lang.reflect.Method;
021: import java.lang.reflect.Proxy;
022:
023: /**
024: * A dynamic proxy which provides CacheException handling.
025: * <p/>
026: * The ehcache configuration will create and register in the <code>CacheManager</code> {@link Ehcache}s decorated
027: * with this dynamic proxy. See following for programmatic use.
028: * <p/>
029: * The createProxy factory method may be used to simply create a proxy. Otherwise the calling client
030: * will need code similar to:
031: * <pre>
032: * (Ehcache) Proxy.newProxyInstance(ehcache.getClass().getClassLoader(), new Class[]{ Ehcache.class },
033: * new ExceptionHandlingDynamicCacheProxy(ehcache));</pre>
034: * <p/>
035: * A common usage is to create a proxy and then register the proxy in <code>CacheManager</code> in place of the
036: * underlying cache. To do that create a proxy and then call
037: * <pre>
038: * cacheManager.replaceCacheWithDecoratedCache(Ehcache cache, Ehcache decoratedCache);
039: * </pre>
040: * All clients accessing the cache through<code>cacheManager.getEhcache()</code> will then receive proxy references.
041: * <p/>
042: * See CacheTest for a perf test.
043: *
044: * @author <a href="mailto:gluck@gregluck.com">Greg Luck</a>
045: * @version $Id: ExceptionHandlingDynamicCacheProxy.java 544 2007-08-19 09:38:21Z gregluck $
046: */
047: public final class ExceptionHandlingDynamicCacheProxy implements
048: InvocationHandler {
049:
050: private Ehcache ehcache;
051:
052: /**
053: * Constructor: Use with something like:
054: * <pre>
055: * (Ehcache) Proxy.newProxyInstance(ehcache.getClass().getClassLoader(), new Class[]{ Ehcache.class },
056: * new ExceptionHandlingDynamicCacheProxy(ehcache));</pre>
057: * @param ehcache the backing ehcache
058: */
059: public ExceptionHandlingDynamicCacheProxy(Ehcache ehcache) {
060: this .ehcache = ehcache;
061: }
062:
063: /**
064: * A simple factory method to hide the messiness of creating the proxy from clients.
065: *
066: * @param ehcache the target cache
067: * @return a proxied Ehcache
068: */
069: public static Ehcache createProxy(Ehcache ehcache) {
070: return (Ehcache) Proxy.newProxyInstance(ehcache.getClass()
071: .getClassLoader(), new Class[] { Ehcache.class },
072: new ExceptionHandlingDynamicCacheProxy(ehcache));
073: }
074:
075: /**
076: * Processes a method invocation on a proxy instance and returns
077: * the result. This method will be invoked on an invocation handler
078: * when a method is invoked on a proxy instance that it is
079: * associated with.
080: *
081: * @param proxy the proxy instance that the method was invoked on
082: * @param method the <code>Method</code> instance corresponding to
083: * the interface method invoked on the proxy instance. The declaring
084: * class of the <code>Method</code> object will be the interface that
085: * the method was declared in, which may be a superinterface of the
086: * proxy interface that the proxy class inherits the method through.
087: * @param args an array of objects containing the values of the
088: * arguments passed in the method invocation on the proxy instance,
089: * or <code>null</code> if interface method takes no arguments.
090: * Arguments of primitive types are wrapped in instances of the
091: * appropriate primitive wrapper class, such as
092: * <code>java.lang.Integer</code> or <code>java.lang.Boolean</code>.
093: * @return the value to return from the method invocation on the
094: * proxy instance. If the declared return type of the interface
095: * method is a primitive type, then the value returned by
096: * this method must be an instance of the corresponding primitive
097: * wrapper class; otherwise, it must be a type assignable to the
098: * declared return type. If the value returned by this method is
099: * <code>null</code> and the interface method's return type is
100: * primitive, then a <code>NullPointerException</code> will be
101: * thrown by the method invocation on the proxy instance. If the
102: * value returned by this method is otherwise not compatible with
103: * the interface method's declared return type as described above,
104: * a <code>ClassCastException</code> will be thrown by the method
105: * invocation on the proxy instance.
106: * @throws Throwable the exception to throw from the method
107: * invocation on the proxy instance. The exception's type must be
108: * assignable either to any of the exception types declared in the
109: * <code>throws</code> clause of the interface method or to the
110: * unchecked exception types <code>java.lang.RuntimeException</code>
111: * or <code>java.lang.Error</code>. If a checked exception is
112: * thrown by this method that is not assignable to any of the
113: * exception types declared in the <code>throws</code> clause of
114: * the interface method, then an
115: * {@link java.lang.reflect.UndeclaredThrowableException} containing the
116: * exception that was thrown by this method will be thrown by the
117: * method invocation on the proxy instance.
118: * @see java.lang.reflect.UndeclaredThrowableException
119: *
120: */
121: public Object invoke(Object proxy, Method method, Object[] args)
122: throws Throwable {
123: Object invocationResult = null;
124: try {
125: invocationResult = method.invoke(ehcache, args);
126: } catch (Exception e) {
127: CacheExceptionHandler cacheExceptionHandler = ehcache
128: .getCacheExceptionHandler();
129: if (cacheExceptionHandler != null) {
130: String keyAsString = null;
131: if (e.getCause() != null) {
132: keyAsString = extractKey(e.getCause().getMessage());
133: }
134: cacheExceptionHandler.onException(ehcache, keyAsString,
135: (Exception) e.getCause());
136: } else {
137: throw e.getCause();
138: }
139:
140: }
141: return invocationResult;
142: }
143:
144: /**
145: * Extracts the key from the message, if any
146: */
147: static String extractKey(String message) {
148: if (message == null) {
149: return null;
150: }
151: int beginIndex = message.lastIndexOf("key ");
152: if (beginIndex < 0) {
153: return null;
154: }
155: beginIndex += "key ".length();
156: int endIndex = beginIndex;
157: for (int i = beginIndex; i < message.length(); i++) {
158: char character = message.charAt(i);
159: if (character == ' ') {
160: break;
161: }
162: endIndex = i;
163: }
164: endIndex++;
165: if (endIndex > message.length()) {
166: endIndex = message.length();
167: }
168: String key = message.substring(beginIndex, endIndex);
169: return key;
170: }
171: }
|