add --shared
[pylucene.git] / lucene-java-3.4.0 / lucene / src / java / org / apache / lucene / search / FilterManager.java
1 package org.apache.lucene.search;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.util.Comparator;
21 import java.util.Date;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.TreeSet;
26
27 import org.apache.lucene.util.ThreadInterruptedException;
28
29 /**
30  * Filter caching singleton. It can be used to save filters locally for reuse.
31  * This class makes it possible to cache Filters even when using RMI, as it
32  * keeps the cache on the searcher side of the RMI connection.
33  * 
34  * Also could be used as a persistent storage for any filter as long as the
35  * filter provides a proper hashCode(), as that is used as the key in the cache.
36  * 
37  * The cache is periodically cleaned up from a separate thread to ensure the
38  * cache doesn't exceed the maximum size.
39  * 
40  * @deprecated used by remote package which is deprecated as well. You should
41  *             use {@link CachingWrapperFilter} if you wish to cache
42  *             {@link Filter}s.
43  */
44 @Deprecated
45 public class FilterManager {
46
47   protected static FilterManager manager;
48   
49   /** The default maximum number of Filters in the cache */
50   protected static final int  DEFAULT_CACHE_CLEAN_SIZE = 100;
51   /** The default frequency of cache cleanup */
52   protected static final long DEFAULT_CACHE_SLEEP_TIME = 1000 * 60 * 10;
53
54   /** The cache itself */
55   protected Map<Integer,FilterItem>           cache;
56   /** Maximum allowed cache size */
57   protected int           cacheCleanSize;
58   /** Cache cleaning frequency */
59   protected long          cleanSleepTime;
60   /** Cache cleaner that runs in a separate thread */
61   protected FilterCleaner filterCleaner;
62
63   public synchronized static FilterManager getInstance() {
64     if (manager == null) {
65       manager = new FilterManager();
66     }
67     return manager;
68   }
69
70   /**
71    * Sets up the FilterManager singleton.
72    */
73   protected FilterManager() {
74     cache            = new HashMap<Integer,FilterItem>();
75     cacheCleanSize   = DEFAULT_CACHE_CLEAN_SIZE; // Let the cache get to 100 items
76     cleanSleepTime   = DEFAULT_CACHE_SLEEP_TIME; // 10 minutes between cleanings
77
78     filterCleaner   = new FilterCleaner();
79     Thread fcThread = new Thread(filterCleaner);
80     // set to be a Daemon so it doesn't have to be stopped
81     fcThread.setDaemon(true);
82     fcThread.start();
83   }
84   
85   /**
86    * Sets the max size that cache should reach before it is cleaned up
87    * @param cacheCleanSize maximum allowed cache size
88    */
89   public void setCacheSize(int cacheCleanSize) {
90     this.cacheCleanSize = cacheCleanSize;
91   }
92
93   /**
94    * Sets the cache cleaning frequency in milliseconds.
95    * @param cleanSleepTime cleaning frequency in milliseconds
96    */
97   public void setCleanThreadSleepTime(long cleanSleepTime) {
98     this.cleanSleepTime  = cleanSleepTime;
99   }
100
101   /**
102    * Returns the cached version of the filter.  Allows the caller to pass up
103    * a small filter but this will keep a persistent version around and allow
104    * the caching filter to do its job.
105    * 
106    * @param filter The input filter
107    * @return The cached version of the filter
108    */
109   public Filter getFilter(Filter filter) {
110     synchronized(cache) {
111       FilterItem fi = null;
112       fi = cache.get(Integer.valueOf(filter.hashCode()));
113       if (fi != null) {
114         fi.timestamp = new Date().getTime();
115         return fi.filter;
116       }
117       cache.put(Integer.valueOf(filter.hashCode()), new FilterItem(filter));
118       return filter;
119     }
120   }
121
122   /**
123    * Holds the filter and the last time the filter was used, to make LRU-based
124    * cache cleaning possible.
125    * TODO: Clean this up when we switch to Java 1.5
126    */
127   protected class FilterItem {
128     public Filter filter;
129     public long   timestamp;
130
131     public FilterItem (Filter filter) {        
132       this.filter = filter;
133       this.timestamp = new Date().getTime();
134     }
135   }
136
137
138   /**
139    * Keeps the cache from getting too big.
140    * If we were using Java 1.5, we could use LinkedHashMap and we would not need this thread
141    * to clean out the cache.
142    * 
143    * The SortedSet sortedFilterItems is used only to sort the items from the cache,
144    * so when it's time to clean up we have the TreeSet sort the FilterItems by
145    * timestamp.
146    * 
147    * Removes 1.5 * the numbers of items to make the cache smaller.
148    * For example:
149    * If cache clean size is 10, and the cache is at 15, we would remove (15 - 10) * 1.5 = 7.5 round up to 8.
150    * This way we clean the cache a bit more, and avoid having the cache cleaner having to do it frequently.
151    */
152   protected class FilterCleaner implements Runnable  {
153
154     private boolean running = true;
155     private TreeSet<Map.Entry<Integer,FilterItem>> sortedFilterItems;
156
157     public FilterCleaner() {
158       sortedFilterItems = new TreeSet<Map.Entry<Integer,FilterItem>>(new Comparator<Map.Entry<Integer,FilterItem>>() {
159         public int compare(Map.Entry<Integer,FilterItem> a, Map.Entry<Integer,FilterItem> b) {
160             FilterItem fia = a.getValue();
161             FilterItem fib = b.getValue();
162             if ( fia.timestamp == fib.timestamp ) {
163               return 0;
164             }
165             // smaller timestamp first
166             if ( fia.timestamp < fib.timestamp ) {
167               return -1;
168             }
169             // larger timestamp last
170             return 1;
171           
172         }
173       });
174     }
175
176     public void run () {
177       while (running) {
178
179         // sort items from oldest to newest 
180         // we delete the oldest filters 
181         if (cache.size() > cacheCleanSize) {
182           // empty the temporary set
183           sortedFilterItems.clear();
184           synchronized (cache) {
185             sortedFilterItems.addAll(cache.entrySet());
186             Iterator<Map.Entry<Integer,FilterItem>> it = sortedFilterItems.iterator();
187             int numToDelete = (int) ((cache.size() - cacheCleanSize) * 1.5);
188             int counter = 0;
189             // loop over the set and delete all of the cache entries not used in a while
190             while (it.hasNext() && counter++ < numToDelete) {
191               Map.Entry<Integer,FilterItem> entry = it.next();
192               cache.remove(entry.getKey());
193             }
194           }
195           // empty the set so we don't tie up the memory
196           sortedFilterItems.clear();
197         }
198         // take a nap
199         try {
200           Thread.sleep(cleanSleepTime);
201         } catch (InterruptedException ie) {
202           throw new ThreadInterruptedException(ie);
203         }
204       }
205     }
206   }
207 }