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}