001/* 002 * Copyright 2002-2017 the original author or authors. 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 * https://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 */ 016 017package org.springframework.cache.jcache.interceptor; 018 019import java.lang.annotation.Annotation; 020import java.lang.reflect.Method; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025import org.springframework.aop.framework.AopProxyUtils; 026import org.springframework.beans.factory.InitializingBean; 027import org.springframework.cache.interceptor.AbstractCacheInvoker; 028import org.springframework.cache.interceptor.BasicOperation; 029import org.springframework.cache.interceptor.CacheOperationInvocationContext; 030import org.springframework.cache.interceptor.CacheOperationInvoker; 031import org.springframework.util.Assert; 032 033/** 034 * Base class for JSR-107 caching aspects, such as the {@link JCacheInterceptor} 035 * or an AspectJ aspect. 036 * 037 * <p>Use the Spring caching abstraction for cache-related operations. No JSR-107 038 * {@link javax.cache.Cache} or {@link javax.cache.CacheManager} are required to 039 * process standard JSR-107 cache annotations. 040 * 041 * <p>The {@link JCacheOperationSource} is used for determining caching operations 042 * 043 * <p>A cache aspect is serializable if its {@code JCacheOperationSource} is serializable. 044 * 045 * @author Stephane Nicoll 046 * @since 4.1 047 * @see org.springframework.cache.interceptor.CacheAspectSupport 048 * @see KeyGeneratorAdapter 049 * @see CacheResolverAdapter 050 */ 051public class JCacheAspectSupport extends AbstractCacheInvoker implements InitializingBean { 052 053 protected final Log logger = LogFactory.getLog(getClass()); 054 055 private JCacheOperationSource cacheOperationSource; 056 057 private boolean initialized = false; 058 059 private CacheResultInterceptor cacheResultInterceptor; 060 061 private CachePutInterceptor cachePutInterceptor; 062 063 private CacheRemoveEntryInterceptor cacheRemoveEntryInterceptor; 064 065 private CacheRemoveAllInterceptor cacheRemoveAllInterceptor; 066 067 068 public void setCacheOperationSource(JCacheOperationSource cacheOperationSource) { 069 Assert.notNull(cacheOperationSource, "JCacheOperationSource must not be null"); 070 this.cacheOperationSource = cacheOperationSource; 071 } 072 073 /** 074 * Return the CacheOperationSource for this cache aspect. 075 */ 076 public JCacheOperationSource getCacheOperationSource() { 077 return this.cacheOperationSource; 078 } 079 080 @Override 081 public void afterPropertiesSet() { 082 Assert.state(getCacheOperationSource() != null, "The 'cacheOperationSource' property is required: " + 083 "If there are no cacheable methods, then don't use a cache aspect."); 084 Assert.state(getErrorHandler() != null, "The 'errorHandler' property is required"); 085 086 this.cacheResultInterceptor = new CacheResultInterceptor(getErrorHandler()); 087 this.cachePutInterceptor = new CachePutInterceptor(getErrorHandler()); 088 this.cacheRemoveEntryInterceptor = new CacheRemoveEntryInterceptor(getErrorHandler()); 089 this.cacheRemoveAllInterceptor = new CacheRemoveAllInterceptor(getErrorHandler()); 090 091 this.initialized = true; 092 } 093 094 095 protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) { 096 // Check whether aspect is enabled to cope with cases where the AJ is pulled in automatically 097 if (this.initialized) { 098 Class<?> targetClass = AopProxyUtils.ultimateTargetClass(target); 099 JCacheOperation<?> operation = getCacheOperationSource().getCacheOperation(method, targetClass); 100 if (operation != null) { 101 CacheOperationInvocationContext<?> context = 102 createCacheOperationInvocationContext(target, args, operation); 103 return execute(context, invoker); 104 } 105 } 106 107 return invoker.invoke(); 108 } 109 110 @SuppressWarnings("unchecked") 111 private CacheOperationInvocationContext<?> createCacheOperationInvocationContext( 112 Object target, Object[] args, JCacheOperation<?> operation) { 113 114 return new DefaultCacheInvocationContext<Annotation>( 115 (JCacheOperation<Annotation>) operation, target, args); 116 } 117 118 @SuppressWarnings("unchecked") 119 private Object execute(CacheOperationInvocationContext<?> context, CacheOperationInvoker invoker) { 120 CacheOperationInvoker adapter = new CacheOperationInvokerAdapter(invoker); 121 BasicOperation operation = context.getOperation(); 122 123 if (operation instanceof CacheResultOperation) { 124 return this.cacheResultInterceptor.invoke( 125 (CacheOperationInvocationContext<CacheResultOperation>) context, adapter); 126 } 127 else if (operation instanceof CachePutOperation) { 128 return this.cachePutInterceptor.invoke( 129 (CacheOperationInvocationContext<CachePutOperation>) context, adapter); 130 } 131 else if (operation instanceof CacheRemoveOperation) { 132 return this.cacheRemoveEntryInterceptor.invoke( 133 (CacheOperationInvocationContext<CacheRemoveOperation>) context, adapter); 134 } 135 else if (operation instanceof CacheRemoveAllOperation) { 136 return this.cacheRemoveAllInterceptor.invoke( 137 (CacheOperationInvocationContext<CacheRemoveAllOperation>) context, adapter); 138 } 139 else { 140 throw new IllegalArgumentException("Cannot handle " + operation); 141 } 142 } 143 144 /** 145 * Execute the underlying operation (typically in case of cache miss) and return 146 * the result of the invocation. If an exception occurs it will be wrapped in 147 * a {@code ThrowableWrapper}: the exception can be handled or modified but it 148 * <em>must</em> be wrapped in a {@code ThrowableWrapper} as well. 149 * @param invoker the invoker handling the operation being cached 150 * @return the result of the invocation 151 * @see CacheOperationInvoker#invoke() 152 */ 153 protected Object invokeOperation(CacheOperationInvoker invoker) { 154 return invoker.invoke(); 155 } 156 157 158 private class CacheOperationInvokerAdapter implements CacheOperationInvoker { 159 160 private final CacheOperationInvoker delegate; 161 162 public CacheOperationInvokerAdapter(CacheOperationInvoker delegate) { 163 this.delegate = delegate; 164 } 165 166 @Override 167 public Object invoke() throws ThrowableWrapper { 168 return invokeOperation(this.delegate); 169 } 170 } 171 172}