pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / search / FilterManager.java
diff --git a/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/search/FilterManager.java b/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/search/FilterManager.java
new file mode 100644 (file)
index 0000000..516a091
--- /dev/null
@@ -0,0 +1,207 @@
+package org.apache.lucene.search;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeSet;
+
+import org.apache.lucene.util.ThreadInterruptedException;
+
+/**
+ * Filter caching singleton. It can be used to save filters locally for reuse.
+ * This class makes it possible to cache Filters even when using RMI, as it
+ * keeps the cache on the searcher side of the RMI connection.
+ * 
+ * Also could be used as a persistent storage for any filter as long as the
+ * filter provides a proper hashCode(), as that is used as the key in the cache.
+ * 
+ * The cache is periodically cleaned up from a separate thread to ensure the
+ * cache doesn't exceed the maximum size.
+ * 
+ * @deprecated used by remote package which is deprecated as well. You should
+ *             use {@link CachingWrapperFilter} if you wish to cache
+ *             {@link Filter}s.
+ */
+@Deprecated
+public class FilterManager {
+
+  protected static FilterManager manager;
+  
+  /** The default maximum number of Filters in the cache */
+  protected static final int  DEFAULT_CACHE_CLEAN_SIZE = 100;
+  /** The default frequency of cache cleanup */
+  protected static final long DEFAULT_CACHE_SLEEP_TIME = 1000 * 60 * 10;
+
+  /** The cache itself */
+  protected Map<Integer,FilterItem>           cache;
+  /** Maximum allowed cache size */
+  protected int           cacheCleanSize;
+  /** Cache cleaning frequency */
+  protected long          cleanSleepTime;
+  /** Cache cleaner that runs in a separate thread */
+  protected FilterCleaner filterCleaner;
+
+  public synchronized static FilterManager getInstance() {
+    if (manager == null) {
+      manager = new FilterManager();
+    }
+    return manager;
+  }
+
+  /**
+   * Sets up the FilterManager singleton.
+   */
+  protected FilterManager() {
+    cache            = new HashMap<Integer,FilterItem>();
+    cacheCleanSize   = DEFAULT_CACHE_CLEAN_SIZE; // Let the cache get to 100 items
+    cleanSleepTime   = DEFAULT_CACHE_SLEEP_TIME; // 10 minutes between cleanings
+
+    filterCleaner   = new FilterCleaner();
+    Thread fcThread = new Thread(filterCleaner);
+    // set to be a Daemon so it doesn't have to be stopped
+    fcThread.setDaemon(true);
+    fcThread.start();
+  }
+  
+  /**
+   * Sets the max size that cache should reach before it is cleaned up
+   * @param cacheCleanSize maximum allowed cache size
+   */
+  public void setCacheSize(int cacheCleanSize) {
+    this.cacheCleanSize = cacheCleanSize;
+  }
+
+  /**
+   * Sets the cache cleaning frequency in milliseconds.
+   * @param cleanSleepTime cleaning frequency in milliseconds
+   */
+  public void setCleanThreadSleepTime(long cleanSleepTime) {
+    this.cleanSleepTime  = cleanSleepTime;
+  }
+
+  /**
+   * Returns the cached version of the filter.  Allows the caller to pass up
+   * a small filter but this will keep a persistent version around and allow
+   * the caching filter to do its job.
+   * 
+   * @param filter The input filter
+   * @return The cached version of the filter
+   */
+  public Filter getFilter(Filter filter) {
+    synchronized(cache) {
+      FilterItem fi = null;
+      fi = cache.get(Integer.valueOf(filter.hashCode()));
+      if (fi != null) {
+        fi.timestamp = new Date().getTime();
+        return fi.filter;
+      }
+      cache.put(Integer.valueOf(filter.hashCode()), new FilterItem(filter));
+      return filter;
+    }
+  }
+
+  /**
+   * Holds the filter and the last time the filter was used, to make LRU-based
+   * cache cleaning possible.
+   * TODO: Clean this up when we switch to Java 1.5
+   */
+  protected class FilterItem {
+    public Filter filter;
+    public long   timestamp;
+
+    public FilterItem (Filter filter) {        
+      this.filter = filter;
+      this.timestamp = new Date().getTime();
+    }
+  }
+
+
+  /**
+   * Keeps the cache from getting too big.
+   * If we were using Java 1.5, we could use LinkedHashMap and we would not need this thread
+   * to clean out the cache.
+   * 
+   * The SortedSet sortedFilterItems is used only to sort the items from the cache,
+   * so when it's time to clean up we have the TreeSet sort the FilterItems by
+   * timestamp.
+   * 
+   * Removes 1.5 * the numbers of items to make the cache smaller.
+   * For example:
+   * 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.
+   * This way we clean the cache a bit more, and avoid having the cache cleaner having to do it frequently.
+   */
+  protected class FilterCleaner implements Runnable  {
+
+    private boolean running = true;
+    private TreeSet<Map.Entry<Integer,FilterItem>> sortedFilterItems;
+
+    public FilterCleaner() {
+      sortedFilterItems = new TreeSet<Map.Entry<Integer,FilterItem>>(new Comparator<Map.Entry<Integer,FilterItem>>() {
+        public int compare(Map.Entry<Integer,FilterItem> a, Map.Entry<Integer,FilterItem> b) {
+            FilterItem fia = a.getValue();
+            FilterItem fib = b.getValue();
+            if ( fia.timestamp == fib.timestamp ) {
+              return 0;
+            }
+            // smaller timestamp first
+            if ( fia.timestamp < fib.timestamp ) {
+              return -1;
+            }
+            // larger timestamp last
+            return 1;
+          
+        }
+      });
+    }
+
+    public void run () {
+      while (running) {
+
+        // sort items from oldest to newest 
+        // we delete the oldest filters 
+        if (cache.size() > cacheCleanSize) {
+          // empty the temporary set
+          sortedFilterItems.clear();
+          synchronized (cache) {
+            sortedFilterItems.addAll(cache.entrySet());
+            Iterator<Map.Entry<Integer,FilterItem>> it = sortedFilterItems.iterator();
+            int numToDelete = (int) ((cache.size() - cacheCleanSize) * 1.5);
+            int counter = 0;
+            // loop over the set and delete all of the cache entries not used in a while
+            while (it.hasNext() && counter++ < numToDelete) {
+              Map.Entry<Integer,FilterItem> entry = it.next();
+              cache.remove(entry.getKey());
+            }
+          }
+          // empty the set so we don't tie up the memory
+          sortedFilterItems.clear();
+        }
+        // take a nap
+        try {
+          Thread.sleep(cleanSleepTime);
+        } catch (InterruptedException ie) {
+          throw new ThreadInterruptedException(ie);
+        }
+      }
+    }
+  }
+}