package org.apache.blur.index; /** * 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.io.IOException; import java.util.Comparator; import java.util.Iterator; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.lucene.index.AtomicReader; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.DocsAndPositionsEnum; import org.apache.lucene.index.DocsEnum; import org.apache.lucene.index.Fields; import org.apache.lucene.index.FilterAtomicReader; import org.apache.lucene.index.FilterDirectoryReader; import org.apache.lucene.index.Terms; import org.apache.lucene.index.TermsEnum; import org.apache.lucene.util.Bits; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.automaton.CompiledAutomaton; /** * The {@link DirectoryReader} wraps a real index {@link DirectoryReader} and * allows for a {@link AtomicBoolean} to be checked periodically to see if the * thread should exit or not. The exit mechanism is by throw a * {@link ExitingReaderException} exception. */ public class ExitableReader extends FilterDirectoryReader { @SuppressWarnings("serial") public static class ExitingReaderException extends RuntimeException { } public static class ExitableSubReaderWrapper extends SubReaderWrapper { private final ExitObject _exitObject; public ExitableSubReaderWrapper(ExitObject exitObject) { _exitObject = exitObject; } @Override public AtomicReader wrap(AtomicReader reader) { return new ExitableFilterAtomicReader(reader, _exitObject); } } public static class ExitableFilterAtomicReader extends FilterAtomicReader { private final ExitObject _exitObject; public ExitableFilterAtomicReader(AtomicReader in, ExitObject exitObject) { super(in); _exitObject = exitObject; } public AtomicReader getOriginalReader() { return in; } @Override public Fields fields() throws IOException { Fields fields = super.fields(); if (fields == null) { return null; } return new ExitableFields(fields, _exitObject); } @Override public Object getCoreCacheKey() { return in.getCoreCacheKey(); } @Override public Object getCombinedCoreAndDeletesKey() { return in.getCombinedCoreAndDeletesKey(); } } public static class ExitableFields extends Fields { private final ExitObject _exitObject; private final Fields _fields; public ExitableFields(Fields fields, ExitObject exitObject) { _fields = fields; _exitObject = exitObject; } @Override public Terms terms(String field) throws IOException { Terms terms = _fields.terms(field); if (terms == null) { return null; } return new ExitableTerms(terms, _exitObject); } @Override public Iterator<String> iterator() { return _fields.iterator(); } @Override public int size() { return _fields.size(); } } public static class ExitableTerms extends Terms { private final ExitObject _exitObject; private final Terms _terms; public ExitableTerms(Terms terms, ExitObject exitObject) { _terms = terms; _exitObject = exitObject; } @Override public TermsEnum intersect(CompiledAutomaton compiled, BytesRef startTerm) throws IOException { return new ExitableTermsEnum(_terms.intersect(compiled, startTerm), _exitObject); } @Override public TermsEnum iterator(TermsEnum reuse) throws IOException { return new ExitableTermsEnum(_terms.iterator(reuse), _exitObject); } @Override public Comparator<BytesRef> getComparator() { return _terms.getComparator(); } @Override public long size() throws IOException { return _terms.size(); } @Override public long getSumTotalTermFreq() throws IOException { return _terms.getSumTotalTermFreq(); } @Override public long getSumDocFreq() throws IOException { return _terms.getSumDocFreq(); } @Override public int getDocCount() throws IOException { return _terms.getDocCount(); } @Override public boolean hasOffsets() { return _terms.hasOffsets(); } @Override public boolean hasPositions() { return _terms.hasPositions(); } @Override public boolean hasPayloads() { return _terms.hasPayloads(); } } public static class ExitableTermsEnum extends TermsEnum { private static final long CHECK_TIME = TimeUnit.SECONDS.toNanos(1); private final AtomicBoolean _running; private final TermsEnum _termsEnum; private int max = 1000; private long _lastCheck; private int count = 0; public ExitableTermsEnum(TermsEnum termsEnum, ExitObject exitObject) { _termsEnum = termsEnum; _running = exitObject.get(); _lastCheck = System.nanoTime(); checkAndThrow(); } private void checkRunningState() { count++; if (count >= max) { long now = System.nanoTime(); if (_lastCheck + CHECK_TIME < now) { checkAndThrow(); } else { // diff is the actual time between last check and now long diff = now - _lastCheck; // try to re-adjust max count int maxShouldBe = (int) (((float) count / (float) diff) * (float) CHECK_TIME); max = maxShouldBe; } count = 0; } } private void checkAndThrow() { if (!_running.get()) { throw new ExitingReaderException(); } } @Override public BytesRef next() throws IOException { checkRunningState(); return _termsEnum.next(); } @Override public Comparator<BytesRef> getComparator() { return _termsEnum.getComparator(); } @Override public SeekStatus seekCeil(BytesRef text, boolean useCache) throws IOException { return _termsEnum.seekCeil(text, useCache); } @Override public void seekExact(long ord) throws IOException { _termsEnum.seekExact(ord); } @Override public BytesRef term() throws IOException { return _termsEnum.term(); } @Override public long ord() throws IOException { return _termsEnum.ord(); } @Override public int docFreq() throws IOException { return _termsEnum.docFreq(); } @Override public long totalTermFreq() throws IOException { return _termsEnum.totalTermFreq(); } @Override public DocsEnum docs(Bits liveDocs, DocsEnum reuse, int flags) throws IOException { checkRunningState(); return _termsEnum.docs(liveDocs, reuse, flags); } @Override public DocsAndPositionsEnum docsAndPositions(Bits liveDocs, DocsAndPositionsEnum reuse, int flags) throws IOException { checkRunningState(); return _termsEnum.docsAndPositions(liveDocs, reuse, flags); } } private final ExitObject _exitObject; private final DirectoryReader _in; public ExitableReader(DirectoryReader in) { this(in, new ExitObject()); } public ExitableReader(DirectoryReader in, ExitObject exitObject) { super(in, new ExitableSubReaderWrapper(exitObject)); _exitObject = exitObject; _in = in; } public DirectoryReader getIn() { return _in; } public AtomicBoolean getRunning() { return _exitObject.get(); } public void setRunning(AtomicBoolean running) { _exitObject.set(running); } public void reset() { _exitObject.reset(); } @Override protected DirectoryReader doWrapDirectoryReader(DirectoryReader in) { return in; } @Override public String toString() { return "ExitableReader(" + in.toString() + ")"; } }