blob: ee1c0ceda2ec50fc9df86e9ad2ced56f05da67f2 [file] [log] [blame]
/*
* 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.index;
import org.apache.lucene.index.FilterLeafReader.FilterFields;
import org.apache.lucene.index.FilterLeafReader.FilterTerms;
import org.apache.lucene.index.FilterLeafReader.FilterTermsEnum;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.automaton.CompiledAutomaton;
import java.io.IOException;
/**
* The {@link ExitableDirectoryReader} wraps a real index {@link DirectoryReader} and
* allows for a {@link QueryTimeout} implementation object to be checked periodically
* to see if the thread should exit or not. If {@link QueryTimeout#shouldExit()}
* returns true, an {@link ExitingReaderException} is thrown.
*/
public class ExitableDirectoryReader extends FilterDirectoryReader {
private QueryTimeout queryTimeout;
/**
* Exception that is thrown to prematurely terminate a term enumeration.
*/
@SuppressWarnings("serial")
public static class ExitingReaderException extends RuntimeException {
/** Constructor **/
ExitingReaderException(String msg) {
super(msg);
}
}
/**
* Wrapper class for a SubReaderWrapper that is used by the ExitableDirectoryReader.
*/
public static class ExitableSubReaderWrapper extends SubReaderWrapper {
private QueryTimeout queryTimeout;
/** Constructor **/
public ExitableSubReaderWrapper(QueryTimeout queryTimeout) {
this.queryTimeout = queryTimeout;
}
@Override
public LeafReader wrap(LeafReader reader) {
return new ExitableFilterAtomicReader(reader, queryTimeout);
}
}
/**
* Wrapper class for another FilterAtomicReader. This is used by ExitableSubReaderWrapper.
*/
public static class ExitableFilterAtomicReader extends FilterLeafReader {
private QueryTimeout queryTimeout;
/** Constructor **/
public ExitableFilterAtomicReader(LeafReader in, QueryTimeout queryTimeout) {
super(in);
this.queryTimeout = queryTimeout;
}
@Override
public Fields fields() throws IOException {
Fields fields = super.fields();
if (queryTimeout.isTimeoutEnabled()) {
return new ExitableFields(fields, queryTimeout);
}
else {
return fields; // break out of wrapper as soon as possible
}
}
@Override
public Object getCoreCacheKey() {
return in.getCoreCacheKey();
}
@Override
public Object getCombinedCoreAndDeletesKey() {
return in.getCombinedCoreAndDeletesKey();
}
}
/**
* Wrapper class for another Fields implementation that is used by the ExitableFilterAtomicReader.
*/
public static class ExitableFields extends FilterFields {
private QueryTimeout queryTimeout;
/** Constructor **/
public ExitableFields(Fields fields, QueryTimeout queryTimeout) {
super(fields);
this.queryTimeout = queryTimeout;
}
@Override
public Terms terms(String field) throws IOException {
Terms terms = in.terms(field);
if (terms == null) {
return null;
}
return new ExitableTerms(terms, queryTimeout);
}
}
/**
* Wrapper class for another Terms implementation that is used by ExitableFields.
*/
public static class ExitableTerms extends FilterTerms {
private QueryTimeout queryTimeout;
/** Constructor **/
public ExitableTerms(Terms terms, QueryTimeout queryTimeout) {
super(terms);
this.queryTimeout = queryTimeout;
}
@Override
public TermsEnum intersect(CompiledAutomaton compiled, BytesRef startTerm) throws IOException {
return new ExitableTermsEnum(in.intersect(compiled, startTerm), queryTimeout);
}
@Override
public TermsEnum iterator() throws IOException {
return new ExitableTermsEnum(in.iterator(), queryTimeout);
}
}
/**
* Wrapper class for TermsEnum that is used by ExitableTerms for implementing an
* exitable enumeration of terms.
*/
public static class ExitableTermsEnum extends FilterTermsEnum {
private QueryTimeout queryTimeout;
/** Constructor **/
public ExitableTermsEnum(TermsEnum termsEnum, QueryTimeout queryTimeout) {
super(termsEnum);
this.queryTimeout = queryTimeout;
checkAndThrow();
}
/**
* Throws {@link ExitingReaderException} if {@link QueryTimeout#shouldExit()} returns true,
* or if {@link Thread#interrupted()} returns true.
*/
private void checkAndThrow() {
if (queryTimeout.shouldExit()) {
throw new ExitingReaderException("The request took too long to iterate over terms. Timeout: "
+ queryTimeout.toString()
+ ", TermsEnum=" + in
);
} else if (Thread.interrupted()) {
throw new ExitingReaderException("Interrupted while iterating over terms. TermsEnum=" + in);
}
}
@Override
public BytesRef next() throws IOException {
// Before every iteration, check if the iteration should exit
checkAndThrow();
return in.next();
}
}
/**
* Constructor
* @param in DirectoryReader that this ExitableDirectoryReader wraps around to make it Exitable.
* @param queryTimeout The object to periodically check if the query should time out.
*/
public ExitableDirectoryReader(DirectoryReader in, QueryTimeout queryTimeout) throws IOException {
super(in, new ExitableSubReaderWrapper(queryTimeout));
this.queryTimeout = queryTimeout;
}
@Override
protected DirectoryReader doWrapDirectoryReader(DirectoryReader in) throws IOException {
return new ExitableDirectoryReader(in, queryTimeout);
}
/**
* Wraps a provided DirectoryReader. Note that for convenience, the returned reader
* can be used normally (e.g. passed to {@link DirectoryReader#openIfChanged(DirectoryReader)})
* and so on.
*/
public static DirectoryReader wrap(DirectoryReader in, QueryTimeout queryTimeout) throws IOException {
return new ExitableDirectoryReader(in, queryTimeout);
}
@Override
public String toString() {
return "ExitableDirectoryReader(" + in.toString() + ")";
}
}