001 /* 002 Copyright (C) 2001 Renaud Pawlak <renaud@aopsys.com> 003 004 This program is free software; you can redistribute it and/or modify 005 it under the terms of the GNU Lesser General Public License as 006 published by the Free Software Foundation; either version 2 of the 007 License, or (at your option) any later version. 008 009 This program is distributed in the hope that it will be useful, 010 but WITHOUT ANY WARRANTY; without even the implied warranty of 011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 GNU Lesser General Public License for more details. 013 014 You should have received a copy of the GNU Lesser General Public License 015 along with this program; if not, write to the Free Software 016 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 017 018 package org.objectweb.jac.wrappers; 019 020 import org.aopalliance.intercept.ConstructorInvocation; 021 import org.aopalliance.intercept.MethodInvocation; 022 import org.objectweb.jac.core.AspectComponent; 023 import org.objectweb.jac.core.Interaction; 024 import org.objectweb.jac.core.Wrapper; 025 026 import java.util.Vector; 027 028 /** 029 * <code>CacheWrapper</code> implements a wrapper that caches the wrappee 030 * methods results so that it can avoid the wrappee computations. It 031 * can improve performances of complex computations. 032 * 033 * <p>Please make sure to use the rigth wrapping method. 034 * 035 * <p>For example, if you have a <code>Matrix</code> class: 036 * 037 * <ul><pre> 038 * public class Matrix { 039 * invert(); 040 * ... 041 * } 042 * </pre></ul> 043 * 044 * <p>An instance of <code>Matrix</code> must be wrapped with 045 * <code>stateCache</code> since the result depends on the matrix 046 * state: 047 * 048 * <ul><pre> 049 * CacheWrapper cw = new CacheWrapper(); 050 * a_matrix.wrap(cw, "stateCache", "invert"); 051 * </pre></ul> 052 * 053 * <p>However, when the state of the matrix is modified, the cache 054 * should be cleared since its values are not valid anymore. This can 055 * be done by using the wrapping method <code>clearCache</code>. 056 * 057 * <ul><pre> 058 * a_matrix.wrap(cw, "clearCache", new String[] { "set", "mul", "div", "add" } ); 059 * </pre></ul> 060 */ 061 062 public class CacheWrapper extends Wrapper { 063 064 /** Store the keys for the cache. */ 065 protected Vector cacheKeys = new Vector(); 066 /** Store the cached values. */ 067 protected Vector cacheValues = new Vector(); 068 069 public CacheWrapper (AspectComponent ac) { 070 super(ac); 071 } 072 073 /** 074 * This wrapping method seeks in the cache wether the couple 075 * (method, args) has already been called on the wrappee 076 * object. If yes, it returns the value from the cache. Otherwise 077 * it calls the wrappee object and memorizes the result in the 078 * cache. 079 * 080 * <p>This wrapper can be used on objects that have complex, but 081 * state independant computation to perform. For instance, 082 * <code>cache</code> could wrap a computational object that would 083 * be able to invert a matrix so that the inversion of a given 084 * matrix would be done only once. 085 * 086 * <p>NOTE: if the wrappee function depends on the the object 087 * state the values returned by the <code>statelessCache</code> 088 * will be wrong. In this case, use the <cache>stateCache</cache> 089 * wrapping method. 090 * 091 * @see CacheWrapper#stateCache(Interaction) 092 */ 093 094 public Object statelessCache(Interaction interaction) { 095 Object[] key = new Object[] { interaction.method, interaction.args }; 096 int i; 097 if((i = isCacheHit(key)) != -1) { 098 return getCacheValue(i); 099 } 100 Object ret; 101 setCacheValue(key, ret = proceed(interaction)); 102 return ret; 103 } 104 105 /** 106 * This wrapping method seeks in the cache wether the triple 107 * (wrappe, method, args) has already been called on the wrappee 108 * object when wrappee was in the same state. If yes, it returns 109 * the value from the cache. Otherwise it calls the wrappee object 110 * and memorizes the result in the cache. 111 * 112 * <p>This wrapper can be used on objects that have complex state 113 * dependant computation to perform. For instance, 114 * <code>cache</code> could wrap the invert method of a matrix 115 * object. If you invert this matrix three times, then the third 116 * inversion result will be found in the cache. 117 * 118 * <p>NOTE: if the wrappee method computation does not depend on 119 * the wrappee state, then you should use the 120 * <code>statelessCache</code> method for better performance and 121 * hits rate. 122 * 123 * @see CacheWrapper#statelessCache(Interaction) 124 */ 125 126 public Object stateCache(Interaction interaction) { 127 System.out.println("-> Testing the cache..."); 128 Object[] key = new Object[] { interaction.method, interaction.args, 129 interaction.wrappee }; 130 int i; 131 if((i = isCacheHit(key)) != -1) { 132 System.out.println("-> Cache hit!"); 133 return getCacheValue(i); 134 } 135 System.out.println("-> Cache miss."); 136 Object ret; 137 setCacheValue(key, ret = proceed(interaction)); 138 return ret; 139 } 140 141 /** 142 * Clear the cache. 143 * 144 * <p>This method should wrap all the methods that change the state 145 * of the wrappee in a case of a state cache. 146 * 147 * @return the value returned by the wrapped method 148 */ 149 150 public Object clearCache(Interaction interaction) { 151 cacheKeys.clear(); 152 cacheValues.clear(); 153 return proceed(interaction); 154 } 155 156 /** 157 * Add a (method, args) key and its value into the cache. 158 * 159 * @param key the key to find the value in the cache 160 * @param value the cached value 161 * 162 * @see CacheWrapper#getCacheValue(Object[]) 163 */ 164 165 protected void setCacheValue(Object[] key, Object value) { 166 cacheKeys.add(key); 167 cacheValues.add(value); 168 } 169 170 /** 171 * Get the memorized value of the method when called with the 172 * given (method, args) key. 173 * 174 * @param key the key 175 * @return the cached value that matches the key 176 * 177 * @see CacheWrapper#setCacheValue(Object[],Object) 178 */ 179 protected Object getCacheValue(Object[] key) { 180 return getCacheValue(isCacheHit(key)); 181 } 182 183 /** 184 * Get a cached value. 185 * 186 * @param index the index in the cache 187 * @return the cached value 188 */ 189 190 protected Object getCacheValue(int index) { 191 if (index == -1) return null; 192 return cacheValues.get(index); 193 } 194 195 /** 196 * Returns the index of a key in the cache, -1 if not found. 197 * 198 * @param key a complex key 199 * @return the location of the key 200 */ 201 202 protected int isCacheHit(Object[] key) { 203 for (int i = 0; i < cacheKeys.size(); i ++) { 204 boolean same = true; 205 Object[] cur_key = (Object[])cacheKeys.get(i); 206 if (cur_key.length != key.length) { 207 System.out.println("A"); 208 same = false; 209 break; 210 } 211 if(!cur_key[0].equals(key[0])) { 212 System.out.println("B"); 213 same = false; 214 break; 215 } 216 if (((Object[])cur_key[1]).length != ((Object[])key[1]).length) { 217 System.out.println("C"); 218 same = false; 219 break; 220 } 221 for (int j = 0; j < ((Object[])cur_key[1]).length; j++) { 222 if(!((Object[])cur_key[1])[j].equals(((Object[])key[1])[j])) { 223 System.out.println("D" + ((Object[])cur_key[1])[j] + ((Object[])key[1])[j]); 224 same = false; 225 break; 226 } 227 } 228 if (key.length == 3) { 229 if(cur_key[2] != key[2]) { 230 System.out.println("E"); 231 same = false; 232 break; 233 } 234 } 235 if (same) { 236 return i; 237 } 238 } 239 return -1; 240 } 241 242 /* (non-Javadoc) 243 * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) 244 */ 245 public Object invoke(MethodInvocation invocation) throws Throwable { 246 // TODO Auto-generated method stub 247 return null; 248 } 249 250 /* (non-Javadoc) 251 * @see org.aopalliance.intercept.ConstructorInterceptor#construct(org.aopalliance.intercept.ConstructorInvocation) 252 */ 253 public Object construct(ConstructorInvocation invocation) throws Throwable { 254 // TODO Auto-generated method stub 255 return null; 256 } 257 258 } 259 260 261 262 263