blob: 37e17a6820f8645446226d608ddfa38fead4dca8 [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.blur.lucene.security.search;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentMap;
import org.apache.blur.lucene.security.index.SecureAtomicReader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.index.AtomicReader;
import org.apache.lucene.index.DocsEnum;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexReader.ReaderClosedListener;
import org.apache.lucene.index.SegmentReader;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.util.Bits;
import org.apache.lucene.util.BytesRef;
import org.apache.lucene.util.OpenBitSet;
import com.google.common.collect.MapMaker;
public class BitSetDocumentVisibilityFilterCacheStrategy extends DocumentVisibilityFilterCacheStrategy {
private static final Log LOG = LogFactory.getLog(BitSetDocumentVisibilityFilterCacheStrategy.class);
public static final DocumentVisibilityFilterCacheStrategy INSTANCE = new BitSetDocumentVisibilityFilterCacheStrategy();
private final ConcurrentMap<Key, DocIdSet> _cache;
public BitSetDocumentVisibilityFilterCacheStrategy() {
_cache = new MapMaker().makeMap();
}
@Override
public DocIdSet getDocIdSet(String fieldName, BytesRef term, AtomicReader reader) {
Key key = new Key(fieldName, term, reader.getCoreCacheKey());
DocIdSet docIdSet = _cache.get(key);
if (docIdSet != null) {
LOG.debug("Cache hit for key [" + key + "]");
} else {
LOG.debug("Cache miss for key [" + key + "]");
}
return docIdSet;
}
@Override
public Builder createBuilder(String fieldName, BytesRef term, final AtomicReader reader) {
int maxDoc = reader.maxDoc();
final Key key = new Key(fieldName, term, reader.getCoreCacheKey());
LOG.debug("Creating new bitset for key [" + key + "] on index [" + reader + "]");
return new Builder() {
private OpenBitSet bitSet = new OpenBitSet(maxDoc);
@Override
public void or(DocIdSetIterator it) throws IOException {
LOG.debug("Building bitset for key [" + key + "]");
int doc;
while ((doc = it.nextDoc()) != DocsEnum.NO_MORE_DOCS) {
bitSet.set(doc);
}
}
@Override
public DocIdSet getDocIdSet() throws IOException {
SegmentReader segmentReader = getSegmentReader(reader);
segmentReader.addReaderClosedListener(new ReaderClosedListener() {
@Override
public void onClose(IndexReader reader) {
LOG.debug("Removing old bitset for key [" + key + "]");
DocIdSet docIdSet = _cache.remove(key);
if (docIdSet == null) {
LOG.warn("DocIdSet was missing for key [" + docIdSet + "]");
}
}
});
long cardinality = bitSet.cardinality();
DocIdSet cacheDocIdSet;
if (isFullySet(maxDoc, bitSet, cardinality)) {
cacheDocIdSet = getFullySetDocIdSet(maxDoc);
} else if (isFullyEmpty(bitSet, cardinality)) {
cacheDocIdSet = getFullyEmptyDocIdSet(maxDoc);
} else {
cacheDocIdSet = bitSet;
}
_cache.put(key, cacheDocIdSet);
return cacheDocIdSet;
}
};
}
public static DocIdSet getFullyEmptyDocIdSet(int maxDoc) {
Bits bits = getFullyEmptyBits(maxDoc);
return new DocIdSet() {
@Override
public DocIdSetIterator iterator() throws IOException {
return getFullyEmptyDocIdSetIterator(maxDoc);
}
@Override
public Bits bits() throws IOException {
return bits;
}
@Override
public boolean isCacheable() {
return true;
}
};
}
public static DocIdSetIterator getFullyEmptyDocIdSetIterator(int maxDoc) {
return new DocIdSetIterator() {
private int _docId = -1;
@Override
public int docID() {
return _docId;
}
@Override
public int nextDoc() throws IOException {
return _docId = DocIdSetIterator.NO_MORE_DOCS;
}
@Override
public int advance(int target) throws IOException {
return _docId = DocIdSetIterator.NO_MORE_DOCS;
}
@Override
public long cost() {
return 0;
}
};
}
public static Bits getFullyEmptyBits(int maxDoc) {
return new Bits() {
@Override
public boolean get(int index) {
return false;
}
@Override
public int length() {
return maxDoc;
}
};
}
public static DocIdSet getFullySetDocIdSet(int maxDoc) {
Bits bits = getFullySetBits(maxDoc);
return new DocIdSet() {
@Override
public DocIdSetIterator iterator() throws IOException {
return getFullySetDocIdSetIterator(maxDoc);
}
@Override
public Bits bits() throws IOException {
return bits;
}
@Override
public boolean isCacheable() {
return true;
}
};
}
public static DocIdSetIterator getFullySetDocIdSetIterator(int maxDoc) {
return new DocIdSetIterator() {
private int _docId = -1;
@Override
public int advance(int target) throws IOException {
if (_docId == DocIdSetIterator.NO_MORE_DOCS) {
return DocIdSetIterator.NO_MORE_DOCS;
}
_docId = target;
if (_docId >= maxDoc) {
return _docId = DocIdSetIterator.NO_MORE_DOCS;
}
return _docId;
}
@Override
public int nextDoc() throws IOException {
if (_docId == DocIdSetIterator.NO_MORE_DOCS) {
return DocIdSetIterator.NO_MORE_DOCS;
}
_docId++;
if (_docId >= maxDoc) {
return _docId = DocIdSetIterator.NO_MORE_DOCS;
}
return _docId;
}
@Override
public int docID() {
return _docId;
}
@Override
public long cost() {
return 0l;
}
};
}
public static Bits getFullySetBits(int maxDoc) {
return new Bits() {
@Override
public boolean get(int index) {
return true;
}
@Override
public int length() {
return maxDoc;
}
};
}
public static boolean isFullyEmpty(OpenBitSet bitSet, long cardinality) {
if (cardinality == 0) {
return true;
}
return false;
}
public static boolean isFullySet(int maxDoc, OpenBitSet bitSet, long cardinality) {
if (cardinality >= maxDoc) {
return true;
}
return false;
}
public static SegmentReader getSegmentReader(IndexReader indexReader) throws IOException {
if (indexReader instanceof SegmentReader) {
return (SegmentReader) indexReader;
} else if (indexReader instanceof SecureAtomicReader) {
SecureAtomicReader atomicReader = (SecureAtomicReader) indexReader;
AtomicReader originalReader = atomicReader.getOriginalReader();
return getSegmentReader(originalReader);
} else {
try {
Method method = indexReader.getClass().getDeclaredMethod("getOriginalReader", new Class[] {});
return getSegmentReader((IndexReader) method.invoke(indexReader, new Object[] {}));
} catch (NoSuchMethodException e) {
LOG.error("IndexReader cannot find method [getOriginalReader]");
} catch (SecurityException e) {
throw new IOException(e);
} catch (IllegalAccessException e) {
throw new IOException(e);
} catch (IllegalArgumentException e) {
throw new IOException(e);
} catch (InvocationTargetException e) {
throw new IOException(e);
}
}
throw new IOException("SegmentReader could not be found [" + indexReader + "].");
}
private static class Key {
private final Object _object;
private final BytesRef _term;
private final String _fieldName;
public Key(String fieldName, BytesRef term, Object object) {
_fieldName = fieldName;
_term = BytesRef.deepCopyOf(term);
_object = object;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((_fieldName == null) ? 0 : _fieldName.hashCode());
result = prime * result + ((_object == null) ? 0 : _object.hashCode());
result = prime * result + ((_term == null) ? 0 : _term.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Key other = (Key) obj;
if (_fieldName == null) {
if (other._fieldName != null)
return false;
} else if (!_fieldName.equals(other._fieldName))
return false;
if (_object == null) {
if (other._object != null)
return false;
} else if (!_object.equals(other._object))
return false;
if (_term == null) {
if (other._term != null)
return false;
} else if (!_term.equals(other._term))
return false;
return true;
}
@Override
public String toString() {
return "Key [_object=" + _object + ", _fieldName=" + _fieldName + ", _term=" + _term + "]";
}
}
@Override
public String toString() {
return "BitSetDocumentVisibilityFilterCacheStrategy [_cache=" + _cache + "]";
}
}