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