1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.cache.jcache.interceptor;
18
19 import javax.cache.annotation.CacheResult;
20
21 import org.springframework.cache.Cache;
22 import org.springframework.cache.interceptor.CacheErrorHandler;
23 import org.springframework.cache.interceptor.CacheOperationInvocationContext;
24 import org.springframework.cache.interceptor.CacheOperationInvoker;
25 import org.springframework.cache.interceptor.CacheResolver;
26 import org.springframework.util.ExceptionTypeFilter;
27 import org.springframework.util.SerializationUtils;
28
29
30
31
32
33
34
35 @SuppressWarnings("serial")
36 class CacheResultInterceptor extends AbstractKeyCacheInterceptor<CacheResultOperation, CacheResult> {
37
38 public CacheResultInterceptor(CacheErrorHandler errorHandler) {
39 super(errorHandler);
40 }
41
42 @Override
43 protected Object invoke(CacheOperationInvocationContext<CacheResultOperation> context,
44 CacheOperationInvoker invoker) {
45
46 CacheResultOperation operation = context.getOperation();
47 Object cacheKey = generateKey(context);
48
49 Cache cache = resolveCache(context);
50 Cache exceptionCache = resolveExceptionCache(context);
51
52 if (!operation.isAlwaysInvoked()) {
53 Cache.ValueWrapper cachedValue = doGet(cache, cacheKey);
54 if (cachedValue != null) {
55 return cachedValue.get();
56 }
57 checkForCachedException(exceptionCache, cacheKey);
58 }
59
60 try {
61 Object invocationResult = invoker.invoke();
62 if (invocationResult != null) {
63 cache.put(cacheKey, invocationResult);
64 }
65 return invocationResult;
66 }
67 catch (CacheOperationInvoker.ThrowableWrapper ex) {
68 Throwable original = ex.getOriginal();
69 cacheException(exceptionCache, operation.getExceptionTypeFilter(), cacheKey, original);
70 throw ex;
71 }
72 }
73
74
75
76
77 protected void checkForCachedException(Cache exceptionCache, Object cacheKey) {
78 if (exceptionCache == null) {
79 return;
80 }
81 Cache.ValueWrapper result = doGet(exceptionCache, cacheKey);
82 if (result != null) {
83 throw rewriteCallStack((Throwable) result.get(), getClass().getName(), "invoke");
84 }
85 }
86
87
88 protected void cacheException(Cache exceptionCache, ExceptionTypeFilter filter, Object cacheKey, Throwable ex) {
89 if (exceptionCache == null) {
90 return;
91 }
92 if (filter.match(ex.getClass())) {
93 exceptionCache.put(cacheKey, ex);
94 }
95 }
96
97
98 private Cache resolveExceptionCache(CacheOperationInvocationContext<CacheResultOperation> context) {
99 CacheResolver exceptionCacheResolver = context.getOperation().getExceptionCacheResolver();
100 if (exceptionCacheResolver != null) {
101 return extractFrom(context.getOperation().getExceptionCacheResolver().resolveCaches(context));
102 }
103 return null;
104 }
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 private static CacheOperationInvoker.ThrowableWrapper rewriteCallStack(
122 Throwable exception, String className, String methodName) {
123
124 Throwable clone = cloneException(exception);
125 if (clone == null) {
126 return new CacheOperationInvoker.ThrowableWrapper(exception);
127 }
128
129 StackTraceElement[] callStack = new Exception().getStackTrace();
130 StackTraceElement[] cachedCallStack = exception.getStackTrace();
131
132 int index = findCommonAncestorIndex(callStack, className, methodName);
133 int cachedIndex = findCommonAncestorIndex(cachedCallStack, className, methodName);
134 if (index == -1 || cachedIndex == -1) {
135 return new CacheOperationInvoker.ThrowableWrapper(exception);
136 }
137 StackTraceElement[] result = new StackTraceElement[cachedIndex + callStack.length - index];
138 System.arraycopy(cachedCallStack, 0, result, 0, cachedIndex);
139 System.arraycopy(callStack, index, result, cachedIndex, callStack.length - index);
140
141 clone.setStackTrace(result);
142 return new CacheOperationInvoker.ThrowableWrapper(clone);
143 }
144
145 @SuppressWarnings("unchecked")
146 private static <T extends Throwable> T cloneException(T exception) {
147 try {
148 return (T) SerializationUtils.deserialize(SerializationUtils.serialize(exception));
149 }
150 catch (Exception ex) {
151 return null;
152 }
153 }
154
155 private static int findCommonAncestorIndex(StackTraceElement[] callStack, String className, String methodName) {
156 for (int i = 0; i < callStack.length; i++) {
157 StackTraceElement element = callStack[i];
158 if (className.equals(element.getClassName()) && methodName.equals(element.getMethodName())) {
159 return i;
160 }
161 }
162 return -1;
163 }
164
165 }