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