--- /dev/null
+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);
+ }
+ }
+ }
+ }
+}