Coverage Report - org.kuali.rice.kew.util.CachingInterceptor
 
Classes in this File Line Coverage Branch Coverage Complexity
CachingInterceptor
0%
0/50
0%
0/36
4.6
 
 1  
 /*
 2  
  * Copyright 2007-2009 The Kuali Foundation
 3  
  *
 4  
  * Licensed under the Educational Community License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  * http://www.opensource.org/licenses/ecl2.php
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.kuali.rice.kew.util;
 17  
 
 18  
 import java.lang.ref.SoftReference;
 19  
 import java.util.HashSet;
 20  
 import java.util.Set;
 21  
 
 22  
 import org.aopalliance.intercept.MethodInterceptor;
 23  
 import org.aopalliance.intercept.MethodInvocation;
 24  
 import org.apache.commons.logging.Log;
 25  
 import org.apache.commons.logging.LogFactory;
 26  
 import org.kuali.rice.kew.service.KEWServiceLocator;
 27  
 
 28  
 /**
 29  
  * <p>This is a generic caching proxy for KEW, using the cache administrator service to store results.  It's inteded
 30  
  * for caching query results for a DAO, but it likely has broader use.
 31  
  * <p>It will cache the results of calls matching method names which are set in {@link #setCacheForMethods}.  
 32  
  * It will clear this "cache group" for calls with matching method names set in {@link #setClearCacheOnMethods}.
 33  
  * <p>The cache group name (see {@link #setCacheGroupName(String)}) in most instances should unique for each class that 
 34  
  * is proxied.
 35  
  * <p>Configuration should be done through Spring, probably using BeanNameAutoProxyCreator.
 36  
  * <p>NOTE: One important assumption here is that the arguments for the methods being cached will all have 
 37  
  * {@link Object#toString()} implementations such that o1.equals(o2) iff o1.toString().equals(o2.toString()).
 38  
  * @author Kuali Rice Team (rice.collab@kuali.org)
 39  
  */
 40  0
 public class CachingInterceptor implements MethodInterceptor {
 41  
         
 42  0
         private static Log LOG = LogFactory.getLog(CachingInterceptor.class);
 43  
         
 44  0
         private Set<String> clearCacheOnMethods = new HashSet<String>(50);
 45  0
         private Set<String> cacheForMethods = new HashSet<String>(50);
 46  0
         private String cacheGroupName = "CachingInterceptor";
 47  
         
 48  0
         private static final Object NULL_OBJECT = new Object();
 49  
         
 50  
         public void setClearCacheOnMethods(String [] methods) {
 51  0
                 for (String method : methods) {
 52  0
                         clearCacheOnMethods.add(method);
 53  
                 }
 54  0
         }
 55  
         
 56  
         public void setCacheForMethods(String [] methods) {
 57  0
                 for (String method : methods) {
 58  0
                         cacheForMethods.add(method);
 59  
                 }
 60  0
         }
 61  
         
 62  
         public void setCacheGroupName(String name) {
 63  0
                 cacheGroupName = name;
 64  0
         }
 65  
         
 66  
         /**
 67  
          * This overridden method intercepts calls, and
 68  
          * <ul>
 69  
          * <li>if the intercepted method is in clearCacheOnMethods
 70  
          * <ul><li>the cache is purged, then the method is called and any results returned.</ul>
 71  
          * <li>if the intercepted method is in cacheForMethods
 72  
          * <ul><li>the cache is checked.  On a hit, cached results are returned.  On a miss, the invocation
 73  
          * occurrs and the results are cached.</ul>
 74  
          * <li>otherwise
 75  
          * <ul><li>the method is called and any results returned</ul>
 76  
          * </ul> 
 77  
          * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
 78  
          */
 79  
         @SuppressWarnings("unchecked")
 80  
         public Object invoke(MethodInvocation invocation) throws Throwable {
 81  
 
 82  0
                 Object results = null;
 83  
                 
 84  0
                 String methodName = invocation.getMethod().getName();
 85  
 
 86  0
                 if (clearCacheOnMethods.contains(methodName)) {
 87  0
                         if (LOG.isTraceEnabled()) { LOG.trace("clearing cache group " +cacheGroupName+" on " + methodName); }
 88  
                         // clear the cache
 89  0
                         KEWServiceLocator.getCacheAdministrator().flushGroup(cacheGroupName);
 90  0
                         results = invocation.proceed();
 91  
 
 92  0
                 } else if (cacheForMethods.contains(methodName)) {
 93  0
                         boolean gotCachedResults = false;
 94  
                         // attempt to use the cache;
 95  0
                         String cacheKey = getCacheKey(methodName, invocation.getArguments());
 96  0
                         if (cacheKey != null) {
 97  0
                                 SoftReference<Object> resultRef = 
 98  
                                         (SoftReference<Object>) KEWServiceLocator.getCacheAdministrator().getFromCache(cacheKey);
 99  
                                 
 100  0
                                 if (resultRef != null) { results = resultRef.get(); }
 101  0
                                 if (null != results) {
 102  0
                                         if (LOG.isTraceEnabled()) { LOG.trace("cache hit in group " +cacheGroupName+" on " + methodName); }
 103  0
                                         gotCachedResults = true;
 104  
                                 }
 105  
                         }
 106  
                         
 107  0
                         if (!gotCachedResults) {
 108  0
                                 if (LOG.isTraceEnabled()) { LOG.trace("cache miss in group " +cacheGroupName+" on " + methodName); }
 109  
 
 110  0
                                 results = invocation.proceed();
 111  0
                                 if (results == null) { results = NULL_OBJECT; /* we'll cache a null result too */ }
 112  
         
 113  
                                 // add to the cache
 114  0
                                 if (cacheKey != null) {
 115  0
                                         KEWServiceLocator.getCacheAdministrator().
 116  
                                             putInCache(cacheKey, new SoftReference<Object>(results), cacheGroupName);
 117  
                                 }
 118  
                         }
 119  0
                 } else {
 120  0
                         if (LOG.isTraceEnabled()) { LOG.trace("no caching enabled in group " +cacheGroupName+" on " + methodName); }
 121  0
                         results = invocation.proceed();
 122  
                 }
 123  
                 
 124  0
                 return (NULL_OBJECT == results) ? null : results;
 125  
         }
 126  
 
 127  
         /**
 128  
          * build a cache key based on the method name and the parameters
 129  
          * 
 130  
          * @param methodName
 131  
          * @param args
 132  
          * @return
 133  
          */
 134  
         private String getCacheKey(String methodName, Object [] args) {
 135  0
                 StringBuilder sb = new StringBuilder(cacheGroupName);
 136  0
                 sb.append(".");
 137  0
                 sb.append(methodName);
 138  0
                 sb.append(":");
 139  0
                 boolean first = true;
 140  0
                 for (Object arg : args) {
 141  0
                         if (first) {
 142  0
                                 first = false;
 143  
                         } else {
 144  0
                                 sb.append(",");
 145  
                         }
 146  0
                         sb.append((arg == null) ? "null" : arg.toString());
 147  
                 }
 148  
                 
 149  0
                 return sb.toString();
 150  
         }
 151  
 }