View Javadoc

1   /*
2    * Copyright 2001-2004 The Apache Software Foundation
3    *
4    * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
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.apache.commons.cache;
17  
18  import java.util.HashMap;
19  import java.util.ArrayList;
20  import java.util.NoSuchElementException;
21  import java.io.Serializable;
22  import java.io.ObjectInputStream;
23  import java.io.IOException;
24  import java.io.NotActiveException;
25  import org.apache.commons.cache.adt.Listable;
26  import org.apache.commons.cache.adt.WListableObject;
27  
28  /**
29   * tk.
30   * @version $Id: LRUEvictionPolicy.java 155435 2005-02-26 13:17:27Z dirkv $
31   * @author Rodney Waldhoff
32   */
33  public class LRUEvictionPolicy implements EvictionPolicy, StorageListener, RetrievalListener, CachedObjectIterator {
34    transient protected HashMap _hash = new HashMap();
35    transient protected WListableObject _leastRecent = null;
36    transient protected WListableObject _mostRecent = null;
37    transient protected WListableObject _current = null;
38  
39    protected boolean _canRemove = false;
40    public Cache _cache = null;
41    protected int _objsbetweennaps;
42    protected long _sleeptimemillis;
43  
44    public LRUEvictionPolicy() {
45      this(StaleObjectEvictor.DEFAULT_OBJECTS_BETWEEN_NAPS,StaleObjectEvictor.DEFAULT_SLEEP_TIME_MILLIS);
46    }
47  
48    public LRUEvictionPolicy(int objsbetweennaps, long sleeptimemillis) {
49      _objsbetweennaps = objsbetweennaps;
50      _sleeptimemillis = sleeptimemillis;
51      Thread t = new Thread(new StaleObjectEvictor(this,_objsbetweennaps,_sleeptimemillis));
52      t.setDaemon(true);
53      t.start();
54    }
55  
56    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
57       out.defaultWriteObject();
58       WListableObject cur = _leastRecent;
59       while(null != cur) {
60          out.writeObject(cur.getValue());
61          cur =(WListableObject)(cur.getNext());
62       }
63       out.writeObject(null);
64    }
65  
66    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
67      try {
68        in.defaultReadObject();
69        _hash = new HashMap();
70        for(;;) {
71           Object obj = in.readObject();
72           if(null == obj) {
73              break;
74           } else {
75              WListableObject temp = new WListableObject(obj,_mostRecent,null);
76              if(null == _leastRecent) {
77                 _leastRecent = temp;
78                 _current = temp;
79              }
80              if(null != _mostRecent) {
81                 _mostRecent.setNext(temp);
82              }
83              _mostRecent = temp;
84              _hash.put(((CachedObjectInfo)obj).getKey(),_mostRecent);
85           }
86        }
87  
88        Thread t = new Thread(new StaleObjectEvictor(this,_objsbetweennaps,_sleeptimemillis));
89        t.setDaemon(true);
90        t.start();
91      } catch(NotActiveException e) {
92        e.printStackTrace();
93      }
94    }
95  
96    protected synchronized void addToList(WListableObject cur) {
97      if(null == _mostRecent) {
98        _mostRecent = cur;
99        _leastRecent = cur;
100     } else {
101       _mostRecent.setNext(cur);
102       cur.setPrev(_mostRecent);
103       _mostRecent = cur;
104     }
105   }
106 
107   protected synchronized void removeFromList(WListableObject cur) {
108     // post condition: cur.next and cur.prev are both null
109     if(_current == cur) {
110       _current = (WListableObject)(_current.getPrev());
111       _canRemove = false;
112     }
113     if(cur.hasPrev()) {
114       if(cur.hasNext()) {
115         cur.getPrev().setNext(cur.getNext());
116         cur.getNext().setPrev(cur.getPrev());
117         cur.setNext(null);
118         cur.setPrev(null);
119       } else {
120         // else cur is the most recent and cur.next is null
121         _mostRecent = (WListableObject)(cur.getPrev());
122         cur.getPrev().setNext(null);
123         cur.setPrev(null);
124       }
125     } else {
126       // else cur is the least recent and cur.prev is null
127       _leastRecent = (WListableObject)(cur.getNext());
128       if(cur.hasNext()) {
129         cur.getNext().setPrev(null);
130         cur.setNext(null);
131       } else {
132         // else cur is both least and most recent
133         // and both next and prev are null
134         _leastRecent = null;
135         _mostRecent = null;
136       }
137     }
138   }
139 
140 
141   public synchronized boolean hasNext() {
142     return (null != _mostRecent);
143   }
144 
145   public synchronized CachedObjectInfo getNext() throws NoSuchElementException {
146     if(null != _current) {
147       _current = (WListableObject)(_current.getNext());
148     }
149     if(null == _current) { _current = _leastRecent; }
150     if(null == _current) {
151       throw new NoSuchElementException();
152     } else {
153       _canRemove = true;
154       return (CachedObjectInfo)(_current.getValue());
155     }
156   }
157 
158   public synchronized Object next() throws NoSuchElementException {
159     return getNext();
160   }
161 
162   public void remove() throws NoSuchElementException, IllegalStateException {
163     // grab a lock on _cache first, since _cache may try to grab a lock on this
164     synchronized(_cache) {
165       synchronized(this) {
166         if(null == _current) {
167           throw new IllegalStateException();
168         }
169         if(_canRemove) {
170           _cache.clear( ((CachedObjectInfo)(_current.getValue())).getKey() );
171         } else {
172           throw new IllegalStateException();
173         }
174       }
175     }
176   }
177 
178 
179   /** Sets my cache. */
180   public void setCache(Cache c) {
181     if(null != _cache) {
182       Object mutex = _cache;
183       synchronized(mutex) {
184         synchronized(c) {
185           synchronized(this) {
186             unsetCache();
187             _cache = c;
188             _cache.registerRetrievalListener(this);
189             _cache.registerStorageListener(this);
190           }
191         }
192       }
193     } else {
194       synchronized(c) {
195         synchronized(this) {
196           _cache = c;
197           _cache.registerRetrievalListener(this);
198           _cache.registerStorageListener(this);
199         }
200       }
201     }
202   }
203 
204   /** Unsets my cache. */
205   public void unsetCache() {
206     if(null != _cache) {
207       Object mutex = _cache;
208       synchronized(mutex) {
209         synchronized(this) {
210           _cache.unregisterRetrievalListener(this);
211           _cache.unregisterStorageListener(this);
212           _cache = null;
213         }
214       }
215     }
216   }
217 
218   public void storeRequested(Serializable key, Serializable val, Long expiresAt, Long cost, Serializable group) { }
219 
220   public void notStored(Serializable key, Serializable val, Long expiresAt, Long cost, Serializable group) { }
221 
222   public synchronized void stored(Serializable key, Serializable val, Long expiresAt, Long cost, Serializable group) {
223     WListableObject cur = (WListableObject)(_hash.get(key));
224     if(null == cur) {
225       cur = new WListableObject(new CachedObjectInfoImpl(key,expiresAt,cost));
226       addToList(cur);
227       _hash.put(key,cur);
228     } else {
229       removeFromList(cur);
230       addToList(cur);
231     }
232   }
233 
234   public synchronized void cleared(Serializable key) {
235     WListableObject cur = (WListableObject)(_hash.get(key));
236     if(cur != null) {
237       removeFromList(cur);
238       _hash.remove(key);
239     }
240   }
241 
242   public synchronized void cleared() {
243     _hash.clear();
244     _mostRecent = null;
245     _leastRecent = null;
246     _current = null;
247   }
248 
249   public void retrieveRequested(Serializable key) {
250   }
251 
252   public synchronized void retrieved(Serializable key) {
253     WListableObject cur = (WListableObject)(_hash.get(key));
254     if(null == cur) {
255       cur = new WListableObject(new CachedObjectInfoImpl(key,null,null));
256       addToList(cur);
257       _hash.put(key,cur);
258     } else {
259       removeFromList(cur);
260       addToList(cur);
261     }
262   }
263 
264   public void notRetrieved(Serializable key) {
265   }
266 
267   public synchronized Serializable getEvictionCandidate() {
268     try {
269       return ((CachedObjectInfoImpl)(_leastRecent.getValue())).getKey();
270     } catch(NullPointerException e) {
271       return null;
272     }
273   }
274 
275   public synchronized Serializable[] getEvictionCandidates(int n) {
276     ArrayList v = new ArrayList(n);
277     WListableObject c = _leastRecent;
278     while(n-- > 0) {
279       if(null != c) {
280         v.add(((CachedObjectInfoImpl)(c.getValue())).getKey());
281         c = (WListableObject)(c.getNext());
282       } else {
283         break;
284       }
285     }
286     return (Serializable[])(v.toArray(new Serializable[0]));
287   }
288 
289   public synchronized String toString() {
290     StringBuffer buf = new StringBuffer();
291     WListableObject c = _leastRecent;
292     while(c != null) {
293       if(c.hasPrev()) {
294         buf.append(", ");
295       }
296       buf.append(c.getValue());
297       c = (WListableObject)(c.getNext());
298     }
299     return buf.toString();
300   }
301 }