Author: navssurtani
Date: 2008-06-30 09:56:28 -0400 (Mon, 30 Jun 2008)
New Revision: 14827
Added:
search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/configuration/ConfigurationParseHelper.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/MultiClassesQueryLoader.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/ObjectLoaderHelper.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/reader/SharingBufferReaderProvider.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/SerializationTestHelper.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/configuration/ConfigurationParseHelperTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/directoryProvider/DirectoryProviderHelperTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/embedded/State.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/query/MultiClassesQueryLoaderTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/functionality/
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/functionality/FilterOnDirectoryTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/functionality/SharingBufferIndexProviderTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/functionality/TestableSharingBufferReaderProvider.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/performance/
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/performance/AbstractActivity.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/performance/BufferSharingReaderPerfTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/performance/IndexFillRunnable.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/performance/InsertActivity.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/performance/NotSharedReaderPerfTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/performance/ReaderPerformance.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/performance/SearchActivity.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/performance/SharedReaderPerfTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/performance/UpdateActivity.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/session/Domain.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/session/MassIndexUsingManualFlushTest.java
Modified:
search/branches/jboss_cache_integration/build.xml
search/branches/jboss_cache_integration/doc/reference/en/modules/configuration.xml
search/branches/jboss_cache_integration/doc/reference/en/modules/mapping.xml
search/branches/jboss_cache_integration/src/java/org/hibernate/search/FullTextSession.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/LuceneIndexingParameters.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/Workspace.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/configuration/IndexWriterSetting.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/configuration/MaskedProperty.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/impl/BatchedQueueingProcessor.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/impl/PostTransactionWorkQueueSynchronization.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/bridge/BridgeFactory.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/DocumentBuilder.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/ObjectLoader.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/ProjectionLoader.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/QueryLoader.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/SearchFactoryImplementor.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/filter/MRUFilterCachingStrategy.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/jpa/FullTextEntityManager.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/jpa/impl/FullTextEntityManagerImpl.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/query/FullTextQueryImpl.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/reader/ReaderProviderHelper.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/reader/SharedReaderProvider.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/DirectoryProviderFactory.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/DirectoryProviderHelper.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/FSDirectoryProvider.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/FSMasterDirectoryProvider.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/FSSlaveDirectoryProvider.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/optimization/IncrementalOptimizerStrategy.java
search/branches/jboss_cache_integration/src/java/org/hibernate/search/util/FileHelper.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/FSDirectoryTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/analyzer/AnalyzerTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/configuration/LuceneIndexingParametersTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/configuration/MaskedPropertiesTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/directoryProvider/FSSlaveAndMasterDPTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/embedded/Author.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/embedded/Country.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/embedded/Person.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/embedded/Tower.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/embedded/doubleinsert/DoubleInsertEmbeddedTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/optimizer/OptimizerTestCase.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/perf/IndexTestDontRun.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/perf/SearcherThread.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/query/Employee.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/query/LuceneQuerySortTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/query/ProjectionQueryTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/NotSharedReaderPerfTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/ReaderPerfTestCase.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/reader/SharedReaderPerfTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/session/Email.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/session/OptimizeTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/session/SessionTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/shards/ShardsTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/worker/ConcurrencyTest.java
search/branches/jboss_cache_integration/src/test/org/hibernate/search/test/worker/WorkerTestCase.java
Log:
Updated from trunk
Modified: search/branches/jboss_cache_integration/build.xml
===================================================================
--- search/branches/jboss_cache_integration/build.xml 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/build.xml 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
<property name="version" value="3.1.0-SNAPSHOT"/>
<property name="javadoc.packagenames" value="org.hibernate.search.*"/>
<property name="copy.test" value="true"/>
+ <property name="copy.test" value="true"/>
<property name="javac.source" value="1.5"/>
<property name="javac.target" value="1.5"/>
<property name="jdbc.dir" value="jdbc"/>
Modified: search/branches/jboss_cache_integration/doc/reference/en/modules/configuration.xml
===================================================================
--- search/branches/jboss_cache_integration/doc/reference/en/modules/configuration.xml 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/doc/reference/en/modules/configuration.xml 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
<entry>
org.hibernate.search.store.FSDirectoryProvider</entry>
<entry>File system based directory. The directory used will be
- <indexBase>/< <literal>@(protected)>
+ <indexBase>/< <literal>@(protected)>
></entry>
<entry><para><literal>indexBase</literal> : Base
directory</para><para><literal>indexName</literal>: override
- @Index.name (useful for sharded indexes)</para></entry>
+ @Indexed.index (useful for sharded indexes)</para></entry>
</row>
<row>
@@(protected) @@
3600 seconds - 60 minutes).</para><para>Note that the copy is
based on an incremental copy mechanism reducing the average copy
time.</para><para>DirectoryProvider typically used on the master
- node in a JMS back end cluster.</para>DirectoryProvider typically
- used on slave nodes using a JMS back end.</entry>
+ node in a JMS back end cluster.</para><para>The <literal>
+ buffer_size_on_copy</literal> optimum depends
+ on your operating system and available RAM; most people reported
+ good results using values between 16 and 64MB.</para></entry>
<entry><para><literal>indexBase</literal>: Base
directory</para><para><literal>indexName</literal>: override
- @Index.name (useful for sharded
+ @Indexed.index (useful for sharded
indexes)</para><para><literal>sourceBase</literal>: Source (copy)
base directory.</para><para><literal>source</literal>: Source
- directory suffix (default to <literal>@(protected)>).
+ directory suffix (default to <literal>@(protected)>).
The actual source directory name being
<filename><sourceBase>/<source></filename>
- </para><para>refresh: refresh period in second (the copy will take
- place every refresh seconds).</para></entry>
+ </para><para><literal>refresh</literal>: refresh period in second
+ (the copy will take place every refresh seconds).</para><para>
+ <literal>buffer_size_on_copy</literal>: The amount of
+ MegaBytes to move in a single low level copy instruction;
+ defaults to 16MB.</para></entry>
</row>
<row>
@@(protected) @@
information (default 3600 seconds - 60 minutes).</para><para>Note
that the copy is based on an incremental copy mechanism reducing
the average copy time.</para><para>DirectoryProvider typically
- used on slave nodes using a JMS back end.</para></entry>
+ used on slave nodes using a JMS back end.</para><para>The <literal>
+ buffer_size_on_copy</literal> optimum depends
+ on your operating system and available RAM; most people reported
+ good results using values between 16 and 64MB.</para></entry>
<entry><para><literal>indexBase</literal>: Base
directory</para><para><literal>indexName</literal>: override
- @Index.name (useful for sharded
+ @Indexed.index (useful for sharded
indexes)</para><para><literal>sourceBase</literal>: Source (copy)
base directory.</para><para><literal>source</literal>: Source
- directory suffix (default to <literal>@(protected)>).
+ directory suffix (default to <literal>@(protected)>).
The actual source directory name being
<filename><sourceBase>/<source></filename>
- </para><para>refresh: refresh period in second (the copy will take
- place every refresh seconds).</para></entry>
+ </para><para><literal>refresh</literal>: refresh period in second
+ (the copy will take place every refresh seconds).</para><para>
+ <literal>buffer_size_on_copy</literal>: The amount of
+ MegaBytes to move in a single low level copy instruction;
+ defaults to 16MB.</para></entry>
</row>
<row>
@@(protected) @@
<entry>Memory based directory, the directory will be uniquely
identified (in the same deployment unit) by the
- <literal>@(protected)>
+ <literal>@(protected)>
<entry>none</entry>
</row>
@@(protected) @@
<para>If the built-in directory providers does not fit your needs, you can
write your own directory provider by implementing the
<classname>org.hibernate.store.DirectoryProvider</classname>
- interface</para>
+ interface.</para>
<para>Each indexed entity is associated to a Lucene index (an index can be
shared by several entities but this is not usually the case). You can
@@(protected) @@
<programlisting>hibernate.search.default.directory_provider
org.hibernate.search.store.FSDirectoryProvider hibernate.search.default.indexBase=/usr/lucene/indexes
+hibernate.search.Rules.directory_provider
org.hibernate.search.store.RAMDirectoryProvider</programlisting>
-hibernate.search.Rules.directory_provider
org.hibernate.search.store.RAMDirectoryProvider </programlisting>
-
<para>applied on</para>
- <programlisting>@(protected)")
+ <programlisting>@(protected)")
public class Status { ... }
-@(protected)")
+@(protected)")
public class Rule { ... }</programlisting>
<para>will create a file system directory in
@@(protected) @@
<literal>Rules</literal> where Rule entities will be indexed.</para>
<para>You can easily define common rules like the directory provider and
- base directory, and overide those default later on on a per index
+ base directory, and override those default later on on a per index
basis.</para>
<para>Writing your own <classname>DirectoryProvider</classname>, you can
@@(protected) @@
several Lucene indexes. This solution is not recommended until you reach
significant index sizes and index update time are slowing down. The main
drawback of index sharding is that searches will end up being slower since
- more files have to be opend for a single search. In other words don't do
+ more files have to be opened for a single search. In other words don't do
it until you have problems :)</para>
<para>Despite this strong warning, Hibernate Search allows you to index a
@@(protected) @@
<programlisting>hibernate.search.reader.strategy = my.corp.myapp.CustomReaderProvider</programlisting>
<para>where <classname>my.corp.myapp.CustomReaderProvider</classname> is
- the custom strategy implementation</para>
+ the custom strategy implementation.</para>
</section>
<section id="search-configuration-event" revision="2">
Modified: search/branches/jboss_cache_integration/doc/reference/en/modules/mapping.xml
===================================================================
--- search/branches/jboss_cache_integration/doc/reference/en/modules/mapping.xml 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/doc/reference/en/modules/mapping.xml 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
...
}</programlisting>
- <para>Any <literal>@(protected)
+ <para>Any <literal>@(protected)
<literal>@(protected)
<literal>@(protected)
class will then be added to the main entity index. In the previous
@@(protected) @@
</section>
</section>
</section>
-</chapter>
\ No newline at end of file
+</chapter>
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/FullTextSession.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/FullTextSession.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/FullTextSession.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
* Non indexable entities are ignored
*
* @param entity The entity to index - must not be <code>null</code>.
+ * @throws IllegalArgumentException if entity is null or not an @Indexed entity
*/
void index(Object entity);
@@(protected) @@
*
* @param entityType
* @param id
+ *
+ * @throws IllegalArgumentException if entityType is null or not an @Indexed entity type
*/
public void purge(Class entityType, Serializable id);
@@(protected) @@
* Remove all entities from a particular class of an index.
*
* @param entityType
+ * @throws IllegalArgumentException if entityType is null or not an @Indexed entity type
*/
public void purgeAll(Class entityType);
+
+ /**
+ * flush full text changes to the index
+ * Force Hibernate Search to apply all changes to the index no waiting for the batch limit
+ */
+ public void flushToIndexes();
}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/LuceneIndexingParameters.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/LuceneIndexingParameters.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/LuceneIndexingParameters.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
package org.hibernate.search.backend;
import
java.io.Serializable;
-import
java.util.HashMap;
+import
java.util.EnumMap;
import
java.util.Map;
import
java.util.Properties;
@@(protected) @@
private static final long serialVersionUID = 5424606407623591663L;
- private final Logger log = LoggerFactory.getLogger( LuceneIndexingParameters.class );
-
// value keyword
public static final String EXPLICIT_DEFAULT_VALUE = "default";
// property path keywords
@@(protected) @@
Properties transactionProps = new MaskedProperty( indexingParameters, TRANSACTION );
//get keys for "batch" (defaulting to transaction)
Properties batchProps = new MaskedProperty( indexingParameters, BATCH, transactionProps ); //TODO to close HSEARCH-201 just remove 3° parameter
- transactionIndexParameters = new ParameterSet( transactionProps, TRANSACTION );
- batchIndexParameters = new ParameterSet( batchProps, BATCH );
- doSanityChecks( transactionIndexParameters, batchIndexParameters );
+ //logger only used during object construction: (logger not serializable).
+ Logger log = LoggerFactory.getLogger( LuceneIndexingParameters.class );
+ transactionIndexParameters = new ParameterSet( transactionProps, TRANSACTION, log );
+ batchIndexParameters = new ParameterSet( batchProps, BATCH, log );
+ doSanityChecks( transactionIndexParameters, batchIndexParameters, log );
}
- private void doSanityChecks(ParameterSet transParams, ParameterSet batchParams) {
+ private void doSanityChecks(ParameterSet transParams, ParameterSet batchParams, Logger log) {
if ( log.isWarnEnabled() ) {
Integer maxFieldLengthTransaction = transParams.parameters.get( MAX_FIELD_LENGTH );
Integer maxFieldLengthBatch = transParams.parameters.get( MAX_FIELD_LENGTH );
@@(protected) @@
return batchIndexParameters;
}
- public class ParameterSet implements Serializable {
+ public static class ParameterSet implements Serializable {
private static final long serialVersionUID = -6121723702279869524L;
- final Map<IndexWriterSetting, Integer> parameters = new HashMap<IndexWriterSetting, Integer>();
+ final Map<IndexWriterSetting, Integer> parameters = new EnumMap<IndexWriterSetting, Integer>(IndexWriterSetting.class);
- public ParameterSet(Properties prop, String paramName) {
+ public ParameterSet(Properties prop, String paramName, Logger log) {
//don't iterate on property entries as we know all the keys:
for ( IndexWriterSetting t : IndexWriterSetting.values() ) {
String key = t.getKey();
@@(protected) @@
}
}
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result
+ + ((parameters == null) ? 0 : parameters.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;
+ final ParameterSet other = (ParameterSet) obj;
+ if (parameters == null) {
+ if (other.parameters != null)
+ return false;
+ } else if (!parameters.equals(other.parameters))
+ return false;
+ return true;
+ }
+
}
public void applyToWriter(IndexWriter writer, boolean batch) {
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/Workspace.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/Workspace.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/Workspace.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
import
java.io.IOException;
import
java.util.HashMap;
import
java.util.Map;
-import
java.util.concurrent.locks.ReentrantLock;
+import
java.util.concurrent.locks.Lock;
import
org.apache.lucene.analysis.Analyzer;
import
org.apache.lucene.analysis.SimpleAnalyzer;
@@(protected) @@
private class DPWorkspace {
private final DirectoryProvider directoryProvider;
- private final ReentrantLock lock;
+ private final Lock lock;
private IndexReader reader;
private IndexWriter writer;
@@(protected) @@
DPWorkspace(DirectoryProvider dp) {
this.directoryProvider = dp;
- this.lock = searchFactoryImplementor.getLockableDirectoryProviders().get( dp );
+ this.lock = searchFactoryImplementor.getDirectoryProviderLock( dp );
}
public boolean needsOptimization() {
Added: search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/configuration/ConfigurationParseHelper.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/configuration/ConfigurationParseHelper.java (rev 0)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/configuration/ConfigurationParseHelper.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
+package org.hibernate.search.backend.configuration;
+
+import
java.util.Properties;
+
+import
org.hibernate.annotations.common.util.StringHelper;
+import
org.hibernate.search.SearchException;
+
+/**
+ * Helper class to avoid managing NumberFormatException and similar code
+ * and ensure consistent error messages across Configuration parsing problems.
+ *
+ * @author Sanne Grinovero
+ */
+public abstract class ConfigurationParseHelper {
+
+ /**
+ * Parses a String to get an int value.
+ * @param value A string containing an int value to parse
+ * @param errorMsgOnParseFailure message being wrapped in a SearchException if value is null or not correct.
+ * @return the parsed value
+ * @throws SearchException both for null values and for Strings not containing a valid int.
+ */
+ public static final int parseInt(String value, String errorMsgOnParseFailure) {
+ if ( value == null ) {
+ throw new SearchException( errorMsgOnParseFailure );
+ }
+ else {
+ try {
+ return Integer.parseInt( value.trim() );
+ } catch (NumberFormatException nfe) {
+ throw new SearchException( errorMsgOnParseFailure, nfe );
+ }
+ }
+ }
+
+ /**
+ * In case value is null or an empty string the defValue is returned
+ * @param value
+ * @param defValue
+ * @param errorMsgOnParseFailure
+ * @return the converted int.
+ * @throws SearchException if value can't be parsed.
+ */
+ public static final int parseInt(String value, int defValue, String errorMsgOnParseFailure) {
+ if ( StringHelper.isEmpty( value ) ) {
+ return defValue;
+ }
+ else {
+ return parseInt( value, errorMsgOnParseFailure );
+ }
+ }
+
+ /**
+ * Looks for a numeric value in the Properties, returning
+ * defValue if not found or if an empty string is found.
+ * When the key the value is found but not in valid format
+ * a standard error message is generated.
+ * @param cfg
+ * @param key
+ * @param defValue
+ * @return the converted int.
+ * @throws SearchException for invalid format.
+ */
+ public static final int getIntValue(Properties cfg, String key, int defValue) {
+ String propValue = cfg.getProperty( key );
+ return parseInt( propValue, defValue, "Unable to parse " + key + ": " + propValue );
+ }
+
+}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/configuration/IndexWriterSetting.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/configuration/IndexWriterSetting.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/configuration/IndexWriterSetting.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
* @throws SearchException for unrecognized values
*/
public Integer parseVal(String value) {
- try {
- return Integer.valueOf( value );
- } catch (NumberFormatException ne) {
- throw new SearchException( "Invalid value for " + cfgKey + ": " + value );
- }
+ return ConfigurationParseHelper.parseInt( value,
+ "Invalid value for " + cfgKey + ": " + value );
}
}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/configuration/MaskedProperty.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/configuration/MaskedProperty.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/configuration/MaskedProperty.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
import
java.io.IOException;
import
java.io.InputStream;
+import
java.io.ObjectInputStream;
+import
java.io.ObjectOutputStream;
import
java.io.OutputStream;
import
java.io.PrintStream;
import
java.io.PrintWriter;
@@(protected) @@
private static final long serialVersionUID = -593307257383085113L;
- private final Logger log = LoggerFactory.getLogger( MaskedProperty.class );
+ private transient Logger log = LoggerFactory.getLogger( MaskedProperty.class );
private final Properties masked;
private final Properties fallBack;
private final String radix;
@@(protected) @@
return true;
}
+ private void readObject(ObjectInputStream aInputStream) throws ClassNotFoundException, IOException {
+ //always perform the default de-serialization first
+ aInputStream.defaultReadObject();
+ log = LoggerFactory.getLogger( MaskedProperty.class );
+ }
+
+ private void writeObject(ObjectOutputStream aOutputStream) throws IOException {
+ aOutputStream.defaultWriteObject();
+ }
+
}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/impl/BatchedQueueingProcessor.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/impl/BatchedQueueingProcessor.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/impl/BatchedQueueingProcessor.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
import
org.hibernate.search.backend.Work;
import
org.hibernate.search.backend.WorkType;
import
org.hibernate.search.backend.WorkQueue;
+import
org.hibernate.search.backend.configuration.ConfigurationParseHelper;
import
org.hibernate.search.backend.impl.jms.JMSBackendQueueProcessorFactory;
import
org.hibernate.search.backend.impl.lucene.LuceneBackendQueueProcessorFactory;
import
org.hibernate.search.engine.DocumentBuilder;
@@(protected) @@
private static final Logger log = LoggerFactory.getLogger( BatchedQueueingProcessor.class );
- private boolean sync;
- private int batchSize;
- private ExecutorService executorService;
- private BackendQueueProcessorFactory backendQueueProcessorFactory;
- private SearchFactoryImplementor searchFactoryImplementor;
+ private final boolean sync;
+ private final int batchSize;
+ private final ExecutorService executorService;
+ private final BackendQueueProcessorFactory backendQueueProcessorFactory;
+ private final SearchFactoryImplementor searchFactoryImplementor;
- public BatchedQueueingProcessor(SearchFactoryImplementor searchFactoryImplementor,
- Properties properties) {
+ public BatchedQueueingProcessor(SearchFactoryImplementor searchFactoryImplementor, Properties properties) {
this.searchFactoryImplementor = searchFactoryImplementor;
//default to sync if none defined
this.sync = !"async".equalsIgnoreCase( properties.getProperty( Environment.WORKER_EXECUTION ) );
//default to a simple asynchronous operation
- int min = Integer.parseInt(
- properties.getProperty( Environment.WORKER_THREADPOOL_SIZE, "1" ).trim()
- );
+ int min = ConfigurationParseHelper.getIntValue( properties, Environment.WORKER_THREADPOOL_SIZE, 1 );
//no queue limit
- int queueSize = Integer.parseInt(
- properties.getProperty( Environment.WORKER_WORKQUEUE_SIZE, Integer.toString( Integer.MAX_VALUE ) ).trim()
- );
- batchSize = Integer.parseInt(
- properties.getProperty( Environment.WORKER_BATCHSIZE, "0" ).trim()
- );
+ int queueSize = ConfigurationParseHelper.getIntValue( properties, Environment.WORKER_WORKQUEUE_SIZE, Integer.MAX_VALUE );
+
+ batchSize = ConfigurationParseHelper.getIntValue( properties, Environment.WORKER_BATCHSIZE, 0 );
+
if ( !sync ) {
/**
* choose min = max with a sizable queue to be able to
@@(protected) @@
new ThreadPoolExecutor.CallerRunsPolicy()
);
}
+ else {
+ executorService = null;
+ }
String backend = properties.getProperty( Environment.WORKER_BACKEND );
if ( StringHelper.isEmpty( backend ) || "lucene".equalsIgnoreCase( backend ) ) {
backendQueueProcessorFactory = new LuceneBackendQueueProcessorFactory();
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/impl/PostTransactionWorkQueueSynchronization.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/impl/PostTransactionWorkQueueSynchronization.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/backend/impl/PostTransactionWorkQueueSynchronization.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
if (queuePerTransaction != null) queuePerTransaction.removeValue( this );
}
}
+
+ public void flushWorks() {
+ WorkQueue subQueue = queue.splitQueue();
+ queueingProcessor.prepareWorks( subQueue );
+ queueingProcessor.performWorks( subQueue );
+ }
}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/bridge/BridgeFactory.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/bridge/BridgeFactory.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/bridge/BridgeFactory.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
public static final TwoWayFieldBridge Uri = new TwoWayString2FieldBridgeAdaptor( new UriBridge() );
- public static final FieldBridge DATE_YEAR = new String2FieldBridgeAdaptor( DateBridge.DATE_YEAR );
- public static final FieldBridge DATE_MONTH = new String2FieldBridgeAdaptor( DateBridge.DATE_MONTH );
- public static final FieldBridge DATE_DAY = new String2FieldBridgeAdaptor( DateBridge.DATE_DAY );
- public static final FieldBridge DATE_HOUR = new String2FieldBridgeAdaptor( DateBridge.DATE_HOUR );
- public static final FieldBridge DATE_MINUTE = new String2FieldBridgeAdaptor( DateBridge.DATE_MINUTE );
- public static final FieldBridge DATE_SECOND = new String2FieldBridgeAdaptor( DateBridge.DATE_SECOND );
+ public static final FieldBridge DATE_YEAR = new TwoWayString2FieldBridgeAdaptor( DateBridge.DATE_YEAR );
+ public static final FieldBridge DATE_MONTH = new TwoWayString2FieldBridgeAdaptor( DateBridge.DATE_MONTH );
+ public static final FieldBridge DATE_DAY = new TwoWayString2FieldBridgeAdaptor( DateBridge.DATE_DAY );
+ public static final FieldBridge DATE_HOUR = new TwoWayString2FieldBridgeAdaptor( DateBridge.DATE_HOUR );
+ public static final FieldBridge DATE_MINUTE = new TwoWayString2FieldBridgeAdaptor( DateBridge.DATE_MINUTE );
+ public static final FieldBridge DATE_SECOND = new TwoWayString2FieldBridgeAdaptor( DateBridge.DATE_SECOND );
public static final TwoWayFieldBridge DATE_MILLISECOND =
new TwoWayString2FieldBridgeAdaptor( DateBridge.DATE_MILLISECOND );
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/DocumentBuilder.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/DocumentBuilder.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/DocumentBuilder.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
import
org.hibernate.search.bridge.BridgeFactory;
import
org.hibernate.search.bridge.FieldBridge;
import
org.hibernate.search.bridge.TwoWayFieldBridge;
+import
org.hibernate.search.bridge.TwoWayString2FieldBridgeAdaptor;
import
org.hibernate.search.store.DirectoryProvider;
import
org.hibernate.search.store.IndexShardingStrategy;
import
org.hibernate.search.util.BinderHelper;
@@(protected) @@
private int maxLevel = Integer.MAX_VALUE;
private final ScopedAnalyzer analyzer = new ScopedAnalyzer();
private Similarity similarity;
+ private boolean isRoot;
+ //if composite id, use of (a, b) in ((1,2), (3,4)) fails on most database
+ private boolean safeFromTupleId;
+ public boolean isRoot() {
+ return isRoot;
+ }
public DocumentBuilder(XClass clazz, InitContext context, DirectoryProvider[] directoryProviders,
IndexShardingStrategy shardingStrategy, ReflectionManager reflectionManager) {
@@(protected) @@
if ( idKeywordName == null ) {
throw new SearchException( "No document id in: " + clazz.getName() );
}
+ //if composite id, use of (a, b) in ((1,2)TwoWayString2FieldBridgeAdaptor, (3,4)) fails on most database
+ //a TwoWayString2FieldBridgeAdaptor is never a composite id
+ safeFromTupleId = TwoWayString2FieldBridgeAdaptor.class.isAssignableFrom( idBridge.getClass() );
}
private Analyzer getAnalyzer(XAnnotatedElement annotatedElement, InitContext context) {
@@(protected) @@
for (Class currentClass : indexedClasses) {
if ( plainClass.isAssignableFrom( currentClass ) ) tempMappedSubclasses.add( currentClass );
}
- mappedSubclasses = Collections.unmodifiableSet( tempMappedSubclasses );
+ this.mappedSubclasses = Collections.unmodifiableSet( tempMappedSubclasses );
+ Class superClass = plainClass.getSuperclass();
+ this.isRoot = true;
+ while ( superClass != null) {
+ if ( indexedClasses.contains( superClass ) ) {
+ this.isRoot = false;
+ break;
+ }
+ superClass = superClass.getSuperclass();
+ }
}
@@(protected) @@
return mappedSubclasses;
}
+ /**
+ * Make sure to return false if there is a risk of composite id
+ * if composite id, use of (a, b) in ((1,2), (3,4)) fails on most database
+ */
+ public boolean isSafeFromTupleId() {
+ return safeFromTupleId;
+ }
+
private static class PropertiesMetadata {
public Float boost;
public Analyzer analyzer;
Added: search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/MultiClassesQueryLoader.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/MultiClassesQueryLoader.java (rev 0)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/MultiClassesQueryLoader.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
+package org.hibernate.search.engine;
+
+import
java.util.List;
+import
java.util.ArrayList;
+import
java.util.Collections;
+import
java.util.Map;
+import
java.util.Set;
+import
java.util.HashMap;
+import
java.util.Arrays;
+
+import
org.hibernate.Session;
+import
org.hibernate.Criteria;
+import
org.hibernate.annotations.common.AssertionFailure;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class MultiClassesQueryLoader implements Loader {
+ private Session session;
+ private SearchFactoryImplementor searchFactoryImplementor;
+ private List<RootEntityMetadata> entityMatadata;
+ //useful if loading with a query is unsafe
+ private ObjectLoader objectLoader;
+
+ public void init(Session session, SearchFactoryImplementor searchFactoryImplementor) {
+ this.session = session;
+ this.searchFactoryImplementor = searchFactoryImplementor;
+ this.objectLoader = new ObjectLoader();
+ this.objectLoader.init( session, searchFactoryImplementor );
+ }
+
+ public void setEntityTypes(Class[] entityTypes) {
+ List<Class> safeEntityTypes;
+ //TODO should we go find the root entity for a given class rather than just checking for it's root status?
+ // root entity could lead to quite inefficient queries in Hibnernate when using table per class
+ if ( entityTypes.length == 0 ) {
+ //support all classes
+ safeEntityTypes = new ArrayList<Class>();
+ for( Map.Entry<Class, DocumentBuilder<Object>> entry : searchFactoryImplementor.getDocumentBuilders().entrySet() ) {
+ //get only root entities to limit queries
+ if ( entry.getValue().isRoot() ) {
+ safeEntityTypes.add( entry.getKey() );
+ }
+ }
+ }
+ else {
+ safeEntityTypes = Arrays.asList(entityTypes);
+ }
+ entityMatadata = new ArrayList<RootEntityMetadata>( safeEntityTypes.size() );
+ for (Class clazz : safeEntityTypes) {
+ entityMatadata.add( new RootEntityMetadata( clazz, searchFactoryImplementor, session ) );
+ }
+ }
+
+ public Object load(EntityInfo entityInfo) {
+ return ObjectLoaderHelper.load( entityInfo, session );
+ }
+
+ public List load(EntityInfo... entityInfos) {
+ if ( entityInfos.length == 0 ) return Collections.EMPTY_LIST;
+ if ( entityInfos.length == 1 ) {
+ final Object entity = load( entityInfos[0] );
+ if ( entity == null ) {
+ return Collections.EMPTY_LIST;
+ }
+ else {
+ final List<Object> list = new ArrayList<Object>( 1 );
+ list.add( entity );
+ return list;
+ }
+ }
+
+ //split EntityInfo per root entity
+ Map<RootEntityMetadata, List<EntityInfo>> entityinfoBuckets =
+ new HashMap<RootEntityMetadata, List<EntityInfo>>( entityMatadata.size());
+ for (EntityInfo entityInfo : entityInfos) {
+ boolean found = false;
+ for (RootEntityMetadata rootEntityInfo : entityMatadata) {
+ if ( rootEntityInfo.mappedSubclasses.contains( entityInfo.clazz ) ) {
+ List<EntityInfo> bucket = entityinfoBuckets.get( rootEntityInfo );
+ if ( bucket == null ) {
+ bucket = new ArrayList<EntityInfo>();
+ entityinfoBuckets.put( rootEntityInfo, bucket );
+ }
+ bucket.add( entityInfo );
+ found = true;
+ break; //we stop looping for the right bucket
+ }
+ }
+ if (!found) throw new AssertionFailure( "Could not find root entity for " + entityInfo.clazz );
+ }
+
+ //initialize objects by bucket
+ for ( Map.Entry<RootEntityMetadata, List<EntityInfo>> entry : entityinfoBuckets.entrySet() ) {
+ final RootEntityMetadata key = entry.getKey();
+ final List<EntityInfo> value = entry.getValue();
+ final EntityInfo[] bucketEntityInfos = value.toArray( new EntityInfo[value.size()] );
+ if ( key.useObjectLoader ) {
+ objectLoader.load( bucketEntityInfos );
+ }
+ else {
+ ObjectLoaderHelper.initializeObjects( bucketEntityInfos,
+ key.criteria, key.rootEntity, searchFactoryImplementor);
+ }
+ }
+ return ObjectLoaderHelper.returnAlreadyLoadedObjectsInCorrectOrder( entityInfos, session );
+ }
+
+ private static class RootEntityMetadata {
+ public final Class rootEntity;
+ public final Set<Class> mappedSubclasses;
+ private final Criteria criteria;
+ public final boolean useObjectLoader;
+
+ RootEntityMetadata(Class rootEntity, SearchFactoryImplementor searchFactoryImplementor, Session session) {
+ this.rootEntity = rootEntity;
+ DocumentBuilder provider = searchFactoryImplementor.getDocumentBuilders().get( rootEntity );
+ if ( provider == null) throw new AssertionFailure("Provider not found for class: " + rootEntity);
+ this.mappedSubclasses = provider.getMappedSubclasses();
+ this.criteria = session.createCriteria( rootEntity );
+ this.useObjectLoader = !provider.isSafeFromTupleId();
+ }
+ }
+}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/ObjectLoader.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/ObjectLoader.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/ObjectLoader.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
import
java.util.ArrayList;
import
java.util.List;
+import
java.util.Collections;
import
org.hibernate.Hibernate;
import
org.hibernate.Session;
@@(protected) @@
}
public Object load(EntityInfo entityInfo) {
- //be sure to get an initialized object
- Object maybeProxy = session.get( entityInfo.clazz, entityInfo.id );
- try {
- Hibernate.initialize( maybeProxy );
- }
- catch (RuntimeException e) {
- if ( LoaderHelper.isObjectNotFoundException( e ) ) {
- log.debug( "Object found in Search index but not in database: {} with id {}",
- entityInfo.clazz, entityInfo.id );
- maybeProxy = null;
+ return ObjectLoaderHelper.load( entityInfo, session );
+ }
+
+ public List load(EntityInfo... entityInfos) {
+ if ( entityInfos.length == 0 ) return Collections.EMPTY_LIST;
+ if ( entityInfos.length == 1 ) {
+ final Object entity = load( entityInfos[0] );
+ if ( entity == null ) {
+ return Collections.EMPTY_LIST;
}
else {
- throw e;
+ final List<Object> list = new ArrayList<Object>( 1 );
+ list.add( entity );
+ return list;
}
}
- return maybeProxy;
- }
-
- public List load(EntityInfo... entityInfos) {
//use load to benefit from the batch-size
//we don't face proxy casting issues since the exact class is extracted from the index
for (EntityInfo entityInfo : entityInfos) {
Added: search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/ObjectLoaderHelper.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/ObjectLoaderHelper.java (rev 0)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/ObjectLoaderHelper.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
+package org.hibernate.search.engine;
+
+import
java.util.Collections;
+import
java.util.List;
+import
java.util.ArrayList;
+import
java.io.Serializable;
+
+import
org.hibernate.Hibernate;
+import
org.hibernate.Session;
+import
org.hibernate.Criteria;
+import
org.hibernate.type.EntityType;
+import
org.hibernate.criterion.Disjunction;
+import
org.hibernate.criterion.Restrictions;
+import
org.hibernate.annotations.common.AssertionFailure;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class ObjectLoaderHelper {
+
+ private static final int MAX_IN_CLAUSE = 500;
+ private static final Logger log = LoggerFactory.getLogger( ObjectLoader.class );
+
+ public static Object load(EntityInfo entityInfo, Session session) {
+ //be sure to get an initialized object but save from ONFE and ENFE
+ Object maybeProxy = session.load( entityInfo.clazz, entityInfo.id );
+ try {
+ Hibernate.initialize( maybeProxy );
+ }
+ catch (RuntimeException e) {
+ if ( LoaderHelper.isObjectNotFoundException( e ) ) {
+ log.debug( "Object found in Search index but not in database: {} with id {}",
+ entityInfo.clazz, entityInfo.id );
+ maybeProxy = null;
+ }
+ else {
+ throw e;
+ }
+ }
+ return maybeProxy;
+ }
+
+ public static void initializeObjects(EntityInfo[] entityInfos, Criteria criteria, Class entityType,
+ SearchFactoryImplementor searchFactoryImplementor) {
+ final int maxResults = entityInfos.length;
+ if ( maxResults == 0 ) return;
+
+ DocumentBuilder builder = searchFactoryImplementor.getDocumentBuilders().get( entityType );
+ String idName = builder.getIdentifierName();
+ int loop = maxResults / MAX_IN_CLAUSE;
+ boolean exact = maxResults % MAX_IN_CLAUSE == 0;
+ if ( !exact ) loop++;
+ Disjunction disjunction = Restrictions.disjunction();
+ for (int index = 0; index < loop; index++) {
+ int max = index * MAX_IN_CLAUSE + MAX_IN_CLAUSE <= maxResults ?
+ index * MAX_IN_CLAUSE + MAX_IN_CLAUSE :
+ maxResults;
+ List<Serializable> ids = new ArrayList<Serializable>( max - index * MAX_IN_CLAUSE );
+ for (int entityInfoIndex = index * MAX_IN_CLAUSE; entityInfoIndex < max; entityInfoIndex++) {
+ ids.add( entityInfos[entityInfoIndex].id );
+ }
+ disjunction.add( Restrictions.in( idName, ids ) );
+ }
+ criteria.add( disjunction );
+ criteria.list(); //load all objects
+ }
+
+
+ public static List returnAlreadyLoadedObjectsInCorrectOrder(EntityInfo[] entityInfos, Session session) {
+ //mandatory to keep the same ordering
+ List result = new ArrayList( entityInfos.length );
+ for (EntityInfo entityInfo : entityInfos) {
+ Object element = session.load( entityInfo.clazz, entityInfo.id );
+ if ( Hibernate.isInitialized( element ) ) {
+ //all existing elements should have been loaded by the query,
+ //the other ones are missing ones
+ result.add( element );
+ }
+ else {
+ if ( log.isDebugEnabled() ) {
+ log.debug( "Object found in Search index but not in database: {} with {}",
+ entityInfo.clazz, entityInfo.id );
+ }
+ }
+ }
+ return result;
+ }
+}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/ProjectionLoader.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/ProjectionLoader.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/ProjectionLoader.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
/**
* @author Emmanuel Bernard
*/
+//TODO change the underlying ObjectLoader to a MutliClassesQueryLoader
public class ProjectionLoader implements Loader {
private SearchFactoryImplementor searchFactoryImplementor;
private Session session;
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/QueryLoader.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/QueryLoader.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/QueryLoader.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
package org.hibernate.search.engine;
import
java.util.ArrayList;
-import
java.util.Collections;
import
java.util.List;
+import
java.util.Collections;
import
org.hibernate.Criteria;
import
org.hibernate.Hibernate;
import
org.hibernate.Session;
import
org.hibernate.annotations.common.AssertionFailure;
-import
org.hibernate.criterion.Disjunction;
-import
org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@(protected) @@
* @author Emmanuel Bernard
*/
public class QueryLoader implements Loader {
- private static final int MAX_IN_CLAUSE = 500;
private final Logger log = LoggerFactory.getLogger( QueryLoader.class );
private Session session;
@@(protected) @@
}
public Object load(EntityInfo entityInfo) {
- //be sure to get an initialized object
- Object maybeProxy = session.get( entityInfo.clazz, entityInfo.id );
- try {
- Hibernate.initialize( maybeProxy );
- }
- catch (RuntimeException e) {
- if ( LoaderHelper.isObjectNotFoundException( e ) ) {
- log.debug( "Object found in Search index but not in database: {} with id {}",
- entityInfo.clazz, entityInfo.id );
- maybeProxy = null;
- }
- else {
- throw e;
- }
- }
- return maybeProxy;
+ return ObjectLoaderHelper.load( entityInfo, session );
}
public List load(EntityInfo... entityInfos) {
- final int maxResults = entityInfos.length;
- if ( maxResults == 0 ) return Collections.EMPTY_LIST;
+ if ( entityInfos.length == 0 ) return Collections.EMPTY_LIST;
if ( entityType == null ) throw new AssertionFailure( "EntityType not defined" );
if ( criteria == null ) criteria = session.createCriteria( entityType );
- DocumentBuilder builder = searchFactoryImplementor.getDocumentBuilders().get( entityType );
- String idName = builder.getIdentifierName();
- int loop = maxResults / MAX_IN_CLAUSE;
- boolean exact = maxResults % MAX_IN_CLAUSE == 0;
- if ( !exact ) loop++;
- Disjunction disjunction = Restrictions.disjunction();
- for (int index = 0; index < loop; index++) {
- int max = index * MAX_IN_CLAUSE + MAX_IN_CLAUSE <= maxResults ?
- index * MAX_IN_CLAUSE + MAX_IN_CLAUSE :
- maxResults;
- List ids = new ArrayList( max - index * MAX_IN_CLAUSE );
- for (int entityInfoIndex = index * MAX_IN_CLAUSE; entityInfoIndex < max; entityInfoIndex++) {
- ids.add( entityInfos[entityInfoIndex].id );
- }
- disjunction.add( Restrictions.in( idName, ids ) );
- }
- criteria.add( disjunction );
- criteria.list(); //load all objects
-
- //mandatory to keep the same ordering
- List result = new ArrayList( entityInfos.length );
- for (EntityInfo entityInfo : entityInfos) {
- Object element = session.load( entityInfo.clazz, entityInfo.id );
- if ( Hibernate.isInitialized( element ) ) {
- //all existing elements should have been loaded by the query,
- //the other ones are missing ones
- result.add( element );
- }
- else {
- if ( log.isDebugEnabled() ) {
- log.debug( "Object found in Search index but not in database: {} with {}",
- entityInfo.clazz, entityInfo.id );
- }
- }
- }
- return result;
+ ObjectLoaderHelper.initializeObjects( entityInfos, criteria, entityType, searchFactoryImplementor );
+ return ObjectLoaderHelper.returnAlreadyLoadedObjectsInCorrectOrder( entityInfos, session );
}
public void setCriteria(Criteria criteria) {
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/SearchFactoryImplementor.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/SearchFactoryImplementor.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/engine/SearchFactoryImplementor.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
import
java.util.Map;
import
java.util.Set;
-import
java.util.concurrent.locks.ReentrantLock;
+import
java.util.concurrent.locks.Lock;
import
org.hibernate.search.SearchFactory;
import
org.hibernate.search.backend.BackendQueueProcessorFactory;
@@(protected) @@
Map<Class, DocumentBuilder<Object>> getDocumentBuilders();
- Map<DirectoryProvider, ReentrantLock> getLockableDirectoryProviders();
-
Worker getWorker();
void addOptimizerStrategy(DirectoryProvider<?> provider, OptimizerStrategy optimizerStrategy);
@@(protected) @@
void addClassToDirectoryProvider(Class clazz, DirectoryProvider<?> directoryProvider);
Set<Class> getClassesInDirectoryProvider(DirectoryProvider<?> directoryProvider);
+
+ Set<DirectoryProvider> getDirectoryProviders();
+
+ Lock getDirectoryProviderLock(DirectoryProvider dp);
+
+ void addDirectoryProvider(DirectoryProvider<?> provider);
+
}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/filter/MRUFilterCachingStrategy.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/filter/MRUFilterCachingStrategy.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/filter/MRUFilterCachingStrategy.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
import
org.apache.lucene.search.Filter;
import
org.hibernate.search.Environment;
-import
org.hibernate.search.SearchException;
+import
org.hibernate.search.backend.configuration.ConfigurationParseHelper;
import
org.hibernate.util.SoftLimitMRUCache;
/**
@@(protected) @@
* @author Emmanuel Bernard
*/
public class MRUFilterCachingStrategy implements FilterCachingStrategy {
- private static final String DEFAULT_SIZE = "128";
+ private static final int DEFAULT_SIZE = 128;
private SoftLimitMRUCache cache;
private static final String SIZE = Environment.FILTER_CACHING_STRATEGY + ".size";
public void initialize(Properties properties) {
- int size;
- try {
- size = Integer.parseInt(
- properties.getProperty( SIZE, DEFAULT_SIZE )
- );
- }
- catch (NumberFormatException nfe) {
- throw new SearchException(
- "Unable to parse " + SIZE + ": " + properties.getProperty( SIZE, DEFAULT_SIZE ), nfe
- );
- }
-
+ int size = ConfigurationParseHelper.getIntValue( properties, SIZE, DEFAULT_SIZE );
cache = new SoftLimitMRUCache( size );
}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/impl/SearchFactoryImpl.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/impl/SearchFactoryImpl.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
import
java.util.Properties;
import
java.util.Set;
import
java.util.WeakHashMap;
+import
java.util.concurrent.atomic.AtomicBoolean;
+import
java.util.concurrent.locks.Lock;
import
java.util.concurrent.locks.ReentrantLock;
import
org.apache.lucene.analysis.Analyzer;
@@(protected) @@
Version.touch();
}
- private static final Logger log = LoggerFactory.getLogger( SearchFactoryImpl.class );
+ private final Logger log = LoggerFactory.getLogger( SearchFactoryImpl.class );
private final Map<Class, DocumentBuilder<Object>> documentBuilders = new HashMap<Class, DocumentBuilder<Object>>();
//keep track of the index modifiers per DirectoryProvider since multiple entity can use the same directory provider
- //TODO move the ReentrantLock into DirectoryProviderData.lock, add a getDPLock(DP) and add a Set<DP> getDirectoryProviders() method.
- private final Map<DirectoryProvider, ReentrantLock> lockableDirectoryProviders =
- new HashMap<DirectoryProvider, ReentrantLock>();
- private final Map<DirectoryProvider, DirectoryProviderData> dirProviderData =
- new HashMap<DirectoryProvider, DirectoryProviderData>();
- private Worker worker;
- private ReaderProvider readerProvider;
+ private final Map<DirectoryProvider, DirectoryProviderData> dirProviderData = new HashMap<DirectoryProvider, DirectoryProviderData>();
+ private final Worker worker;
+ private final ReaderProvider readerProvider;
private BackendQueueProcessorFactory backendQueueProcessorFactory;
private final Map<String, FilterDef> filterDefinitions = new HashMap<String, FilterDef>();
- private FilterCachingStrategy filterCachingStrategy;
+ private final FilterCachingStrategy filterCachingStrategy;
private Map<String, Analyzer> analyzers;
- private boolean stopped = false;
+ private final AtomicBoolean stopped = new AtomicBoolean( false );
/**
* Each directory provider (index) can have its own performance settings.
*/
private Map<DirectoryProvider, LuceneIndexingParameters> dirProviderIndexingParams =
new HashMap<DirectoryProvider, LuceneIndexingParameters>();
- private String indexingStrategy;
+ private final String indexingStrategy;
public BackendQueueProcessorFactory getBackendQueueProcessorFactory() {
@@(protected) @@
public SearchFactoryImpl(Configuration cfg) {
//yuk
ReflectionManager reflectionManager = getReflectionManager( cfg );
- setIndexingStrategy(cfg); //need to be done before the document builds
- InitContext context = new InitContext(cfg);
- initDocumentBuilders(cfg, reflectionManager, context );
+ this.indexingStrategy = defineIndexingStrategy( cfg ); //need to be done before the document builds
+ initDocumentBuilders( cfg, reflectionManager );
Set<Class> indexedClasses = documentBuilders.keySet();
for (DocumentBuilder builder : documentBuilders.values()) {
builder.postInitialize( indexedClasses );
}
- worker = WorkerFactory.createWorker( cfg, this );
- readerProvider = ReaderProviderFactory.createReaderProvider( cfg, this );
- buildFilterCachingStrategy( cfg.getProperties() );
+ this.worker = WorkerFactory.createWorker( cfg, this );
+ this.readerProvider = ReaderProviderFactory.createReaderProvider( cfg, this );
+ this.filterCachingStrategy = buildFilterCachingStrategy( cfg.getProperties() );
}
- private void setIndexingStrategy(Configuration cfg) {
- indexingStrategy = cfg.getProperties().getProperty( Environment.INDEXING_STRATEGY, "event" );
+ private static String defineIndexingStrategy(Configuration cfg) {
+ String indexingStrategy = cfg.getProperties().getProperty( Environment.INDEXING_STRATEGY, "event" );
if ( ! ("event".equals( indexingStrategy ) || "manual".equals( indexingStrategy ) ) ) {
- throw new SearchException(Environment.INDEXING_STRATEGY + " unknown: " + indexingStrategy);
+ throw new SearchException( Environment.INDEXING_STRATEGY + " unknown: " + indexingStrategy );
}
+ return indexingStrategy;
}
public String getIndexingStrategy() {
@@(protected) @@
}
public void close() {
- if (!stopped) {
- stopped = true;
+ if ( stopped.compareAndSet( false, true) ) {
try {
worker.close();
}
@@(protected) @@
log.error( "Worker raises an exception on close()", e );
}
//TODO move to DirectoryProviderFactory for cleaner
- for (DirectoryProvider dp : lockableDirectoryProviders.keySet() ) {
+ for (DirectoryProvider dp : getDirectoryProviders() ) {
try {
dp.stop();
}
@@(protected) @@
SearchFactoryImpl searchFactory = contextMap.get( cfg );
if ( searchFactory == null ) {
searchFactory = new SearchFactoryImpl( cfg );
-
contextMap.put( cfg, searchFactory );
}
return searchFactory;
@@(protected) @@
return documentBuilders;
}
- public Map<DirectoryProvider, ReentrantLock> getLockableDirectoryProviders() {
- return lockableDirectoryProviders;
+ public Set<DirectoryProvider> getDirectoryProviders() {
+ return this.dirProviderData.keySet();
}
public Worker getWorker() {
@@(protected) @@
ReflectionManager reflectionManager;
try {
//TODO introduce a ReflectionManagerHolder interface to avoid reflection
- //I want to avoid hard link between HAN and Validator for usch a simple need
+ //I want to avoid hard link between HAN and Validator for such a simple need
//reuse the existing reflectionManager one when possible
reflectionManager =
(ReflectionManager) cfg.getClass().getMethod( "getReflectionManager" ).invoke( cfg );
@@(protected) @@
return analyzer;
}
- private void initDocumentBuilders(Configuration cfg, ReflectionManager reflectionManager, InitContext context) {
+ private void initDocumentBuilders(Configuration cfg, ReflectionManager reflectionManager) {
+ InitContext context = new InitContext( cfg );
Iterator iter = cfg.getClassMappings();
DirectoryProviderFactory factory = new DirectoryProviderFactory();
- while (iter.hasNext()) {
+ while ( iter.hasNext() ) {
PersistentClass clazz = (PersistentClass) iter.next();
Class<?> mappedClass = clazz.getMappedClass();
if (mappedClass != null) {
@@(protected) @@
factory.startDirectoryProviders();
}
- private void buildFilterCachingStrategy(Properties properties) {
+ private static FilterCachingStrategy buildFilterCachingStrategy(Properties properties) {
+ FilterCachingStrategy filterCachingStrategy;
String impl = properties.getProperty( Environment.FILTER_CACHING_STRATEGY );
if ( StringHelper.isEmpty( impl ) || "mru".equalsIgnoreCase( impl ) ) {
filterCachingStrategy = new MRUFilterCachingStrategy();
@@(protected) @@
}
}
filterCachingStrategy.initialize( properties );
+ return filterCachingStrategy;
}
public FilterCachingStrategy getFilterCachingStrategy() {
@@(protected) @@
}
private static class DirectoryProviderData {
+ public final Lock dirLock = new ReentrantLock();
public OptimizerStrategy optimizerStrategy;
public Set<Class> classes = new HashSet<Class>(2);
}
+
+ public Lock getDirectoryProviderLock(DirectoryProvider dp) {
+ return this.dirProviderData.get( dp ).dirLock;
+ }
+
+ public void addDirectoryProvider(DirectoryProvider<?> provider) {
+ this.dirProviderData.put( provider, new DirectoryProviderData() );
+ }
+
}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/jpa/FullTextEntityManager.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/jpa/FullTextEntityManager.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/jpa/FullTextEntityManager.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
/**
* Force the (re)indexing of a given <b>managed</b> object.
* Indexation is batched per transaction
+ *
+ * @throws IllegalArgumentException if entity is null or not an @Indexed entity
*/
void index(Object entity);
@@(protected) @@
*
* @param entityType
* @param id
+ *
+ * @throws IllegalArgumentException if entityType is null or not an @Indexed entity type
*/
public void purge(Class entityType, Serializable id);
@@(protected) @@
* Remove all entities from a particular class of an index.
*
* @param entityType
+ *
+ * @throws IllegalArgumentException if entityType is null or not an @Indexed entity type
*/
public void purgeAll(Class entityType);
+ /**
+ * flush index change
+ * Force Hibernate Search to apply all changes to the index no waiting for the batch limit
+ */
+ public void flushToIndexes();
+
}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/jpa/impl/FullTextEntityManagerImpl.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/jpa/impl/FullTextEntityManagerImpl.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/jpa/impl/FullTextEntityManagerImpl.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
getFullTextSession().purgeAll( entityType );
}
+ public void flushToIndexes() {
+ getFullTextSession().flushToIndexes();
+ }
+
public void persist(Object entity) {
em.persist( entity );
}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/query/FullTextQueryImpl.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/query/FullTextQueryImpl.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
import
org.hibernate.search.engine.EntityInfo;
import
org.hibernate.search.engine.FilterDef;
import
org.hibernate.search.engine.Loader;
-import
org.hibernate.search.engine.ObjectLoader;
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;
@@(protected) @@
//TODO implements setParameter()
public class FullTextQueryImpl extends AbstractQueryImpl implements FullTextQuery {
private final Logger log = LoggerFactory.getLogger( FullTextQueryImpl.class );
- private
org.apache.lucene.search.Query luceneQuery;
+ private final
org.apache.lucene.search.Query luceneQuery;
private Class[] classes;
private Set<Class> classesAndSubclasses;
//optimization: if we can avoid the filter clause (we can most of the time) do it as it has a significant perf impact
@@(protected) @@
//find the directories
IndexSearcher searcher = buildSearcher( searchFactoryImplementor );
if ( searcher == null ) {
- return new IteratorImpl( new ArrayList<EntityInfo>( 0 ), noLoader );
+ return new IteratorImpl( Collections.EMPTY_LIST, noLoader );
}
try {
Hits hits = getHits( searcher );
@@(protected) @@
return loader;
}
else if ( classes.length == 1 ) {
- QueryLoader loader = new QueryLoader();
+ final QueryLoader loader = new QueryLoader();
loader.init( session, searchFactoryImplementor );
loader.setEntityType( classes[0] );
return loader;
}
else {
- final ObjectLoader objectLoader = new ObjectLoader();
- objectLoader.init( session, searchFactoryImplementor );
- return objectLoader;
+ final MultiClassesQueryLoader loader = new MultiClassesQueryLoader();
+ loader.init( session, searchFactoryImplementor );
+ loader.setEntityTypes( classes );
+ return loader;
}
}
@@(protected) @@
SearchFactoryImplementor searchFactoryImplementor = ContextHelper.getSearchFactoryBySFI( session );
//find the directories
IndexSearcher searcher = buildSearcher( searchFactoryImplementor );
- if ( searcher == null ) return new ArrayList( 0 );
+ if ( searcher == null ) return Collections.EMPTY_LIST;
Hits hits;
try {
hits = getHits( searcher );
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/reader/ReaderProviderHelper.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/reader/ReaderProviderHelper.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/reader/ReaderProviderHelper.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
package org.hibernate.search.reader;
import
java.io.IOException;
+import
java.lang.reflect.Field;
import
java.util.HashSet;
import
java.util.Set;
import
org.apache.lucene.index.IndexReader;
+import
org.apache.lucene.index.MultiReader;
import
org.apache.lucene.search.IndexSearcher;
import
org.apache.lucene.search.MultiSearcher;
import
org.apache.lucene.search.Searchable;
@@(protected) @@
* @author Emmanuel Bernard
*/
public abstract class ReaderProviderHelper {
+
+ private static final Field subReadersField = getSubReadersField();
+
+ private static Field getSubReadersField() {
+ try {
+ Field field = MultiReader.class.getDeclaredField( "subReaders" );
+ if ( ! field.isAccessible() ) field.setAccessible( true );
+ return field;
+ }
+ catch (NoSuchFieldException e) {
+ throw new SearchException( "Incompatible version of Lucene: MultiReader.subReaders not available", e );
+ }
+ }
+
+ public static IndexReader[] getSubReadersFromMultiReader(MultiReader parentReader) {
+ try {
+ return (IndexReader[]) subReadersField.get( parentReader );
+ } catch (IllegalAccessException e) {
+ throw new SearchException( "Incompatible version of Lucene: MultiReader.subReaders not accessible", e );
+ }
+ }
+
@SuppressWarnings( { "ThrowableInstanceNeverThrown" } )
public static IndexReader buildMultiReader(int length, IndexReader[] readers) {
if ( length == 0 ) {
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/reader/SharedReaderProvider.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/reader/SharedReaderProvider.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/reader/SharedReaderProvider.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
package org.hibernate.search.reader;
import
java.io.IOException;
-import
java.lang.reflect.Field;
import
java.util.Collections;
import
java.util.HashMap;
import
java.util.Map;
@@(protected) @@
* @author Emmanuel Bernard
*/
public class SharedReaderProvider implements ReaderProvider {
- private static Field subReadersField;
private final Logger log = LoggerFactory.getLogger ( SharedReaderProvider.class );
/**
* nonfair lock. Need to be acquired on indexReader acquisition or release (semaphore)
@@(protected) @@
if ( outOfDateReader != null ) {
ReaderData readerData = searchIndexReaderSemaphores.get( outOfDateReader );
if ( readerData == null ) {
- closeOutOfDateReader = false; //already removed by another prevous thread
+ closeOutOfDateReader = false; //already removed by another previous thread
}
else if ( readerData.semaphore == 0 ) {
searchIndexReaderSemaphores.remove( outOfDateReader );
@@(protected) @@
IndexReader[] readers;
//TODO should it be CacheableMultiReader? Probably no
if ( reader instanceof MultiReader ) {
- try {
- readers = (IndexReader[]) subReadersField.get( reader );
- }
- catch (IllegalAccessException e) {
- throw new SearchException( "Incompatible version of Lucene: MultiReader.subReaders not accessible", e );
- }
+ readers = ReaderProviderHelper.getSubReadersFromMultiReader( (MultiReader) reader );
if ( trace ) log.trace( "Closing MultiReader: {}", reader );
}
else {
@@(protected) @@
}
public void initialize(Properties props, SearchFactoryImplementor searchFactoryImplementor) {
- if ( subReadersField == null ) {
- try {
- subReadersField = MultiReader.class.getDeclaredField( "subReaders" );
- if ( !subReadersField.isAccessible() ) subReadersField.setAccessible( true );
- }
- catch (NoSuchFieldException e) {
- throw new SearchException( "Incompatible version of Lucene: MultiReader.subReaders not accessible", e );
- }
- }
- Set<DirectoryProvider> providers = searchFactoryImplementor.getLockableDirectoryProviders().keySet();
+ Set<DirectoryProvider> providers = searchFactoryImplementor.getDirectoryProviders();
perDirectoryProviderManipulationLocks = new HashMap<DirectoryProvider, Lock>( providers.size() );
for (DirectoryProvider dp : providers) {
perDirectoryProviderManipulationLocks.put( dp, new ReentrantLock() );
@@(protected) @@
perDirectoryProviderManipulationLocks = Collections.unmodifiableMap( perDirectoryProviderManipulationLocks );
}
- private class ReaderData {
+ private static class ReaderData {
public ReaderData(int semaphore, DirectoryProvider provider) {
this.semaphore = semaphore;
Added: search/branches/jboss_cache_integration/src/java/org/hibernate/search/reader/SharingBufferReaderProvider.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/reader/SharingBufferReaderProvider.java (rev 0)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/reader/SharingBufferReaderProvider.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
+package org.hibernate.search.reader;
+
+import
java.io.IOException;
+import
java.util.Collections;
+import
java.util.HashMap;
+import
java.util.Map;
+import
java.util.Properties;
+import
java.util.Set;
+import
java.util.concurrent.ConcurrentHashMap;
+import
java.util.concurrent.atomic.AtomicInteger;
+import
java.util.concurrent.locks.Lock;
+import
java.util.concurrent.locks.ReentrantLock;
+
+import
org.apache.lucene.index.IndexReader;
+import
org.apache.lucene.index.MultiReader;
+import
org.hibernate.annotations.common.AssertionFailure;
+import
org.hibernate.search.SearchException;
+import
org.hibernate.search.engine.SearchFactoryImplementor;
+import
org.hibernate.search.store.DirectoryProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * As does SharedReaderProvider this also shares IndexReaders as long as they are "current";
+ * main difference with SharedReaderProvider is the way to update the Readers when needed:
+ * this uses IndexReader.reopen() which should improve performance on larger indexes
+ * as it shares buffers with previous IndexReader generation for the segments which didn't change.
+ * Current drawbacks are: need of Lucene > 2.3.0 and less mature (experimental).
+ *
+ * @author Sanne Grinovero
+ */
+public class SharingBufferReaderProvider implements ReaderProvider {
+
+ /**
+ * contains all Readers (most current per DP and all unclosed old)
+ */
+ //TODO ConcurrentHashMap's constructor could benefit from some hints as arguments.
+ protected final Map<IndexReader,ReaderUsagePair> allReaders = new ConcurrentHashMap<IndexReader,ReaderUsagePair>();
+
+ /**
+ * contains last updated Reader; protected by lockOnOpenLatest (in the values)
+ */
+ protected Map<DirectoryProvider,PerDirectoryLatestReader> currentReaders;
+
+ private final Logger log = LoggerFactory.getLogger( SharingBufferReaderProvider.class );
+
+ public void closeReader(IndexReader multiReader) {
+ if ( multiReader == null ) return;
+ IndexReader[] readers;
+ if ( multiReader instanceof MultiReader ) {
+ readers = ReaderProviderHelper.getSubReadersFromMultiReader( (MultiReader) multiReader );
+ }
+ else {
+ throw new AssertionFailure( "Everything should be wrapped in a MultiReader" );
+ }
+ log.debug( "Closing MultiReader: {}", multiReader );
+ for ( IndexReader reader : readers ) {
+ ReaderUsagePair container = allReaders.get( reader );
+ container.close();//virtual
+ }
+ log.trace( "IndexReader closed." );
+ }
+
+ public void initialize(Properties props, SearchFactoryImplementor searchFactoryImplementor) {
+ Map<DirectoryProvider,PerDirectoryLatestReader> map = new HashMap<DirectoryProvider,PerDirectoryLatestReader>();
+ Set<DirectoryProvider> providers = searchFactoryImplementor.getDirectoryProviders();
+ for ( DirectoryProvider provider : providers ) {
+ try {
+ map.put( provider, new PerDirectoryLatestReader( provider ) );
+ } catch (IOException e) {
+ throw new SearchException( "Unable to open Lucene IndexReader", e );
+ }
+ }
+ //FIXME I'm not convinced this non-final fields are safe without locks, but I may be wrong.
+ currentReaders = Collections.unmodifiableMap( map );
+ }
+
+ public IndexReader openReader(DirectoryProvider... directoryProviders) {
+ int length = directoryProviders.length;
+ IndexReader[] readers = new IndexReader[length];
+ log.debug( "Opening IndexReader for directoryProviders: {}", length );
+ for ( int index = 0; index < length; index++ ) {
+ DirectoryProvider directoryProvider = directoryProviders[index];
+ if ( log.isTraceEnabled() ) {
+ log.trace( "Opening IndexReader from {}", directoryProvider.getDirectory() );
+ }
+ PerDirectoryLatestReader directoryLatestReader = currentReaders.get( directoryProvider );
+ readers[index] = directoryLatestReader.refreshAndGet();
+ }
+ // don't use ReaderProviderHelper.buildMultiReader as we need our own cleanup.
+ if ( length == 0 ) {
+ return null;
+ }
+ else {
+ try {
+ return new CacheableMultiReader( readers );
+ }
+ catch (Exception e) {
+ //Lucene 2.2 used to throw IOExceptions here
+ for ( IndexReader ir : readers ) {
+ ReaderUsagePair readerUsagePair = allReaders.get( ir );
+ readerUsagePair.close();
+ }
+ throw new SearchException( "Unable to open a MultiReader", e );
+ }
+ }
+ }
+
+ //overridable method for testability:
+ protected IndexReader readerFactory(DirectoryProvider provider) throws IOException {
+ return IndexReader.open( provider.getDirectory() );
+ }
+
+ /**
+ * Container for the couple IndexReader,UsageCounter.
+ */
+ protected final class ReaderUsagePair {
+
+ public final IndexReader reader;
+ /**
+ * When reaching 0 (always test on change) the reader should be really
+ * closed and then discarded.
+ * Starts at 2 because:
+ * first usage token is artificial: means "current" is not to be closed (+1)
+ * additionally when creating it will be used (+1)
+ */
+ protected final AtomicInteger usageCounter = new AtomicInteger( 2 );
+
+ ReaderUsagePair(IndexReader r) {
+ reader = r;
+ }
+
+ /**
+ * closes the IndexReader if no other resource is using it;
+ * in this case the reference to this container will also be removed.
+ */
+ public void close() {
+ int refCount = usageCounter.decrementAndGet();
+ if ( refCount==0 ) {
+ //TODO I've been experimenting with the idea of an async-close: didn't appear to have an interesting benefit,
+ //so discarded the code. should try with bigger indexes to see if the effect gets more impressive.
+ ReaderUsagePair removed = allReaders.remove( reader );//remove ourself
+ try {
+ reader.close();
+ } catch (IOException e) {
+ log.warn( "Unable to close Lucene IndexReader", e );
+ }
+ assert removed != null;
+ }
+ else if ( refCount<0 ) {
+ //doesn't happen with current code, could help spotting future bugs?
+ throw new AssertionFailure( "Closing an IndexReader for which you didn't own a lock-token, or somebody else which didn't own closed already." );
+ }
+ }
+
+ public String toString(){
+ return "Reader:" + this.hashCode() + " ref.count=" + usageCounter.get();
+ }
+
+ }
+
+ /**
+ * An instance for each DirectoryProvider,
+ * establishing the association between "current" ReaderUsagePair
+ * for a DirectoryProvider and it's lock.
+ */
+ protected final class PerDirectoryLatestReader {
+
+ /**
+ * Reference to the most current IndexReader for a DirectoryProvider;
+ * guarded by lockOnReplaceCurrent;
+ */
+ public ReaderUsagePair current; //guarded by lockOnReplaceCurrent
+ private final Lock lockOnReplaceCurrent = new ReentrantLock();
+
+ /**
+ * @param provider The DirectoryProvider for which we manage the IndexReader.
+ * @throws IOException when the index initialization fails.
+ */
+ public PerDirectoryLatestReader(DirectoryProvider provider) throws IOException {
+ IndexReader reader = readerFactory( provider );
+ ReaderUsagePair initialPair = new ReaderUsagePair( reader );
+ initialPair.usageCounter.set( 1 );//a token to mark as active (preventing real close).
+ lockOnReplaceCurrent.lock();//no harm, just ensuring safe publishing.
+ current = initialPair;
+ lockOnReplaceCurrent.unlock();
+ allReaders.put( reader, initialPair );
+ }
+
+ /**
+ * Gets an updated IndexReader for the current DirectoryProvider;
+ * the index status will be checked.
+ * @return the current IndexReader if it's in sync with underlying index, a new one otherwise.
+ */
+ public IndexReader refreshAndGet() {
+ ReaderUsagePair previousCurrent;
+ IndexReader updatedReader;
+ lockOnReplaceCurrent.lock();
+ try {
+ IndexReader beforeUpdateReader = current.reader;
+ try {
+ updatedReader = beforeUpdateReader.reopen();
+ } catch (IOException e) {
+ throw new SearchException( "Unable to reopen IndexReader", e );
+ }
+ if ( beforeUpdateReader == updatedReader ) {
+ previousCurrent = null;
+ current.usageCounter.incrementAndGet();
+ }
+ else {
+ ReaderUsagePair newPair = new ReaderUsagePair( updatedReader );
+ //no need to increment usageCounter in newPair, as it is constructed with correct number 2.
+ assert newPair.usageCounter.get() == 2;
+ previousCurrent = current;
+ current = newPair;
+ allReaders.put( updatedReader, newPair );//unfortunately still needs lock
+ }
+ } finally {
+ lockOnReplaceCurrent.unlock();
+ }
+ // doesn't need lock:
+ if ( previousCurrent != null ) {
+ previousCurrent.close();// release a token as it's not the current any more.
+ }
+ return updatedReader;
+ }
+
+ }
+
+}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/DirectoryProviderFactory.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/DirectoryProviderFactory.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/DirectoryProviderFactory.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
import
org.hibernate.search.SearchException;
import org.hibernate.search.annotations.Indexed;
import
org.hibernate.search.backend.LuceneIndexingParameters;
+import
org.hibernate.search.backend.configuration.ConfigurationParseHelper;
import
org.hibernate.search.backend.configuration.MaskedProperty;
import
org.hibernate.search.engine.SearchFactoryImplementor;
import
org.hibernate.search.impl.SearchFactoryImpl;
@@(protected) @@
public class DirectoryProviderFactory {
private final List<DirectoryProvider<?>> providers = new ArrayList<DirectoryProvider<?>>();
- private static String DEFAULT_DIRECTORY_PROVIDER = FSDirectoryProvider.class.getName();
+ private static final String DEFAULT_DIRECTORY_PROVIDER = FSDirectoryProvider.class.getName();
private static final String SHARDING_STRATEGY = "sharding_strategy";
private static final String NBR_OF_SHARDS = SHARDING_STRATEGY + ".nbr_of_shards";
@@(protected) @@
configureIndexingParameters( searchFactoryImplementor, indexProps, provider );
providers.add( provider );
searchFactoryImplementor.addClassToDirectoryProvider(entity, provider);
- if ( !searchFactoryImplementor.getLockableDirectoryProviders().containsKey( provider ) ) {
- searchFactoryImplementor.getLockableDirectoryProviders().put( provider, new ReentrantLock() );
+ if ( ! searchFactoryImplementor.getDirectoryProviders().contains( provider ) ) {
+ searchFactoryImplementor.addDirectoryProvider( provider );
}
return provider;
}
@@(protected) @@
return new Properties[] { directoryLocalProperties };
} else {
// count shards
- int shardsCount;
- {
- try {
- shardsCount = Integer.parseInt( shardsCountValue );
- } catch (NumberFormatException e) {
- throw new SearchException( shardsCountValue + " is not a number", e);
- }
- }
+ int shardsCount = ConfigurationParseHelper.parseInt( shardsCountValue, shardsCountValue + " is not a number" );
// create shard-specific Props
Properties[] shardLocalProperties = new Properties[shardsCount];
for ( int i = 0; i < shardsCount; i++ ) {
@@(protected) @@
}
}
- public class DirectoryProviders {
+ public static class DirectoryProviders {
private final IndexShardingStrategy shardingStrategy;
private final DirectoryProvider[] providers;
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/DirectoryProviderHelper.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/DirectoryProviderHelper.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/DirectoryProviderHelper.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
import
java.io.IOException;
import
org.hibernate.search.SearchException;
+import
org.hibernate.search.util.FileHelper;
import
org.hibernate.annotations.common.util.StringHelper;
import
org.apache.lucene.analysis.standard.StandardAnalyzer;
import
org.apache.lucene.index.IndexReader;
@@(protected) @@
private static final Logger log = LoggerFactory.getLogger( DirectoryProviderHelper.class );
private static final String ROOTINDEX_PROP_NAME = "sourceBase";
private static final String RELATIVEINDEX_PROP_NAME = "source";
+ public static final String COPYBUFFERSIZE_PROP_NAME = "buffer_size_on_copy";
/**
* Build a directory name out of a root and relative path, guessing the significant part
@@(protected) @@
log.debug( "Refresh period: {} seconds", period );
return period * 1000; //per second
}
+
+ /**
+ * Users may configure the number of MB to use as
+ * "chunk size" for large file copy operations performed
+ * by DirectoryProviders.
+ * @param directoryProviderName
+ * @param properties
+ * @return the number of Bytes to use as "chunk size" in file copy operations.
+ */
+ public static long getCopyBufferSize(String directoryProviderName, Properties properties) {
+ String value = properties.getProperty( COPYBUFFERSIZE_PROP_NAME );
+ long size = FileHelper.DEFAULT_COPY_BUFFER_SIZE;
+ if ( value != null ) {
+ try {
+ size = Long.parseLong( value ) * 1024 * 1024; //from MB to B.
+ } catch (NumberFormatException nfe) {
+ throw new SearchException( "Unable to initialize index " +
+ directoryProviderName +"; "+ COPYBUFFERSIZE_PROP_NAME + " is not numeric.", nfe );
+ }
+ if ( size <= 0 ) {
+ throw new SearchException( "Unable to initialize index " +
+ directoryProviderName +"; "+ COPYBUFFERSIZE_PROP_NAME + " needs to be greater than zero.");
+ }
+ }
+ return size;
+ }
}
Modified: search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/FSDirectoryProvider.java
===================================================================
--- search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/FSDirectoryProvider.java 2008-06-30 12:55:26 UTC (rev 14826)
+++ search/branches/jboss_cache_integration/src/java/org/hibernate/search/store/FSDirectoryProvider.java 2008-06-30 13:56:28 UTC (rev 14827)
@@(protected) @@
import
java.util.Properties;
import
org.apache.lucene.store.FSDirectory;
-import
org.hibernate.search.Environment;
import