Index: src/java/org/apache/lucene/analysis/Analyzer.java =================================================================== --- src/java/org/apache/lucene/analysis/Analyzer.java (revision 830934) +++ src/java/org/apache/lucene/analysis/Analyzer.java (working copy) @@ -17,15 +17,13 @@ * limitations under the License. */ -import java.io.Reader; -import java.io.IOException; import java.io.Closeable; +import java.io.IOException; +import java.io.Reader; import java.lang.reflect.Method; -import org.apache.lucene.util.CloseableThreadLocal; -import org.apache.lucene.store.AlreadyClosedException; - import org.apache.lucene.document.Fieldable; +import org.apache.lucene.store.AlreadyClosedException; /** An Analyzer builds TokenStreams, which analyze text. It thus represents a * policy for extracting index terms from text. @@ -52,7 +50,7 @@ return tokenStream(fieldName, reader); } - private CloseableThreadLocal tokenStreams = new CloseableThreadLocal(); + private ThreadLocal tokenStreams = new ThreadLocal(); /** Used by Analyzers that implement reusableTokenStream * to retrieve previously saved TokenStreams for re-use @@ -85,11 +83,13 @@ } /** @deprecated */ + @Deprecated protected boolean overridesTokenStreamMethod = false; /** @deprecated This is only present to preserve * back-compat of classes that subclass a core analyzer * and override tokenStream but not reusableTokenStream */ + @Deprecated protected void setOverridesTokenStreamMethod(Class baseClass) { try { Method m = this.getClass().getMethod("tokenStream", String.class, Reader.class); @@ -138,7 +138,7 @@ /** Frees persistent resources used by this Analyzer */ public void close() { - tokenStreams.close(); + tokenStreams.remove(); tokenStreams = null; } } Index: src/java/org/apache/lucene/index/FieldsReader.java =================================================================== --- src/java/org/apache/lucene/index/FieldsReader.java (revision 830934) +++ src/java/org/apache/lucene/index/FieldsReader.java (working copy) @@ -17,6 +17,10 @@ * limitations under the License. */ +import java.io.IOException; +import java.io.Reader; +import java.util.zip.DataFormatException; + import org.apache.lucene.analysis.TokenStream; import org.apache.lucene.document.AbstractField; import org.apache.lucene.document.CompressionTools; @@ -25,15 +29,10 @@ import org.apache.lucene.document.FieldSelector; import org.apache.lucene.document.FieldSelectorResult; import org.apache.lucene.document.Fieldable; -import org.apache.lucene.store.Directory; -import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.AlreadyClosedException; import org.apache.lucene.store.BufferedIndexInput; -import org.apache.lucene.util.CloseableThreadLocal; - -import java.io.IOException; -import java.io.Reader; -import java.util.zip.DataFormatException; +import org.apache.lucene.store.Directory; +import org.apache.lucene.store.IndexInput; /** * Class responsible for access to stored document fields. @@ -62,7 +61,7 @@ // file. This will be 0 if we have our own private file. private int docStoreOffset; - private CloseableThreadLocal fieldsStreamTL = new CloseableThreadLocal(); + private ThreadLocal fieldsStreamTL = new ThreadLocal(); private boolean isOriginal = false; /** Returns a cloned FieldsReader that shares open @@ -192,7 +191,7 @@ if (indexStream != null) { indexStream.close(); } - fieldsStreamTL.close(); + fieldsStreamTL.remove(); closed = true; } } @@ -412,6 +411,7 @@ private int toRead; private long pointer; /** @deprecated Only kept for backward-compatbility with <3.0 indexes. Will be removed in 4.0. */ + @Deprecated private boolean isCompressed; public LazyField(String name, Field.Store store, int toRead, long pointer, boolean isBinary, boolean isCompressed) { Index: src/java/org/apache/lucene/index/SegmentReader.java =================================================================== --- src/java/org/apache/lucene/index/SegmentReader.java (revision 830934) +++ src/java/org/apache/lucene/index/SegmentReader.java (working copy) @@ -23,7 +23,6 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; - import java.util.List; import java.util.Map; import java.util.Set; @@ -36,7 +35,6 @@ import org.apache.lucene.store.IndexInput; import org.apache.lucene.store.IndexOutput; import org.apache.lucene.util.BitVector; -import org.apache.lucene.util.CloseableThreadLocal; /** @version $Id */ /** @@ -49,8 +47,8 @@ private SegmentInfo si; private int readBufferSize; - CloseableThreadLocal fieldsReaderLocal = new FieldsReaderLocal(); - CloseableThreadLocal termVectorsLocal = new CloseableThreadLocal(); + ThreadLocal fieldsReaderLocal = new FieldsReaderLocal(); + ThreadLocal termVectorsLocal = new ThreadLocal(); BitVector deletedDocs = null; Ref deletedDocsRef = null; @@ -292,7 +290,7 @@ /** * Sets the initial value */ - private class FieldsReaderLocal extends CloseableThreadLocal { + private class FieldsReaderLocal extends ThreadLocal { @Override protected FieldsReader initialValue() { return (FieldsReader) core.getFieldsReaderOrig().clone(); @@ -780,8 +778,8 @@ @Override protected void doClose() throws IOException { - termVectorsLocal.close(); - fieldsReaderLocal.close(); + termVectorsLocal.remove(); + fieldsReaderLocal.remove(); if (deletedDocs != null) { deletedDocsRef.decRef(); @@ -1275,6 +1273,7 @@ * We do it with R/W access for the tests (BW compatibility) * @deprecated Remove this when tests are fixed! */ + @Deprecated static SegmentReader getOnlySegmentReader(Directory dir) throws IOException { return getOnlySegmentReader(IndexReader.open(dir,false)); } Index: src/java/org/apache/lucene/index/TermInfosReader.java =================================================================== --- src/java/org/apache/lucene/index/TermInfosReader.java (revision 830934) +++ src/java/org/apache/lucene/index/TermInfosReader.java (working copy) @@ -22,7 +22,6 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.util.cache.Cache; import org.apache.lucene.util.cache.SimpleLRUCache; -import org.apache.lucene.util.CloseableThreadLocal; /** This stores a monotonically increasing set of pairs in a * Directory. Pairs are accessed either by Term or by ordinal position the @@ -33,7 +32,7 @@ private final String segment; private final FieldInfos fieldInfos; - private final CloseableThreadLocal threadResources = new CloseableThreadLocal(); + private final ThreadLocal threadResources = new ThreadLocal(); private final SegmentTermEnum origEnum; private final long size; @@ -129,7 +128,7 @@ final void close() throws IOException { if (origEnum != null) origEnum.close(); - threadResources.close(); + threadResources.remove(); } /** Returns the number of term/value pairs in the set. */ Index: src/java/org/apache/lucene/util/CloseableThreadLocal.java =================================================================== --- src/java/org/apache/lucene/util/CloseableThreadLocal.java (revision 830934) +++ src/java/org/apache/lucene/util/CloseableThreadLocal.java (working copy) @@ -1,93 +0,0 @@ -package org.apache.lucene.util; - -/** - * 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.Map; -import java.util.HashMap; -import java.util.Iterator; -import java.lang.ref.WeakReference; -import java.io.Closeable; - -/** Java's builtin ThreadLocal has a serious flaw: - * it can take an arbitrarily long amount of time to - * dereference the things you had stored in it, even once the - * ThreadLocal instance itself is no longer referenced. - * This is because there is single, master map stored for - * each thread, which all ThreadLocals share, and that - * master map only periodically purges "stale" entries. - * - * While not technically a memory leak, because eventually - * the memory will be reclaimed, it can take a long time - * and you can easily hit OutOfMemoryError because from the - * GC's standpoint the stale entries are not reclaimable. - * - * This class works around that, by only enrolling - * WeakReference values into the ThreadLocal, and - * separately holding a hard reference to each stored - * value. When you call {@link #close}, these hard - * references are cleared and then GC is freely able to - * reclaim space by objects stored in it. */ - -public class CloseableThreadLocal implements Closeable { - - private ThreadLocal> t = new ThreadLocal>(); - - private Map hardRefs = new HashMap(); - - protected T initialValue() { - return null; - } - - public T get() { - WeakReference weakRef = t.get(); - if (weakRef == null) { - T iv = initialValue(); - if (iv != null) { - set(iv); - return iv; - } else - return null; - } else { - return weakRef.get(); - } - } - - public void set(T object) { - - t.set(new WeakReference(object)); - - synchronized(hardRefs) { - hardRefs.put(Thread.currentThread(), object); - - // Purge dead threads - for (Iterator it = hardRefs.keySet().iterator(); it.hasNext();) { - final Thread t = it.next(); - if (!t.isAlive()) - it.remove(); - } - } - } - - public void close() { - // Clear the hard refs; then, the only remaining refs to - // all values we were storing are weak (unless somewhere - // else is still using them) and so GC may reclaim them: - hardRefs = null; - t = null; - } -} Index: src/test/org/apache/lucene/util/TestCloseableThreadLocal.java =================================================================== --- src/test/org/apache/lucene/util/TestCloseableThreadLocal.java (revision 830934) +++ src/test/org/apache/lucene/util/TestCloseableThreadLocal.java (working copy) @@ -1,50 +0,0 @@ -/** - * 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. - */ - -package org.apache.lucene.util; - -public class TestCloseableThreadLocal extends LuceneTestCase { - public static final String TEST_VALUE = "initvaluetest"; - - public void testInitValue() { - InitValueThreadLocal tl = new InitValueThreadLocal(); - String str = (String)tl.get(); - assertEquals(TEST_VALUE, str); - } - - public void testNullValue() throws Exception { - // Tests that null can be set as a valid value (LUCENE-1805). This - // previously failed in get(). - CloseableThreadLocal ctl = new CloseableThreadLocal(); - ctl.set(null); - assertNull(ctl.get()); - } - - public void testDefaultValueWithoutSetting() throws Exception { - // LUCENE-1805: make sure default get returns null, - // twice in a row - CloseableThreadLocal ctl = new CloseableThreadLocal(); - assertNull(ctl.get()); - assertNull(ctl.get()); - } - - public class InitValueThreadLocal extends CloseableThreadLocal { - protected Object initialValue() { - return TEST_VALUE; - } - } -}