[hibernate-commits] Hibernate SVN: r14889 - in search/trunk/src: java/org/hibernate/search/engine and 4 other directories.
hibernate-commits at lists.jboss.org
hibernate-commits at lists.jboss.org
Tue Jul 8 06:28:25 EDT 2008
Author: hardy.ferentschik
Date: 2008-07-08 06:28:25 -0400 (Tue, 08 Jul 2008)
New Revision: 14889
Added:
search/trunk/src/java/org/hibernate/search/annotations/CachingWrapperFilter.java
search/trunk/src/java/org/hibernate/search/filter/CachingWrapperFilter.java
Modified:
search/trunk/src/java/org/hibernate/search/engine/FilterDef.java
search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
search/trunk/src/test/org/hibernate/search/test/filter/ExcludeAllFilterFactory.java
search/trunk/src/test/org/hibernate/search/test/filter/FilterTest.java
Log:
HSEARCH-174
- Implemented suggested solution. Hibernate Search's CachingWrapperFilter uses now soft references in order to cache filter bit sets.
Added: search/trunk/src/java/org/hibernate/search/annotations/CachingWrapperFilter.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/annotations/CachingWrapperFilter.java (rev 0)
+++ search/trunk/src/java/org/hibernate/search/annotations/CachingWrapperFilter.java 2008-07-08 10:28:25 UTC (rev 14889)
@@ -0,0 +1,26 @@
+// $Id:$
+package org.hibernate.search.annotations;
+
+/**
+ * Defines the strategy for using <code>CachingWrappingFilter</code>
+ *
+ * @author Hardy Ferentschik
+ * @see org.hibernate.search.filter.CachingWrapperFilter
+ */
+public enum CachingWrapperFilter {
+ /**
+ * Use a <code>CachingWrapperFilter<code> depending on the value of
+ * @see FullTextFilterDef#cache()
+ */
+ AUTOMATIC,
+
+ /**
+ * Wrap the filter around a <code>CachingWrappingFilter</code>.
+ */
+ YES,
+
+ /**
+ * Do not use a <code>CachingWrappingFilter</code>.
+ */
+ NO;
+}
\ No newline at end of file
Property changes on: search/trunk/src/java/org/hibernate/search/annotations/CachingWrapperFilter.java
___________________________________________________________________
Name: svn:eol-style
+ native
Modified: search/trunk/src/java/org/hibernate/search/engine/FilterDef.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/engine/FilterDef.java 2008-07-08 10:26:50 UTC (rev 14888)
+++ search/trunk/src/java/org/hibernate/search/engine/FilterDef.java 2008-07-08 10:28:25 UTC (rev 14889)
@@ -7,18 +7,32 @@
import java.util.Map;
import org.hibernate.search.SearchException;
+import org.hibernate.search.annotations.CachingWrapperFilter;
/**
+ * A wrapper class which encapsualtes all required information to create a defined filter.
+ *
* @author Emmanuel Bernard
*/
//TODO serialization
+ at SuppressWarnings("unchecked")
public class FilterDef {
private Class impl;
private Method factoryMethod;
private Method keyMethod;
private Map<String, Method> setters = new HashMap<String, Method>();
private boolean cache;
+ private CachingWrapperFilter useCachingWrapperFilter;
+ public CachingWrapperFilter getUseCachingWrapperFilter() {
+ return useCachingWrapperFilter;
+ }
+
+ public void setUseCachingWrapperFilter(
+ CachingWrapperFilter useCachingWrapperFilter) {
+ this.useCachingWrapperFilter = useCachingWrapperFilter;
+ }
+
public Class getImpl() {
return impl;
}
Added: search/trunk/src/java/org/hibernate/search/filter/CachingWrapperFilter.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/filter/CachingWrapperFilter.java (rev 0)
+++ search/trunk/src/java/org/hibernate/search/filter/CachingWrapperFilter.java 2008-07-08 10:28:25 UTC (rev 14889)
@@ -0,0 +1,73 @@
+package org.hibernate.search.filter;
+
+import java.io.IOException;
+import java.util.BitSet;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Filter;
+import org.hibernate.util.SoftLimitMRUCache;
+
+/**
+ * A slightly different version of Lucene's original <code>CachingWrapperFilter</code> which
+ * uses <code>SoftReferences</code> instead of <code>WeakReferences</code> in order to cache
+ * the filter <code>BitSet</code>.
+ *
+ * @author Hardy Ferentschik
+ * @see org.apache.lucene.search.CachingWrapperFilter
+ * @see <a href="http://opensource.atlassian.com/projects/hibernate/browse/HSEARCH-174">HSEARCH-174</a>
+ */
+ at SuppressWarnings("serial")
+public class CachingWrapperFilter extends Filter {
+
+ private static final int DEFAULT_SIZE = 128;
+
+ /**
+ * The cache using soft references in order to store the filter bit sets.
+ */
+ protected transient SoftLimitMRUCache cache;
+
+ protected Filter filter;
+
+ /**
+ * @param filter
+ * Filter to cache results of
+ */
+ public CachingWrapperFilter(Filter filter) {
+ this.filter = filter;
+ }
+
+ public BitSet bits(IndexReader reader) throws IOException {
+ if (cache == null) {
+ cache = new SoftLimitMRUCache(DEFAULT_SIZE);
+ }
+
+ synchronized (cache) { // check cache
+ BitSet cached = (BitSet) cache.get(reader);
+ if (cached != null) {
+ return cached;
+ }
+ }
+
+ final BitSet bits = filter.bits(reader);
+
+ synchronized (cache) { // update cache
+ cache.put(reader, bits);
+ }
+
+ return bits;
+ }
+
+ public String toString() {
+ return this.getClass().getName() + "(" + filter + ")";
+ }
+
+ public boolean equals(Object o) {
+ if (!(o instanceof CachingWrapperFilter))
+ return false;
+ return this.filter.equals(((CachingWrapperFilter) o).filter);
+ }
+
+ public int hashCode() {
+ return filter.hashCode() ^ 0x1117BF25;
+ }
+}
Property changes on: search/trunk/src/java/org/hibernate/search/filter/CachingWrapperFilter.java
___________________________________________________________________
Name: svn:keywords
+ Id
Name: svn:eol-style
+ native
Modified: search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2008-07-08 10:26:50 UTC (rev 14888)
+++ search/trunk/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2008-07-08 10:28:25 UTC (rev 14889)
@@ -173,6 +173,7 @@
FilterDef filterDef = new FilterDef();
filterDef.setImpl( defAnn.impl() );
filterDef.setCache( defAnn.cache() );
+ filterDef.setUseCachingWrapperFilter( defAnn.useCachingWrapperFilter() );
try {
filterDef.getImpl().newInstance();
}
Modified: search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2008-07-08 10:26:50 UTC (rev 14888)
+++ search/trunk/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2008-07-08 10:28:25 UTC (rev 14889)
@@ -1,6 +1,8 @@
//$Id$
package org.hibernate.search.query;
+import static org.hibernate.search.reader.ReaderProviderHelper.getIndexReaders;
+
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
@@ -38,19 +40,19 @@
import org.hibernate.search.FullTextFilter;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.SearchException;
+import org.hibernate.search.annotations.CachingWrapperFilter;
import org.hibernate.search.engine.DocumentBuilder;
import org.hibernate.search.engine.DocumentExtractor;
import org.hibernate.search.engine.EntityInfo;
import org.hibernate.search.engine.FilterDef;
import org.hibernate.search.engine.Loader;
+import org.hibernate.search.engine.MultiClassesQueryLoader;
import org.hibernate.search.engine.ProjectionLoader;
import org.hibernate.search.engine.QueryLoader;
import org.hibernate.search.engine.SearchFactoryImplementor;
-import org.hibernate.search.engine.MultiClassesQueryLoader;
import org.hibernate.search.filter.ChainedFilter;
import org.hibernate.search.filter.FilterKey;
import org.hibernate.search.reader.ReaderProvider;
-import static org.hibernate.search.reader.ReaderProviderHelper.getIndexReaders;
import org.hibernate.search.store.DirectoryProvider;
import org.hibernate.search.util.ContextHelper;
import org.hibernate.transform.ResultTransformer;
@@ -289,108 +291,167 @@
}
private void buildFilters() {
+ if ( filterDefinitions == null || filterDefinitions.size() == 0 ) {
+ return; // there is nothing to do if we don't have any filter definitions
+ }
+
+ ChainedFilter chainedFilter = new ChainedFilter();
+ for (FullTextFilterImpl fullTextFilter : filterDefinitions.values()) {
+ Filter filter = buildLuceneFilter(fullTextFilter);
+ chainedFilter.addFilter( filter );
+ }
+
+ if ( filter != null ) chainedFilter.addFilter( filter );
+ filter = chainedFilter;
+ }
+
+ /**
+ * Builds a Lucene filter using the given <code>FullTextFilter</code>.
+ *
+ * @param fullTextFilter the Hibernate specific <code>FullTextFilter</code> used to create the
+ * Lucene <code>Filter</code>.
+ * @return the Lucene filter mapped to the filter definition
+ */
+ private Filter buildLuceneFilter(FullTextFilterImpl fullTextFilter) {
+
SearchFactoryImplementor searchFactoryImplementor = getSearchFactoryImplementor();
- if ( filterDefinitions != null && filterDefinitions.size() > 0 ) {
- /*
- * FilterKey implementations and Filter(Factory) do not have to be threadsafe wrt their parameter injection
- * as FilterCachingStrategy ensure a memory barrier between concurrent thread calls
- */
- ChainedFilter chainedFilter = new ChainedFilter();
- for (FullTextFilterImpl filterDefinition : filterDefinitions.values()) {
- FilterDef def = searchFactoryImplementor.getFilterDefinition( filterDefinition.getName() );
- Class implClass = def.getImpl();
- Object instance;
- try {
- instance = implClass.newInstance();
- }
- catch (InstantiationException e) {
- throw new SearchException( "Unable to create @FullTextFilterDef: " + def.getImpl(), e );
- }
- catch (IllegalAccessException e) {
- throw new SearchException( "Unable to create @FullTextFilterDef: " + def.getImpl(), e );
- }
- for (Map.Entry<String, Object> entry : filterDefinition.getParameters().entrySet()) {
- def.invoke( entry.getKey(), instance, entry.getValue() );
- }
- if ( def.isCache() && def.getKeyMethod() == null && filterDefinition.getParameters().size() > 0 ) {
- throw new SearchException( "Filter with parameters and no @Key method: " + filterDefinition.getName() );
- }
+
+ /*
+ * FilterKey implementations and Filter(Factory) do not have to be threadsafe wrt their parameter injection
+ * as FilterCachingStrategy ensure a memory barrier between concurrent thread calls
+ */
+ FilterDef def = searchFactoryImplementor.getFilterDefinition( fullTextFilter.getName() );
+ Object instance = createFilterInstance(fullTextFilter, def);
+ FilterKey key = createFilterKey(def, instance);
- FilterKey key = null;
- if ( def.isCache() ) {
- if ( def.getKeyMethod() == null ) {
- key = new FilterKey() {
- public int hashCode() {
- return getImpl().hashCode();
- }
+ // try to get the filter out of the cache
+ Filter filter = def.isCache() ?
+ searchFactoryImplementor.getFilterCachingStrategy().getCachedFilter( key ) :
+ null;
+
+ if ( filter == null ) {
+ filter = createFilter(def, instance);
+
+ // add filter to cache if we have to
+ if ( def.isCache() ) {
+ searchFactoryImplementor.getFilterCachingStrategy().addCachedFilter( key, filter );
+ }
+ }
+ return filter;
+ }
- public boolean equals(Object obj) {
- if ( !( obj instanceof FilterKey ) ) return false;
- FilterKey that = (FilterKey) obj;
- return this.getImpl().equals( that.getImpl() );
- }
- };
- }
- else {
- try {
- key = (FilterKey) def.getKeyMethod().invoke( instance );
- }
- catch (IllegalAccessException e) {
- throw new SearchException( "Unable to access @Key method: "
- + def.getImpl().getName() + "." + def.getKeyMethod().getName() );
- }
- catch (InvocationTargetException e) {
- throw new SearchException( "Unable to access @Key method: "
- + def.getImpl().getName() + "." + def.getKeyMethod().getName() );
- }
- catch (ClassCastException e) {
- throw new SearchException( "@Key method does not return FilterKey: "
- + def.getImpl().getName() + "." + def.getKeyMethod().getName() );
- }
- }
- key.setImpl( def.getImpl() );
+ private Filter createFilter(FilterDef def, Object instance) {
+ Filter filter = null;
+ if ( def.getFactoryMethod() != null ) {
+ try {
+ filter = (Filter) def.getFactoryMethod().invoke( instance );
+ }
+ catch (IllegalAccessException e) {
+ throw new SearchException( "Unable to access @Factory method: "
+ + def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
+ }
+ catch (InvocationTargetException e) {
+ throw new SearchException( "Unable to access @Factory method: "
+ + def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
+ }
+ catch (ClassCastException e) {
+ throw new SearchException( "@Key method does not return a org.apache.lucene.search.Filter class: "
+ + def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
+ }
+ }
+ else {
+ try {
+ filter = (Filter) instance;
+ }
+ catch (ClassCastException e) {
+ throw new SearchException( "@Key method does not return a org.apache.lucene.search.Filter class: "
+ + def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
+ }
+ }
+
+ filter = addCachingWrapperFilter(filter, def);
+ return filter;
+ }
+
+ /**
+ * Decides whether to wrap the given filter around a <code>CachingWrapperFilter<code>.
+ *
+ * @param filter the filter which maybe gets wrapped.
+ * @param def The filter definition used to decide whether wrapping should occur or not.
+ * @return The original filter or wrapped filter depending on the information extracted from
+ * <code>def</code>.
+ */
+ private Filter addCachingWrapperFilter(Filter filter, FilterDef def) {
+ if (def.getUseCachingWrapperFilter() == CachingWrapperFilter.YES
+ || (def.getUseCachingWrapperFilter() == CachingWrapperFilter.AUTOMATIC && def
+ .isCache())) {
+ filter = new org.hibernate.search.filter.CachingWrapperFilter(filter);
+ }
+
+ return filter;
+ }
+
+ private FilterKey createFilterKey(FilterDef def, Object instance) {
+ FilterKey key = null;
+ if ( !def.isCache() ) {
+ return key; // if the filter is not cached there is no key!
+ }
+
+ if ( def.getKeyMethod() == null ) {
+ key = new FilterKey() {
+ public int hashCode() {
+ return getImpl().hashCode();
}
- Filter filter = def.isCache() ?
- searchFactoryImplementor.getFilterCachingStrategy().getCachedFilter( key ) :
- null;
- if ( filter == null ) {
- if ( def.getFactoryMethod() != null ) {
- try {
- filter = (Filter) def.getFactoryMethod().invoke( instance );
- }
- catch (IllegalAccessException e) {
- throw new SearchException( "Unable to access @Factory method: "
- + def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
- }
- catch (InvocationTargetException e) {
- throw new SearchException( "Unable to access @Factory method: "
- + def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
- }
- catch (ClassCastException e) {
- throw new SearchException( "@Key method does not return a org.apache.lucene.search.Filter class: "
- + def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
- }
- }
- else {
- try {
- filter = (Filter) instance;
- }
- catch (ClassCastException e) {
- throw new SearchException( "@Key method does not return a org.apache.lucene.search.Filter class: "
- + def.getImpl().getName() + "." + def.getFactoryMethod().getName() );
- }
- }
- if ( def.isCache() )
- searchFactoryImplementor.getFilterCachingStrategy().addCachedFilter( key, filter );
+ public boolean equals(Object obj) {
+ if ( !( obj instanceof FilterKey ) ) return false;
+ FilterKey that = (FilterKey) obj;
+ return this.getImpl().equals( that.getImpl() );
}
- chainedFilter.addFilter( filter );
+ };
+ }
+ else {
+ try {
+ key = (FilterKey) def.getKeyMethod().invoke( instance );
}
- if ( filter != null ) chainedFilter.addFilter( filter );
- filter = chainedFilter;
+ catch (IllegalAccessException e) {
+ throw new SearchException( "Unable to access @Key method: "
+ + def.getImpl().getName() + "." + def.getKeyMethod().getName() );
+ }
+ catch (InvocationTargetException e) {
+ throw new SearchException( "Unable to access @Key method: "
+ + def.getImpl().getName() + "." + def.getKeyMethod().getName() );
+ }
+ catch (ClassCastException e) {
+ throw new SearchException( "@Key method does not return FilterKey: "
+ + def.getImpl().getName() + "." + def.getKeyMethod().getName() );
+ }
}
+ key.setImpl( def.getImpl() );
+ return key;
}
+ private Object createFilterInstance(FullTextFilterImpl fullTextFilter,
+ FilterDef def) {
+ Object instance;
+ try {
+ instance = def.getImpl().newInstance();
+ }
+ catch (InstantiationException e) {
+ throw new SearchException( "Unable to create @FullTextFilterDef: " + def.getImpl(), e );
+ }
+ catch (IllegalAccessException e) {
+ throw new SearchException( "Unable to create @FullTextFilterDef: " + def.getImpl(), e );
+ }
+ for (Map.Entry<String, Object> entry : fullTextFilter.getParameters().entrySet()) {
+ def.invoke( entry.getKey(), instance, entry.getValue() );
+ }
+ if ( def.isCache() && def.getKeyMethod() == null && fullTextFilter.getParameters().size() > 0 ) {
+ throw new SearchException( "Filter with parameters and no @Key method: " + fullTextFilter.getName() );
+ }
+ return instance;
+ }
+
private org.apache.lucene.search.Query filterQueryByClasses(org.apache.lucene.search.Query luceneQuery) {
if ( ! needClassFilterClause ) {
return luceneQuery;
Modified: search/trunk/src/test/org/hibernate/search/test/filter/ExcludeAllFilterFactory.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/filter/ExcludeAllFilterFactory.java 2008-07-08 10:26:50 UTC (rev 14888)
+++ search/trunk/src/test/org/hibernate/search/test/filter/ExcludeAllFilterFactory.java 2008-07-08 10:28:25 UTC (rev 14889)
@@ -1,9 +1,9 @@
-//$
+// $Id:$
package org.hibernate.search.test.filter;
-import org.apache.lucene.search.CachingWrapperFilter;
import org.apache.lucene.search.Filter;
import org.hibernate.search.annotations.Factory;
+import org.hibernate.search.filter.CachingWrapperFilter;
/**
* @author Emmanuel Bernard
Property changes on: search/trunk/src/test/org/hibernate/search/test/filter/ExcludeAllFilterFactory.java
___________________________________________________________________
Name: svn:keywords
+ Id
Modified: search/trunk/src/test/org/hibernate/search/test/filter/FilterTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/filter/FilterTest.java 2008-07-08 10:26:50 UTC (rev 14888)
+++ search/trunk/src/test/org/hibernate/search/test/filter/FilterTest.java 2008-07-08 10:28:25 UTC (rev 14889)
@@ -1,21 +1,19 @@
-//$Id$
+// $Id:$
package org.hibernate.search.test.filter;
-import java.util.Date;
import java.util.Calendar;
-import org.hibernate.search.test.SearchTestCase;
-import org.hibernate.search.FullTextSession;
-import org.hibernate.search.Search;
-import org.hibernate.search.FullTextQuery;
-import org.hibernate.Session;
-import org.apache.lucene.search.Query;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.RangeFilter;
-import org.apache.lucene.index.Term;
+import org.apache.lucene.search.TermQuery;
+import org.hibernate.Session;
+import org.hibernate.search.FullTextQuery;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.search.test.SearchTestCase;
/**
* @author Emmanuel Bernard
@@ -69,6 +67,11 @@
ftQuery = s.createFullTextQuery( query, Driver.class );
ftQuery.enableFullTextFilter( "cachetest");
assertEquals("Should filter out all", 0, ftQuery.getResultSize() );
+
+ // HSEARCH-174 - we call System.gc() to force a garbage collection. Prior to the fix
+ // to HSEARCH-174 this would cause the filter to be garbage collected since Lucene
+ // used weak references in the cache.
+ System.gc();
ftQuery = s.createFullTextQuery( query, Driver.class );
ftQuery.enableFullTextFilter( "cachetest");
@@ -158,6 +161,7 @@
s.close();
}
+ @SuppressWarnings("unchecked")
protected Class[] getMappings() {
return new Class[] {
Driver.class,
Property changes on: search/trunk/src/test/org/hibernate/search/test/filter/FilterTest.java
___________________________________________________________________
Name: svn:keywords
+ Id
More information about the hibernate-commits
mailing list