[hibernate-commits] Hibernate SVN: r14690 - in search/trunk: src/java/org/hibernate/search/backend and 3 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Mon May 26 19:04:53 EDT 2008


Author: sannegrinovero
Date: 2008-05-26 19:04:52 -0400 (Mon, 26 May 2008)
New Revision: 14690

Added:
   search/trunk/src/java/org/hibernate/search/backend/configuration/MaskedProperty.java
   search/trunk/src/test/org/hibernate/search/test/configuration/MaskedPropertiesTest.java
Modified:
   search/trunk/doc/reference/en/modules/batchindex.xml
   search/trunk/doc/reference/en/modules/configuration.xml
   search/trunk/doc/reference/en/modules/optimize.xml
   search/trunk/src/java/org/hibernate/search/backend/LuceneIndexingParameters.java
   search/trunk/src/java/org/hibernate/search/backend/configuration/IndexWriterSetting.java
   search/trunk/src/java/org/hibernate/search/store/DirectoryProviderFactory.java
   search/trunk/src/test/org/hibernate/search/test/configuration/ConfigurationReadTestCase.java
   search/trunk/src/test/org/hibernate/search/test/configuration/LuceneIndexingParametersTest.java
   search/trunk/src/test/org/hibernate/search/test/configuration/ShardsConfigurationTest.java
Log:
HSEARCH-200: more IndexWriter settings available, some changes about configuration parsing;
Also includes related Documentation changes, and some unrelated documentation typos.

Modified: search/trunk/doc/reference/en/modules/batchindex.xml
===================================================================
--- search/trunk/doc/reference/en/modules/batchindex.xml	2008-05-23 21:42:40 UTC (rev 14689)
+++ search/trunk/doc/reference/en/modules/batchindex.xml	2008-05-26 23:04:52 UTC (rev 14690)
@@ -42,13 +42,17 @@
 
     <para>Other parameters which also can affect indexing time and memory
     consumption are
-    <literal>hibernate.search.[default|&lt;indexname&gt;].batch.merge_factor</literal>
+    <literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.batch.max_buffered_docs</literal>
     ,
-    <literal>hibernate.search.[default|&lt;indexname&gt;].batch.max_merge_docs</literal>
+    <literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.batch.max_field_length</literal>
     ,
-    <literal>hibernate.search.[default|&lt;indexname&gt;].batch.max_buffered_docs</literal>
+    <literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.batch.max_merge_docs</literal>
+    ,
+    <literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.batch.merge_factor</literal>
+    ,
+    <literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.batch.ram_buffer_size</literal>
     and
-    <literal>hibernate.search.[default|&lt;indexname&gt;].batch.ram_buffer_size</literal>
+    <literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.batch.term_index_interval</literal>
     . These parameters are Lucene specific and Hibernate Search is just
     passing these paramters through - see <xref
     linkend="lucene-indexing-performance" /> for more details.</para>
@@ -108,7 +112,7 @@
     <note>
       <para>Methods <methodname>index</methodname>,
       <methodname>purge</methodname> and <methodname>purgeAll</methodname> are
-      available on <classname>FullTextEntityManager</classname> as well</para>
+      available on <classname>FullTextEntityManager</classname> as well.</para>
     </note>
   </section>
 </chapter>

Modified: search/trunk/doc/reference/en/modules/configuration.xml
===================================================================
--- search/trunk/doc/reference/en/modules/configuration.xml	2008-05-23 21:42:40 UTC (rev 14689)
+++ search/trunk/doc/reference/en/modules/configuration.xml	2008-05-26 23:04:52 UTC (rev 14690)
@@ -400,7 +400,7 @@
 
       <programlisting>@MessageDriven(activationConfig = {
       @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
-      @ActivationConfigProperty(propertyName="destination", propertyValue="queue/hiebrnatesearch"),
+      @ActivationConfigProperty(propertyName="destination", propertyValue="queue/hibernatesearch"),
       @ActivationConfigProperty(propertyName="DLQMaxResent", propertyValue="1")
    } )
 public class MDBSearchController extends AbstractJMSHibernateSearchController implements MessageListener {
@@ -556,7 +556,7 @@
     </section>
   </section>
 
-  <section id="lucene-indexing-performance" revision="2">
+  <section id="lucene-indexing-performance" revision="3">
     <title>Tuning Lucene indexing performance</title>
 
     <para>Hibernate Search allows you to tune the Lucene indexing performance
@@ -564,54 +564,41 @@
     Lucene <literal>IndexWriter</literal> such as
     <literal>mergeFactor</literal>, <literal>maxMergeDocs</literal> and
     <literal>maxBufferedDocs</literal>. You can specify these parameters
-    either as default values applying for all indexes or on a per index
-    basis.</para>
+    either as default values applying for all indexes, on a per index
+    basis, or even per shard.</para>
 
     <para>There are two sets of parameters allowing for different performance
     settings depending on the use case. During indexing operations triggered
-    by database modifications, the following ones are used: <itemizedlist>
-        <listitem>
-          <para><literal>hibernate.search.[default|&lt;indexname&gt;].transaction.merge_factor</literal></para>
-        </listitem>
+    by database modifications, the parameters are grouped by the
+    <literal>transaction</literal> keyword:
+    <programlisting>hibernate.search.[default|&lt;indexname&gt;].indexwriter.transaction.&lt;parameter_name&gt;</programlisting>
+    When indexing occurs via <literal>FullTextSession.index()</literal> (see <xref
+    linkend="search-batchindex" />), the used properties are those grouped under the <literal>batch</literal> keyword:
+    <programlisting>hibernate.search.[default|&lt;indexname&gt;].indexwriter.batch.&lt;parameter_name&gt;</programlisting>
+    </para>
 
-        <listitem>
-          <para><literal>hibernate.search.[default|&lt;indexname&gt;].transaction.max_merge_docs</literal></para>
-        </listitem>
-
-        <listitem>
-          <para><literal>hibernate.search.[default|&lt;indexname&gt;].transaction.max_buffered_docs</literal></para>
-        </listitem>
-        
-        <listitem>
-          <para><literal>hibernate.search.[default|&lt;indexname&gt;].transaction.ram_buffer_size</literal></para>
-        </listitem>
-      </itemizedlist>When indexing occurs via
-    <literal>FullTextSession.index()</literal> (see <xref
-    linkend="search-batchindex" />), the following properties are used:
-    <itemizedlist>
-        <listitem>
-          <para><literal>hibernate.search.[default|&lt;indexname&gt;].batch.merge_factor</literal></para>
-        </listitem>
-
-        <listitem>
-          <para><literal>hibernate.search.[default|&lt;indexname&gt;].batch.max_merge_docs</literal></para>
-        </listitem>
-
-        <listitem>
-          <para><literal>hibernate.search.[default|&lt;indexname&gt;].batch.max_buffered_docs</literal></para>
-        </listitem>
-        
-        <listitem>
-          <para><literal>hibernate.search.[default|&lt;indexname&gt;].batch.ram_buffer_size</literal></para>
-        </listitem>
-      </itemizedlist></para>
-
     <para>Unless the corresponding <literal>.batch</literal> property is
     explicitly set, the value will default to the
-    <literal>.transaction</literal> property.</para>
+    <literal>.transaction</literal> property.
+    If no value is set for a <literal>.batch</literal> value in a specific shard configuration,
+    Hibernate Search will look at the index section, then at the default section and after that
+    it will look for a <literal>.transaction</literal> in the same order:
+    <programlisting>
+    hibernate.search.Animals.2.indexwriter.transaction.max_merge_docs 10
+    hibernate.search.Animals.2.indexwriter.transaction.merge_factor 20
+    hibernate.search.default.indexwriter.batch.max_merge_docs 100</programlisting>
+    This configuration will result in these settings applied to the second shard of Animals index:
+    <itemizedlist>
+    	<listitem><literal>transaction.max_merge_docs</literal> = 10</listitem>
+        <listitem><literal>batch.max_merge_docs</literal> = 100</listitem>
+        <listitem><literal>transaction.merge_factor</literal> = 20</listitem>
+        <listitem><literal>batch.merge_factor</literal> = 20</listitem>
+    </itemizedlist>
+    All other values will use the defaults defined in Lucene.
+    </para>
 
     <para>
-    The default for all values is to leave them at Lucene's own default,
+    The default for all values is to leave them at Lucene&#39;s own default,
     so the listed values in the following table actually depend on the
     version of Lucene you are using;
     values shown are relative to version <literal>2.3</literal>.
@@ -633,62 +620,60 @@
         </thead>
 
         <tbody>
+        
           <row>
-            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].transaction.merge_factor</literal></entry>
+            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.[transaction|batch].max_buffered_delete_terms</literal></entry>
 
-            <entry><para>Controls segment merge frequency and size. </para>
-            <para>Determines how often segment indices are merged when
-            insertion occurs. With smaller values, less RAM is used while
-            indexing, and searches on unoptimized indices are faster, but
-            indexing speed is slower. With larger values, more RAM is used
-            during indexing, and while searches on unoptimized indices are
-            slower, indexing is faster. Thus larger values (&gt; 10) are best
-            for batch index creation, and smaller values (&lt; 10) for indices
-            that are interactively maintained. The value must no be lower than
-            2.</para> <para>Used by Hibernate Search during index update
-            operations as part of database modifications.</para></entry>
+            <entry><para>Determines the minimal number of delete terms required before the buffered
+			in-memory delete terms are applied and flushed. If there are documents
+			buffered in memory at the time, they are merged and a new segment is
+   			created.</para></entry>
 
-            <entry>10</entry>
+            <entry>Disabled (flushes by RAM usage)</entry>
           </row>
 
           <row>
-            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].transaction.max_merge_docs</literal></entry>
+            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.[transaction|batch].max_buffered_docs</literal></entry>
 
-            <entry><para>Defines the largest number of documents allowed in a
-            segment.</para> <para>Used by Hibernate Search during index update
-            operations as part of database modifications.</para></entry>
+            <entry><para>Controls the amount of documents buffered in memory
+            during indexing. The bigger the more RAM is consumed.</para>
+           </entry>
 
-            <entry>Unlimited (Integer.MAX_VALUE)</entry>
+            <entry>Disabled (flushes by RAM usage)</entry>
           </row>
 
           <row>
-            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].transaction.max_buffered_docs</literal></entry>
+            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.[transaction|batch].max_field_length</literal></entry>
 
-            <entry><para>Controls the amount of documents buffered in memory
-            during indexing. The bigger the more RAM is consumed.</para>
-            <para>Used by Hibernate Search during index update operations as
-            part of database modifications.</para></entry>
+            <entry><para>The maximum number of terms that will be indexed for a single field.
+            This limits the amount of memory required for indexing so that very large data will not crash the indexing process by
+			running out of memory. This setting refers to the number of running terms,
+			not to the number of different terms.</para>
+			<para>This silently truncates large documents, excluding from the index all terms that occur further in the document.
+			If you know your source documents are large, be sure to set this value high enough to accomodate the expected size. 
+			If you set it to Integer.MAX_VALUE, then the only limit is your memory, but you should anticipate an OutOfMemoryError.
+			</para>
+			<para>If setting this value in <literal>batch</literal> differently than in <literal>transaction</literal>
+			you may get different data (and results) in your index depending on the indexing mode.</para>
+           </entry>
 
-            <entry>Disabled (not set)</entry>
+            <entry>10000</entry>
           </row>
           
           <row>
-            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].transaction.ram_buffer_size</literal></entry>
+            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.[transaction|batch].max_merge_docs</literal></entry>
 
-            <entry><para>Controls the amount of RAM in MB dedicated to document buffers.
-            When used together max_buffered_docs a flush occurs for whichever event happens first.</para>
-            <para>Generally for faster indexing performance it's best to flush by RAM usage instead of document
-   			count and use as large a RAM buffer as you can.</para>
-            <para>Used by Hibernate Search during index update operations as
-            part of database modifications.</para></entry>
+            <entry><para>Defines the largest number of documents allowed in a segment.
+            Larger values are best for batched indexing and speedier searches.
+            Small values are best for transaction indexing.</para></entry>
 
-            <entry>16 MB</entry>
+            <entry>Unlimited (Integer.MAX_VALUE)</entry>
           </row>
 
           <row>
-            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].batch.merge_factor</literal></entry>
+            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.[transaction|batch].merge_factor</literal></entry>
 
-            <entry><para>Controls segment merge frequency and size.</para>
+            <entry><para>Controls segment merge frequency and size. </para>
             <para>Determines how often segment indices are merged when
             insertion occurs. With smaller values, less RAM is used while
             indexing, and searches on unoptimized indices are faster, but
@@ -697,45 +682,34 @@
             slower, indexing is faster. Thus larger values (&gt; 10) are best
             for batch index creation, and smaller values (&lt; 10) for indices
             that are interactively maintained. The value must no be lower than
-            2.</para> <para>Used during indexing via
-            <literal>FullTextSession.index()</literal></para></entry>
+            2.</para></entry>
 
             <entry>10</entry>
           </row>
 
           <row>
-            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].batch.max_merge_docs</literal></entry>
+            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.[transaction|batch].ram_buffer_size</literal></entry>
 
-            <entry><para>Defines the largest number of documents allowed in a
-            segment.</para> <para>Used during indexing via
-            <literal>FullTextSession.index()</literal></para></entry>
-
-            <entry>Unlimited (Integer.MAX_VALUE)</entry>
-          </row>
-
-          <row>
-            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].batch.max_buffered_docs</literal></entry>
-
-            <entry><para>Controls the amount of documents buffered in memory
-            during indexing. The bigger the more RAM is consumed.</para>
-            <para>Used during indexing via
-            <literal>FullTextSession.index()</literal></para></entry>
-
-            <entry>Disabled (not set)</entry>
-          </row>
-          
-          <row>
-            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].batch.ram_buffer_size</literal></entry>
-
             <entry><para>Controls the amount of RAM in MB dedicated to document buffers.
             When used together max_buffered_docs a flush occurs for whichever event happens first.</para>
             <para>Generally for faster indexing performance it's best to flush by RAM usage instead of document
    			count and use as large a RAM buffer as you can.</para>
-            <para>Used during indexing via
-            <literal>FullTextSession.index()</literal></para></entry>
+            </entry>
 
             <entry>16 MB</entry>
           </row>
+          <row>
+            <entry><literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.[transaction|batch].term_index_interval</literal></entry>
+
+            <entry><para>Expert: Set the interval between indexed terms.</para>
+            <para>Large values cause less memory to be used by IndexReader, but slow random-access to terms.
+            Small values cause more memory to be used by an IndexReader, and speed
+   			random-access to terms. See Lucene documentation for more details.</para>
+            </entry>
+
+            <entry>128</entry>
+          </row>
+
         </tbody>
       </tgroup>
     </table>

Modified: search/trunk/doc/reference/en/modules/optimize.xml
===================================================================
--- search/trunk/doc/reference/en/modules/optimize.xml	2008-05-23 21:42:40 UTC (rev 14689)
+++ search/trunk/doc/reference/en/modules/optimize.xml	2008-05-26 23:04:52 UTC (rev 14690)
@@ -4,8 +4,8 @@
   <title>Index Optimization</title>
 
   <para>From time to time, the Lucene index needs to be optimized. The process
-  is essentially a defragmentation: until the optimization occurs, deleted
-  documents are just marked as such, no physical deletion is applied, the
+  is essentially a defragmentation: until the optimization occurs deleted
+  documents are just marked as such, no physical deletion is applied; the
   optimization can also adjust the number of files in the Lucene
   Directory.</para>
 
@@ -46,7 +46,6 @@
 
     <programlisting>hibernate.search.default.optimizer.operation_limit.max = 1000
 hibernate.search.default.optimizer.transaction_limit.max = 100
-
 hibernate.search.Animal.optimizer.transaction_limit.max = 50</programlisting>
 
     <para>An optimization will be triggered to the <literal>Animal</literal>
@@ -65,7 +64,7 @@
       </listitem>
     </itemizedlist>
 
-    <para>If none of these parameters are defined, not optimization is
+    <para>If none of these parameters are defined, no optimization is
     processed automatically.</para>
   </section>
 
@@ -73,14 +72,14 @@
     <title>Manual optimization</title>
 
     <para>You can programmatically optimize (defragment) a Lucene index from
-    Hibernate Search through the <classname>SearchFactory</classname></para>
+    Hibernate Search through the <classname>SearchFactory</classname>:</para>
 
-    <programlisting>searchFactory.optimize(Order.class);
+    <programlisting>searchFactory.optimize(Order.class);</programlisting>
 
-searchFactory.optimize();</programlisting>
+	<programlisting>searchFactory.optimize();</programlisting>
 
-    <para>The first example reindex the Lucene index holding
-    <classname>Order</classname>s, the second, optimize all indexes.</para>
+    <para>The first example optimizes the Lucene index holding
+    <classname>Order</classname>s; the second, optimizes all indexes.</para>
 
     <para>The <classname>SearchFactory</classname> can be accessed from a
     <classname>FullTextSession</classname>:</para>
@@ -97,15 +96,17 @@
     <title>Adjusting optimization</title>
 
     <para>Apache Lucene has a few parameters to influence how optimization is
-    performed. Hibernate Search expose those parameters.</para>
+    performed. Hibernate Search exposes those parameters.</para>
 
-    <para>Further index optimisation parameters include
-    <literal>hibernate.search.[default|&lt;indexname&gt;].[batch|transaction].merge_factor</literal>,
-    <literal>hibernate.search.[default|&lt;indexname&gt;].[batch|transaction].max_merge_docs</literal>,
-    <literal>hibernate.search.[default|&lt;indexname&gt;].[batch|transaction].max_buffered_docs</literal>
-    and
-    <literal>hibernate.search.[default|&lt;indexname&gt;].[batch|transaction].ram_buffer_size</literal>
-    - see <xref linkend="lucene-indexing-performance" /> for more
-    details.</para>
+    <para>Further index optimisation parameters include:
+    <itemizedlist>
+		<listitem><literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.[batch|transaction].max_buffered_docs</literal></listitem>
+		<listitem><literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.[batch|transaction].max_field_length</literal></listitem>
+		<listitem><literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.[batch|transaction].max_merge_docs</literal></listitem>
+		<listitem><literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.[batch|transaction].merge_factor</literal></listitem>
+		<listitem><literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.[batch|transaction].ram_buffer_size</literal></listitem>
+		<listitem><literal>hibernate.search.[default|&lt;indexname&gt;].indexwriter.[batch|transaction].term_index_interval</literal></listitem>
+	</itemizedlist>
+    See <xref linkend="lucene-indexing-performance" /> for more details.</para>
   </section>
 </chapter>

Modified: search/trunk/src/java/org/hibernate/search/backend/LuceneIndexingParameters.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/LuceneIndexingParameters.java	2008-05-23 21:42:40 UTC (rev 14689)
+++ search/trunk/src/java/org/hibernate/search/backend/LuceneIndexingParameters.java	2008-05-26 23:04:52 UTC (rev 14690)
@@ -1,107 +1,131 @@
-//$Id$
-package org.hibernate.search.backend;
-
-import java.io.Serializable;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-import org.apache.lucene.index.IndexWriter;
-import org.hibernate.search.SearchException;
-import org.hibernate.search.backend.configuration.IndexWriterSetting;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Wrapper class around the Lucene indexing parameters <i>mergeFactor</i>, <i>maxMergeDocs</i>,
- * <i>maxBufferedDocs</i>, <i>termIndexInterval</i>, <i>RAMBufferSizeMB</i>.
- * <p>
- * There are two sets of these parameters. One is for regular indexing the other is for batch indexing
- * triggered by <code>FullTextSessoin.index(Object entity)</code>
- * 
- * @author Hardy Ferentschik
- * @author Sanne Grinovero
- */
-public class LuceneIndexingParameters implements Serializable {
-
-	// value keyword
-	public static final String EXPLICIT_DEFAULT_VALUE = "default"; 
-	// property path keywords
-	public static final String BATCH = "batch";
-	public static final String TRANSACTION = "transaction";
-	
-	private final ParameterSet transactionIndexParameters;
-	private final ParameterSet batchIndexParameters;
-	
-	public LuceneIndexingParameters( Properties sourceProps ) {
-		Properties transactionProps = new Properties();
-		Properties batchProps = new Properties( transactionProps ); // transaction settings is the default for batch
-		//don't iterate on property entries we know all the keys:
-		for ( IndexWriterSetting t : IndexWriterSetting.values() ) {
-			String key = t.getKey();
-			String trxValue = sourceProps.getProperty( TRANSACTION + "." + key );
-			if (trxValue != null) {
-				transactionProps.setProperty( key, trxValue );
-			}
-			String batchValue = sourceProps.getProperty( BATCH + "." + key );
-			if (batchValue != null) {
-				batchProps.setProperty( key, batchValue );
-			}
-		}
-		transactionIndexParameters = new ParameterSet(transactionProps);
-		batchIndexParameters = new ParameterSet(batchProps);
-	}
-
-	public ParameterSet getTransactionIndexParameters() {
-		return transactionIndexParameters;
-	}
-
-	public ParameterSet getBatchIndexParameters() {
-		return batchIndexParameters;
-	}
-
-	public class ParameterSet implements Serializable {
-		
-		final Map<IndexWriterSetting, Integer> parameters = new HashMap<IndexWriterSetting, Integer>();
-		
-		public ParameterSet(Properties prop) {
-			for ( IndexWriterSetting t : IndexWriterSetting.values() ) {
-				String value = prop.getProperty( t.getKey() );
-				if ( ! (value==null || EXPLICIT_DEFAULT_VALUE.equals(value) ) ) {
-					parameters.put( t, t.parseVal(value) );
-				}
-			}
-		}
-		
-		/**
-		 * Applies the parameters represented by this to a writer.
-		 * Undefined parameters are not set, leaving the lucene default.
-		 * @param writer the IndexWriter whereto the parameters will be applied.
-		 */
-		public void applyToWriter(IndexWriter writer) {
-			for ( Map.Entry<IndexWriterSetting,Integer> entry : parameters.entrySet() ) {
-				try {
-					entry.getKey().applySetting( writer, entry.getValue() );
-				} catch ( IllegalArgumentException e ) {
-					//TODO if DirectoryProvider had getDirectoryName() exceptions could tell better
-					throw new SearchException( "Illegal IndexWriter setting "
-							+ entry.getKey().getKey() + " "+ e.getMessage(), e );
-				}
-			}
-		}
-		
-		public Integer getCurrentValueFor(IndexWriterSetting ws){
-			return parameters.get(ws);
-		}
-		
-		public void setCurrentValueFor(IndexWriterSetting ws, Integer newValue){
-			if (newValue==null){
-				parameters.remove(ws);
-			} else {
-				parameters.put(ws, newValue);
-			}
-		}
-
- 	}
-	
-}
+//$Id$
+package org.hibernate.search.backend;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.lucene.index.IndexWriter;
+import org.hibernate.search.SearchException;
+import org.hibernate.search.backend.configuration.IndexWriterSetting;
+import org.hibernate.search.backend.configuration.MaskedProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.hibernate.search.backend.configuration.IndexWriterSetting.MAX_FIELD_LENGTH;
+
+/**
+ * Wrapper class around the Lucene indexing parameters defined in IndexWriterSetting.
+ * <p>
+ * There are two sets of these parameters. One is for regular indexing the other is for batch indexing
+ * triggered by <code>FullTextSessoin.index(Object entity)</code>
+ *
+ * @author Hardy Ferentschik
+ * @author Sanne Grinovero
+ */
+public class LuceneIndexingParameters implements Serializable {
+
+	private static final long serialVersionUID = 5424606407623591663L;
+	private static Logger log = LoggerFactory.getLogger( LuceneIndexingParameters.class );
+	
+	// value keyword
+	public static final String EXPLICIT_DEFAULT_VALUE = "default";
+	// property path keywords
+	public static final String BATCH = "batch";
+	public static final String TRANSACTION = "transaction";
+	public static final String PROP_GROUP = "indexwriter";
+	
+	private final ParameterSet transactionIndexParameters;
+	private final ParameterSet batchIndexParameters;
+	
+	public LuceneIndexingParameters( Properties sourceProps ) {
+		//prefer keys under "indexwriter" but fallback for backwards compatibility:
+		Properties indexingParameters = new MaskedProperty( sourceProps, PROP_GROUP, sourceProps );
+		//get keys for "transaction"
+		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 );
+	}
+
+	private void doSanityChecks(ParameterSet transParams, ParameterSet batchParams) {
+		if ( log.isWarnEnabled() ) {
+			Integer maxFieldLengthTransaction = transParams.parameters.get( MAX_FIELD_LENGTH );
+			Integer maxFieldLengthBatch = transParams.parameters.get( MAX_FIELD_LENGTH );
+			if ( notEquals( maxFieldLengthTransaction, maxFieldLengthBatch ) ){
+				log.warn( "The max_field_length value configured for transaction is different than the value configured for batch." );
+			}
+		}
+	}
+
+	private boolean notEquals(Integer a, Integer b) {
+		if ( a==null && b==null ) return false;
+		if ( a==null && b!=null ) return true;
+		if ( a!=null && b==null ) return true;
+		return a.intValue() != b.intValue();
+	}
+
+	public ParameterSet getTransactionIndexParameters() {
+		return transactionIndexParameters;
+	}
+
+	public ParameterSet getBatchIndexParameters() {
+		return batchIndexParameters;
+	}
+
+	public class ParameterSet implements Serializable {
+		
+		private static final long serialVersionUID = -6121723702279869524L;
+		
+		final Map<IndexWriterSetting, Integer> parameters = new HashMap<IndexWriterSetting, Integer>();
+		
+		public ParameterSet(Properties prop, String paramName) {
+			//don't iterate on property entries as we know all the keys:
+			for ( IndexWriterSetting t : IndexWriterSetting.values() ) {
+				String key = t.getKey();
+				String value = prop.getProperty( key );
+				if ( ! ( value==null || EXPLICIT_DEFAULT_VALUE.equalsIgnoreCase( value ) ) ) {
+					if ( log.isDebugEnabled() ) {
+						//TODO add DirectoryProvider name when available to log message
+						log.debug( "Set indexwriter parameter " + paramName +"." + key + " to value : "+ value );
+					}
+					parameters.put( t, t.parseVal( value ) );
+				}
+			}
+		}
+		
+		/**
+		 * Applies the parameters represented by this to a writer.
+		 * Undefined parameters are not set, leaving the lucene default.
+		 * @param writer the IndexWriter whereto the parameters will be applied.
+		 */
+		public void applyToWriter(IndexWriter writer) {
+			for ( Map.Entry<IndexWriterSetting,Integer> entry : parameters.entrySet() ) {
+				try {
+					entry.getKey().applySetting( writer, entry.getValue() );
+				} catch ( IllegalArgumentException e ) {
+					//TODO if DirectoryProvider had getDirectoryName() exceptions could tell better
+					throw new SearchException( "Illegal IndexWriter setting "
+							+ entry.getKey().getKey() + " "+ e.getMessage(), e );
+				}
+			}
+		}
+		
+		public Integer getCurrentValueFor(IndexWriterSetting ws){
+			return parameters.get(ws);
+		}
+
+		public void setCurrentValueFor(IndexWriterSetting ws, Integer newValue){
+			if (newValue==null){
+				parameters.remove(ws);
+			} else {
+				parameters.put(ws, newValue);
+			}
+		}
+
+ 	}
+
+}

Modified: search/trunk/src/java/org/hibernate/search/backend/configuration/IndexWriterSetting.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/configuration/IndexWriterSetting.java	2008-05-23 21:42:40 UTC (rev 14689)
+++ search/trunk/src/java/org/hibernate/search/backend/configuration/IndexWriterSetting.java	2008-05-26 23:04:52 UTC (rev 14690)
@@ -5,32 +5,73 @@
 import org.apache.lucene.index.IndexWriter;
 import org.hibernate.search.SearchException;
 
+/**
+ * Represents possible options to be applied to an
+ * <code>org.apache.lucene.index.IndexWriter</code>
+ * 
+ * @author Sanne Grinovero
+ */
 public enum IndexWriterSetting implements Serializable {
-	
-	MERGE_FACTOR( "merge_factor" ) {
+	/**
+	 * @see org.apache.lucene.index.IndexWriter.setMaxBufferedDeleteTerms(int)
+	 */
+	MAX_BUFFERED_DELETE_TERMS( "max_buffered_delete_terms" ) {
 		public void applySetting(IndexWriter writer, int value) {
-			writer.setMergeFactor( value );
+			writer.setMaxBufferedDeleteTerms( value );
 		}
 	} ,
+	/**
+	 * @see org.apache.lucene.index.IndexWriter.setMaxBufferedDocs(int)
+	 */
+	MAX_BUFFERED_DOCS( "max_buffered_docs" ) {
+		public void applySetting(IndexWriter writer, int value) {
+			writer.setMaxBufferedDocs( value );
+		}
+	} ,
+	/**
+	 * @see org.apache.lucene.index.IndexWriter.setMaxFieldLength(int)
+	 */
+	MAX_FIELD_LENGTH( "max_field_length" ) {
+		public void applySetting(IndexWriter writer, int value) {
+			writer.setMaxFieldLength( value );
+		}
+	} ,
+	/**
+	 * @see org.apache.lucene.index.IndexWriter.setMaxMergeDocs(int)
+	 */
 	MAX_MERGE_DOCS( "max_merge_docs" ) {
 		public void applySetting(IndexWriter writer, int value) {
 			writer.setMaxMergeDocs( value );
 		}
 	} ,
-	MAX_BUFFERED_DOCS( "max_buffered_docs" ) {
+	/**
+	 * @see org.apache.lucene.index.IndexWriter.setMergeFactor(int)
+	 */
+	MERGE_FACTOR( "merge_factor" ) {
 		public void applySetting(IndexWriter writer, int value) {
-			writer.setMaxBufferedDocs( value );
+			writer.setMergeFactor( value );
 		}
 	} ,
+	/**
+	 * @see org.apache.lucene.index.IndexWriter.setRAMBufferSizeMB(int)
+	 */
 	RAM_BUFFER_SIZE( "ram_buffer_size" ) {
 		public void applySetting(IndexWriter writer, int value) {
 			writer.setRAMBufferSizeMB( value );
 		}
+	},
+	/**
+	 * @see org.apache.lucene.index.IndexWriter.setTermIndexInterval(int)
+	 */
+	TERM_INDEX_INTERVAL( "term_index_interval" ) {
+		public void applySetting(IndexWriter writer, int value) {
+			writer.setTermIndexInterval( value );
+		}
 	};
 	
 	private final String cfgKey;
 	
-	IndexWriterSetting(String configurationKey){
+	IndexWriterSetting(String configurationKey) {
 		this.cfgKey = configurationKey;
 	}
 	
@@ -39,6 +80,9 @@
 	 */
 	public abstract void applySetting(IndexWriter writer, int value);
 
+	/**
+	 * @return The key used in configuration files to select an option.
+	 */
 	public String getKey() {
 		return cfgKey;
 	}

Added: search/trunk/src/java/org/hibernate/search/backend/configuration/MaskedProperty.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/backend/configuration/MaskedProperty.java	                        (rev 0)
+++ search/trunk/src/java/org/hibernate/search/backend/configuration/MaskedProperty.java	2008-05-26 23:04:52 UTC (rev 14690)
@@ -0,0 +1,345 @@
+package org.hibernate.search.backend.configuration;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.InvalidPropertiesFormatException;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A wrapper to Properties, to restrict the availability of
+ * values to only those which have a key beginning with some
+ * masking String.
+ * Methods to list available keys or values or otherwise
+ * enumerate available properties are not available.
+ * 
+ * @author Sanne Grinovero
+ */
+public class MaskedProperty extends Properties implements Serializable {
+	
+	private static final long serialVersionUID = -593307257383085113L;
+	private static Logger log = LoggerFactory.getLogger( MaskedProperty.class );
+	
+	private final Properties masked;
+	private final Properties fallBack;
+	private final String radix;
+	
+	/**
+	 * Provides a view to the provided Properties hiding
+	 * all keys not starting with some [mask.].
+	 * @param propsToMask the Properties containing the values.
+	 * @param mask
+	 */
+	public MaskedProperty(Properties propsToMask, String mask) {
+		this( propsToMask, mask, null );
+	}
+	
+	/**
+	 * Provides a view to the provided Properties hiding
+	 * all keys not starting with some [mask.].
+	 * If no value is found then a value is returned from propsFallBack,
+	 * without masking.
+	 * @param propsToMask
+	 * @param mask
+	 * @param propsFallBack
+	 */
+	public MaskedProperty(Properties propsToMask, String mask, Properties propsFallBack) {
+		if ( propsToMask==null || mask==null ) {
+			throw new java.lang.IllegalArgumentException();
+		}
+		this.masked = propsToMask;
+		this.radix = mask + ".";
+		this.fallBack = propsFallBack;
+	}
+	
+	@Override
+	public String getProperty(String key) {
+		String compositeKey = radix + key;
+		String value = masked.getProperty( compositeKey );
+		if ( value != null) {
+			log.trace( "found a match for key: [{}] value: {}", compositeKey, value );
+			return value;
+		}
+		else if ( fallBack != null ) {
+			return fallBack.getProperty( key );
+		}
+		else {
+			return null;
+		}
+	}
+
+	/**
+	 * @throws IllegalArgumentException if the key is not a String instance
+	 */
+	@Override
+	public boolean containsKey(Object key) {
+		if ( ! ( key instanceof String ) ) {
+			throw new IllegalArgumentException( "key must be a String" );
+		}
+		return getProperty( key.toString() ) != null;
+	}
+
+	@Override
+	public String getProperty(String key, String defaultValue) {
+		String val = getProperty( key );
+		return ( val == null ) ? defaultValue : val;
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public void list(PrintStream out) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public void list(PrintWriter out) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized void load(InputStream inStream) throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized void loadFromXML(InputStream in) throws IOException,
+			InvalidPropertiesFormatException {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public Enumeration<?> propertyNames() {
+		throw new UnsupportedOperationException();
+	}
+	
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized void save(OutputStream out, String comments) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized Object setProperty(String key, String value) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized void store(OutputStream out, String comments)
+			throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized void storeToXML(OutputStream os, String comment,
+			String encoding) throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized void storeToXML(OutputStream os, String comment)
+			throws IOException {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized void clear() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized Object clone() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized boolean contains(Object value) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public boolean containsValue(Object value) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized Enumeration<Object> elements() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public Set<java.util.Map.Entry<Object, Object>> entrySet() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized Object get(Object key) {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public synchronized boolean isEmpty() {
+		if ( fallBack==null ) {
+			return masked.isEmpty();
+		}
+		else {
+			return masked.isEmpty() && fallBack.isEmpty();
+		}
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized Enumeration<Object> keys() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public Set<Object> keySet() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized Object put(Object key, Object value) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized void putAll(Map<? extends Object, ? extends Object> t) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	protected void rehash() {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized Object remove(Object key) {
+		throw new UnsupportedOperationException();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public synchronized int size() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public synchronized String toString() {
+		return masked.toString();
+	}
+
+	/**
+	 * @throws UnsupportedOperationException
+	 */
+	@Override
+	public Collection<Object> values() {
+		throw new UnsupportedOperationException();
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = ( ( fallBack == null ) ? 0 : fallBack.hashCode() );
+		result = prime * result + masked.hashCode();
+		result = prime * result + radix.hashCode();
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if ( this == obj )
+			return true;
+		if ( getClass() != obj.getClass() )
+			return false;
+		final MaskedProperty other = (MaskedProperty) obj;
+		if ( fallBack == null ) {
+			if ( other.fallBack != null )
+				return false;
+		} else if ( ! fallBack.equals( other.fallBack ) )
+			return false;
+		if ( ! masked.equals( other.masked ) )
+			return false;
+		if ( ! radix.equals( other.radix ) )
+			return false;
+		return true;
+	}
+	
+}

Modified: search/trunk/src/java/org/hibernate/search/store/DirectoryProviderFactory.java
===================================================================
--- search/trunk/src/java/org/hibernate/search/store/DirectoryProviderFactory.java	2008-05-23 21:42:40 UTC (rev 14689)
+++ search/trunk/src/java/org/hibernate/search/store/DirectoryProviderFactory.java	2008-05-26 23:04:52 UTC (rev 14690)
@@ -1,331 +1,272 @@
-//$Id$
-package org.hibernate.search.store;
-
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.regex.Pattern;
-
-import org.hibernate.HibernateException;
-import org.hibernate.annotations.common.reflection.ReflectionManager;
-import org.hibernate.annotations.common.reflection.XClass;
-import org.hibernate.cfg.Configuration;
-import org.hibernate.mapping.PersistentClass;
-import org.hibernate.search.SearchException;
-import org.hibernate.search.annotations.Indexed;
-import org.hibernate.search.backend.LuceneIndexingParameters;
-import org.hibernate.search.engine.SearchFactoryImplementor;
-import org.hibernate.search.impl.SearchFactoryImpl;
-import org.hibernate.search.store.optimization.IncrementalOptimizerStrategy;
-import org.hibernate.search.store.optimization.NoOpOptimizerStrategy;
-import org.hibernate.search.store.optimization.OptimizerStrategy;
-import org.hibernate.util.ReflectHelper;
-import org.hibernate.util.StringHelper;
-
-/**
- * Create a Lucene directory provider
- * <p/>
- * Lucene directory providers are configured through properties
- * <ul>
- * 	<li>hibernate.search.default.* and</li>
- * 	<li>hibernate.search.&lt;indexname&gt;.*</li>
- * </ul>
- * <p/>
- * &lt;indexname&gt; properties have precedence over default
- * <p/>
- * The implementation is described by
- * hibernate.search.[default|indexname].directory_provider
- * <p/>
- * If none is defined the default value is FSDirectory
- *
- * @author Emmanuel Bernard
- * @author Sylvain Vieujot
- * @author Hardy Ferentschik
- */
-public class DirectoryProviderFactory {
-	private List<DirectoryProvider<?>> providers = new ArrayList<DirectoryProvider<?>>();
-	private static String LUCENE_PREFIX = "hibernate.search.";
-	private static String LUCENE_DEFAULT = LUCENE_PREFIX + "default.";
-	private static 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";
-	private static Pattern dotPattern = Pattern.compile( "\\." );
-
-
-	public DirectoryProviders createDirectoryProviders(XClass entity, Configuration cfg, SearchFactoryImplementor searchFactoryImplementor) {
-		//get properties
-		String directoryProviderName = getDirectoryProviderName( entity, cfg );
-		Properties[] indexProps = getDirectoryProperties( cfg, directoryProviderName );
-
-		//set up the directories
-		int nbrOfProviders = indexProps.length;
-		DirectoryProvider[] providers = new DirectoryProvider[nbrOfProviders];
-		for (int index = 0 ; index < nbrOfProviders ; index++) {
-			String providerName = nbrOfProviders > 1 ?
-					directoryProviderName + "." + index :
-					directoryProviderName;
-			providers[index] = createDirectoryProvider( providerName,indexProps[index], searchFactoryImplementor);
-		}
-
-		//define sharding strategy
-		IndexShardingStrategy shardingStrategy;
-		Properties shardingProperties = new Properties();
-		//we use an enumeration to get the keys from defaultProperties as well
-		Enumeration<String> allProps = (Enumeration<String>) indexProps[0].propertyNames();
-		while ( allProps.hasMoreElements() ){
-			String key = allProps.nextElement();
-			if ( key.startsWith( SHARDING_STRATEGY ) ) {
-				shardingProperties.put( key, indexProps[0].getProperty( key ) );
-			}
-		}
-
-		String shardingStrategyName = shardingProperties.getProperty( SHARDING_STRATEGY );
-		if ( shardingStrategyName == null) {
-			if ( indexProps.length == 1 ) {
-				shardingStrategy = new NotShardedStrategy();
-			}
-			else {
-				shardingStrategy = new IdHashShardingStrategy();
-			}
-		}
-		else {
-			try {
-				Class shardigStrategyClass = ReflectHelper.classForName( shardingStrategyName, this.getClass() );
-				shardingStrategy = (IndexShardingStrategy) shardigStrategyClass.newInstance();
-			}
-			catch (ClassNotFoundException e) {
-				throw new SearchException("Unable to find ShardingStrategy class " + shardingStrategyName + " for " + directoryProviderName, e);
-			}
-			catch (IllegalAccessException e) {
-				throw new SearchException("Unable to create instance of ShardingStrategy class " + shardingStrategyName
-						+ " Be sure to have a no-arg constructor", e);
-			}
-			catch (InstantiationException e) {
-				throw new SearchException("Unable to create instance of ShardingStrategy class " + shardingStrategyName
-						+ " Be sure to have a no-arg constructor", e);
-			}
-			catch (ClassCastException e) {
-				throw new SearchException("ShardingStrategy class does not implements DirecotryProviderShardingStrategy: "
-						+ shardingStrategyName, e);
-			}
-		}
-		shardingStrategy.initialize( shardingProperties, providers );
-
-		return new DirectoryProviders( shardingStrategy, providers );
-	}
-
-	public void startDirectoryProviders() {
-		for ( DirectoryProvider provider : providers ) {
-			provider.start();
-		}
-	}
-
-	private DirectoryProvider<?> createDirectoryProvider(String directoryProviderName, Properties indexProps, SearchFactoryImplementor searchFactoryImplementor) {
-		String className = indexProps.getProperty( "directory_provider" );
-		if ( StringHelper.isEmpty( className ) ) {
-			className = DEFAULT_DIRECTORY_PROVIDER;
-		}
-		DirectoryProvider<?> provider;
-		try {
-			@SuppressWarnings( "unchecked" )
-			Class<DirectoryProvider> directoryClass = ReflectHelper.classForName(
-					className, DirectoryProviderFactory.class
-			);
-			provider = directoryClass.newInstance();
-		}
-		catch (Exception e) {
-			throw new HibernateException( "Unable to instanciate directory provider: " + className, e );
-		}
-		try {
-			provider.initialize( directoryProviderName, indexProps, searchFactoryImplementor );
-		}
-		catch (Exception e) {
-			throw new HibernateException( "Unable to initialize: " + directoryProviderName, e);
-		}
-		int index = providers.indexOf( provider );
-		if ( index != -1 ) {
-			//share the same Directory provider for the same underlying store
-			return providers.get( index );
-		}
-		else {
-			configureOptimizerStrategy(searchFactoryImplementor, indexProps, provider);
-			configureIndexingParameters(searchFactoryImplementor, indexProps, provider);
-			providers.add( provider );
-			if ( !searchFactoryImplementor.getLockableDirectoryProviders().containsKey( provider ) ) {
-				searchFactoryImplementor.getLockableDirectoryProviders().put( provider, new ReentrantLock() );
-			}
-			return provider;
-		}
-	}
-
-	private void configureOptimizerStrategy(SearchFactoryImplementor searchFactoryImplementor, Properties indexProps, DirectoryProvider<?> provider) {
-		boolean incremental = indexProps.containsKey( "optimizer.operation_limit.max" )
-				|| indexProps.containsKey( "optimizer.transaction_limit.max" );
-		OptimizerStrategy optimizerStrategy;
-		if (incremental) {
-			optimizerStrategy = new IncrementalOptimizerStrategy();
-			optimizerStrategy.initialize( provider, indexProps, searchFactoryImplementor);
-		}
-		else {
-			optimizerStrategy = new NoOpOptimizerStrategy();
-		}
-		searchFactoryImplementor.addOptimizerStrategy(provider, optimizerStrategy);
-	}
-	
-	/**
-	 * Creates a new <code>LuceneIndexingParameters</code> instance for the specified provider. 
-	 * If there are no matching properties in the configuration default values will be applied.
-	 * <p>
-	 * NOTE:</br>
-	 * If a non batch value is set in the configuration apply it also to the
-     * batch mode. This covers the case where users only specify 
-	 * parameters for the non batch mode. In this case the same parameters apply for 
-	 * batch indexing.
-	 * </p>
-	 * 
-	 * @param searchFactoryImplementor the search factory.
-	 * @param indexProps The properties extracted from the configuration.
-	 * @param provider The directory provider for which to configure the indexing parameters.
-	 */
-	private void configureIndexingParameters(SearchFactoryImplementor searchFactoryImplementor, Properties indexProps, DirectoryProvider<?> provider) {
-		LuceneIndexingParameters indexingParams = new LuceneIndexingParameters( indexProps );
-		searchFactoryImplementor.addIndexingParmeters( provider, indexingParams );
-	}
-
-	/**
-	 * Returns an array of directory properties
-	 * Properties are defaulted. For a given property name,
-	 * hibernate.search.indexname.n has priority over hibernate.search.indexname which has priority over hibernate.search
-	 * If the Index is not sharded, a single Properties is returned
-	 * If the index is sharded, the Properties index matches the shard index
-	 */	
-	private static Properties[] getDirectoryProperties(Configuration cfg, String directoryProviderName) {
-
-		Properties cfgAndImplicitProperties = new Properties();
-		// cfg has no defaults, so we may use keySet iteration
-		//FIXME not so sure about that cfg.setProperties()?
-		for ( Map.Entry entry : cfg.getProperties().entrySet() ) {
-			String key = entry.getKey().toString();// casting to String
-			if ( key.startsWith( LUCENE_PREFIX ) ) {
-				//put regular properties and add an explicit batch property when a transaction property is set
-				cfgAndImplicitProperties.put( key, entry.getValue() );
-				//be careful to replace only the intended ".transaction." with ".batch.":
-				String[] splitKey = dotPattern.split( key );
-				//TODO this code is vulnerable to properties with dot in the name. This is not a problem today though
-				if ( splitKey.length > 2 && splitKey[ splitKey.length - 2 ]
-				                                      .equals( LuceneIndexingParameters.TRANSACTION ) ) {
-					splitKey[ splitKey.length - 2 ] = LuceneIndexingParameters.BATCH;
-					StringBuilder missingKeyBuilder = new StringBuilder( splitKey[0] );
-					for (int i = 1; i < splitKey.length; i++) {
-						missingKeyBuilder.append( "." );
-						missingKeyBuilder.append( splitKey[i] );
-					}
-					String additionalKey = missingKeyBuilder.toString();
-					if ( cfg.getProperty(additionalKey) == null ){
-						cfgAndImplicitProperties.put(additionalKey, cfg.getProperty(key) );
-					}
-				}
-			}
-		}
-		Properties globalProperties = new Properties();
-		Properties directoryLocalProperties = new Properties( globalProperties );
-		String directoryLocalPrefix = LUCENE_PREFIX + directoryProviderName + ".";
-		for ( Map.Entry entry : cfgAndImplicitProperties.entrySet() ) {
-			String key = entry.getKey().toString();// casting to String
-			if ( key.startsWith( LUCENE_DEFAULT ) ) {
-				globalProperties.put( key.substring( LUCENE_DEFAULT.length() ), entry.getValue() );
-			}
-			else if ( key.startsWith( directoryLocalPrefix ) ) {
-				directoryLocalProperties.put( key.substring( directoryLocalPrefix.length() ),entry.getValue() );
-			}
-		}
-		final String shardsCountValue = directoryLocalProperties.getProperty(NBR_OF_SHARDS);
-		if (shardsCountValue == null) {
-			// no shards: finished.
-			return new Properties[] { directoryLocalProperties };
-		} else {
-			// count shards
-			int shardsCount = -1;
-			{
-				try {
-					shardsCount = Integer.parseInt( shardsCountValue );
-				} catch (NumberFormatException e) {
-					if ( cfgAndImplicitProperties.getProperty(directoryLocalPrefix + NBR_OF_SHARDS ) != null)
-						throw new SearchException( shardsCountValue + " is not a number", e);
-				}
-			}
-			// create shard-specific Props
-			Properties[] shardLocalProperties = new Properties[shardsCount];
-			for ( int i = 0; i < shardsCount; i++ ) {
-				String currentShardPrefix = i + ".";
-				Properties currentProp = new Properties( directoryLocalProperties );
-				//Enumerations are ugly but otherwise we can't get the property defaults:
-				Enumeration<String> localProps = (Enumeration<String>) directoryLocalProperties.propertyNames();
-				while ( localProps.hasMoreElements() ){
-					String key = localProps.nextElement();
-					if ( key.startsWith( currentShardPrefix ) ) {
-						currentProp.setProperty( key.substring( currentShardPrefix.length() ), directoryLocalProperties.getProperty( key ) );
-					}
-				}
-				shardLocalProperties[i] = currentProp;
-			}
-			return shardLocalProperties;
-		}
-	}
-
-	private static String getDirectoryProviderName(XClass clazz, Configuration cfg) {
-		//yuk
-		ReflectionManager reflectionManager = SearchFactoryImpl.getReflectionManager(cfg);
-		//get the most specialized (ie subclass > superclass) non default index name
-		//if none extract the name from the most generic (superclass > subclass) @Indexed class in the hierarchy
-		//FIXME I'm inclined to get rid of the default value
-		PersistentClass pc = cfg.getClassMapping( clazz.getName() );
-		XClass rootIndex = null;
-		do {
-			XClass currentClazz = reflectionManager.toXClass( pc.getMappedClass() );
-			Indexed indexAnn = currentClazz.getAnnotation( Indexed.class );
-			if ( indexAnn != null ) {
-				if ( indexAnn.index().length() != 0 ) {
-					return indexAnn.index();
-				}
-				else {
-					rootIndex = currentClazz;
-				}
-			}
-			pc = pc.getSuperclass();
-		}
-		while ( pc != null );
-		//there is nobody out there with a non default @Indexed.index
-		if ( rootIndex != null ) {
-			return rootIndex.getName();
-		}
-		else {
-			throw new HibernateException(
-					"Trying to extract the index name from a non @Indexed class: " + clazz.getName() );
-		}
-	}
-
-	public class DirectoryProviders {
-		private IndexShardingStrategy shardingStrategy;
-		private DirectoryProvider[] providers;
-
-
-		public DirectoryProviders(IndexShardingStrategy shardingStrategy, DirectoryProvider[] providers) {
-			this.shardingStrategy = shardingStrategy;
-			this.providers = providers;
-		}
-
-
-		public IndexShardingStrategy getSelectionStrategy() {
-			return shardingStrategy;
-		}
-
-		public DirectoryProvider[] getProviders() {
-			return providers;
-		}
-	}
-}
+//$Id$
+package org.hibernate.search.store;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.concurrent.locks.ReentrantLock;
+
+import org.hibernate.annotations.common.reflection.ReflectionManager;
+import org.hibernate.annotations.common.reflection.XClass;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.search.SearchException;
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.backend.LuceneIndexingParameters;
+import org.hibernate.search.backend.configuration.MaskedProperty;
+import org.hibernate.search.engine.SearchFactoryImplementor;
+import org.hibernate.search.impl.SearchFactoryImpl;
+import org.hibernate.search.store.optimization.IncrementalOptimizerStrategy;
+import org.hibernate.search.store.optimization.NoOpOptimizerStrategy;
+import org.hibernate.search.store.optimization.OptimizerStrategy;
+import org.hibernate.util.ReflectHelper;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Create a Lucene directory provider
+ * <p/>
+ * Lucene directory providers are configured through properties
+ * <ul>
+ * 	<li>hibernate.search.default.* and</li>
+ * 	<li>hibernate.search.&lt;indexname&gt;.*</li>
+ * </ul>
+ * <p/>
+ * &lt;indexname&gt; properties have precedence over default
+ * <p/>
+ * The implementation is described by
+ * hibernate.search.[default|indexname].directory_provider
+ * <p/>
+ * If none is defined the default value is FSDirectory
+ *
+ * @author Emmanuel Bernard
+ * @author Sylvain Vieujot
+ * @author Hardy Ferentschik
+ * @author Sanne Grinovero
+ */
+public class DirectoryProviderFactory {
+	
+	private List<DirectoryProvider<?>> providers = new ArrayList<DirectoryProvider<?>>();
+	private static 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";
+
+	public DirectoryProviders createDirectoryProviders(XClass entity, Configuration cfg, SearchFactoryImplementor searchFactoryImplementor) {
+		//get properties
+		String directoryProviderName = getDirectoryProviderName( entity, cfg );
+		Properties[] indexProps = getDirectoryProperties( cfg, directoryProviderName );
+
+		//set up the directories
+		int nbrOfProviders = indexProps.length;
+		DirectoryProvider[] providers = new DirectoryProvider[nbrOfProviders];
+		for ( int index = 0 ; index < nbrOfProviders ; index++ ) {
+			String providerName = nbrOfProviders > 1 ?
+					directoryProviderName + "." + index :
+					directoryProviderName;
+			providers[index] = createDirectoryProvider( providerName, indexProps[index], searchFactoryImplementor );
+		}
+
+		//define sharding strategy
+		IndexShardingStrategy shardingStrategy;
+		//any indexProperty will do, the indexProps[0] surely exists.
+		String shardingStrategyName = indexProps[0].getProperty( SHARDING_STRATEGY );
+		if ( shardingStrategyName == null) {
+			if ( indexProps.length == 1 ) {
+				shardingStrategy = new NotShardedStrategy();
+			}
+			else {
+				shardingStrategy = new IdHashShardingStrategy();
+			}
+		}
+		else {
+			try {
+				Class shardigStrategyClass = ReflectHelper.classForName( shardingStrategyName, this.getClass() );
+				shardingStrategy = (IndexShardingStrategy) shardigStrategyClass.newInstance();
+			}
+			catch (ClassNotFoundException e) {
+				throw new SearchException("Unable to find ShardingStrategy class " + shardingStrategyName + " for " + directoryProviderName, e);
+			}
+			catch (IllegalAccessException e) {
+				throw new SearchException("Unable to create instance of ShardingStrategy class " + shardingStrategyName
+						+ " Be sure to have a no-arg constructor", e);
+			}
+			catch (InstantiationException e) {
+				throw new SearchException("Unable to create instance of ShardingStrategy class " + shardingStrategyName
+						+ " Be sure to have a no-arg constructor", e);
+			}
+			catch (ClassCastException e) {
+				throw new SearchException("ShardingStrategy class does not implements DirecotryProviderShardingStrategy: "
+						+ shardingStrategyName, e);
+			}
+		}
+		shardingStrategy.initialize(
+				new MaskedProperty( indexProps[0], SHARDING_STRATEGY ), providers );
+		return new DirectoryProviders( shardingStrategy, providers );
+	}
+
+	public void startDirectoryProviders() {
+		for ( DirectoryProvider provider : providers ) {
+			provider.start();
+		}
+	}
+
+	private DirectoryProvider<?> createDirectoryProvider(String directoryProviderName, Properties indexProps, SearchFactoryImplementor searchFactoryImplementor) {
+		String className = indexProps.getProperty( "directory_provider" );
+		if ( StringHelper.isEmpty( className ) ) {
+			className = DEFAULT_DIRECTORY_PROVIDER;
+		}
+		DirectoryProvider<?> provider;
+		try {
+			@SuppressWarnings( "unchecked" )
+			Class<DirectoryProvider> directoryClass = ReflectHelper.classForName(
+					className, DirectoryProviderFactory.class
+			);
+			provider = directoryClass.newInstance();
+		}
+		catch (Exception e) {
+			throw new SearchException( "Unable to instantiate directory provider: " + className, e );
+		}
+		try {
+			provider.initialize( directoryProviderName, indexProps, searchFactoryImplementor );
+		}
+		catch (Exception e) {
+			throw new SearchException( "Unable to initialize: " + directoryProviderName, e );
+		}
+		int index = providers.indexOf( provider );
+		if ( index != -1 ) {
+			//share the same Directory provider for the same underlying store
+			return providers.get( index );
+		}
+		else {
+			configureOptimizerStrategy( searchFactoryImplementor, indexProps, provider );
+			configureIndexingParameters( searchFactoryImplementor, indexProps, provider );
+			providers.add( provider );
+			if ( !searchFactoryImplementor.getLockableDirectoryProviders().containsKey( provider ) ) {
+				searchFactoryImplementor.getLockableDirectoryProviders().put( provider, new ReentrantLock() );
+			}
+			return provider;
+		}
+	}
+
+	private void configureOptimizerStrategy(SearchFactoryImplementor searchFactoryImplementor, Properties indexProps, DirectoryProvider<?> provider) {
+		boolean incremental = indexProps.containsKey( "optimizer.operation_limit.max" )
+				|| indexProps.containsKey( "optimizer.transaction_limit.max" );
+		OptimizerStrategy optimizerStrategy;
+		if (incremental) {
+			optimizerStrategy = new IncrementalOptimizerStrategy();
+			optimizerStrategy.initialize( provider, indexProps, searchFactoryImplementor );
+		}
+		else {
+			optimizerStrategy = new NoOpOptimizerStrategy();
+		}
+		searchFactoryImplementor.addOptimizerStrategy( provider, optimizerStrategy );
+	}
+	
+	/**
+	 * Creates a new <code>LuceneIndexingParameters</code> instance for the specified provider. 
+	 * If there are no matching properties in the configuration default values will be applied.
+	 * <p>
+	 * NOTE:</br>
+	 * If a non batch value is set in the configuration apply it also to the
+     * batch mode. This covers the case where users only specify 
+	 * parameters for the non batch mode. In this case the same parameters apply for 
+	 * batch indexing. Parameters are found "depth-first": if a batch parameter is set
+	 * in a global scope it will take priority on local transaction parameters.
+	 * </p>
+	 * 
+	 * @param searchFactoryImplementor the search factory.
+	 * @param directoryProperties The properties extracted from the configuration.
+	 * @param provider The directory provider for which to configure the indexing parameters.
+	 */
+	private void configureIndexingParameters(SearchFactoryImplementor searchFactoryImplementor,
+			Properties directoryProperties, DirectoryProvider<?> provider) {
+		LuceneIndexingParameters indexingParams = new LuceneIndexingParameters( directoryProperties );
+		searchFactoryImplementor.addIndexingParmeters( provider, indexingParams );
+	}
+
+	/**
+	 * Returns an array of directory properties
+	 * Properties are defaulted. For a given property name,
+	 * hibernate.search.indexname.n has priority over hibernate.search.indexname which has priority over hibernate.search.default
+	 * If the Index is not sharded, a single Properties is returned
+	 * If the index is sharded, the Properties index matches the shard index
+	 */	
+	private static Properties[] getDirectoryProperties(Configuration cfg, String directoryProviderName) {
+		Properties rootCfg = new MaskedProperty( cfg.getProperties(), "hibernate.search" );
+		Properties globalProperties = new MaskedProperty( rootCfg, "default" );
+		Properties directoryLocalProperties = new MaskedProperty( rootCfg, directoryProviderName, globalProperties );
+		final String shardsCountValue = directoryLocalProperties.getProperty( NBR_OF_SHARDS );
+		if ( shardsCountValue == null ) {
+			// no shards: finished.
+			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);
+				}
+			}
+			// create shard-specific Props
+			Properties[] shardLocalProperties = new Properties[shardsCount];
+			for ( int i = 0; i < shardsCount; i++ ) {
+				shardLocalProperties[i] = new MaskedProperty(
+						directoryLocalProperties, Integer.toString(i), directoryLocalProperties );
+			}
+			return shardLocalProperties;
+		}
+	}
+
+	private static String getDirectoryProviderName(XClass clazz, Configuration cfg) {
+		//yuk
+		ReflectionManager reflectionManager = SearchFactoryImpl.getReflectionManager(cfg);
+		//get the most specialized (ie subclass > superclass) non default index name
+		//if none extract the name from the most generic (superclass > subclass) @Indexed class in the hierarchy
+		//FIXME I'm inclined to get rid of the default value
+		PersistentClass pc = cfg.getClassMapping( clazz.getName() );
+		XClass rootIndex = null;
+		do {
+			XClass currentClazz = reflectionManager.toXClass( pc.getMappedClass() );
+			Indexed indexAnn = currentClazz.getAnnotation( Indexed.class );
+			if ( indexAnn != null ) {
+				if ( indexAnn.index().length() != 0 ) {
+					return indexAnn.index();
+				}
+				else {
+					rootIndex = currentClazz;
+				}
+			}
+			pc = pc.getSuperclass();
+		}
+		while ( pc != null );
+		//there is nobody out there with a non default @Indexed.index
+		if ( rootIndex != null ) {
+			return rootIndex.getName();
+		}
+		else {
+			throw new SearchException(
+					"Trying to extract the index name from a non @Indexed class: " + clazz.getName() );
+		}
+	}
+
+	public class DirectoryProviders {
+		private IndexShardingStrategy shardingStrategy;
+		private DirectoryProvider[] providers;
+
+		public DirectoryProviders(IndexShardingStrategy shardingStrategy, DirectoryProvider[] providers) {
+			this.shardingStrategy = shardingStrategy;
+			this.providers = providers;
+		}
+
+		public IndexShardingStrategy getSelectionStrategy() {
+			return shardingStrategy;
+		}
+
+		public DirectoryProvider[] getProviders() {
+			return providers;
+		}
+	}
+
+}

Modified: search/trunk/src/test/org/hibernate/search/test/configuration/ConfigurationReadTestCase.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/configuration/ConfigurationReadTestCase.java	2008-05-23 21:42:40 UTC (rev 14689)
+++ search/trunk/src/test/org/hibernate/search/test/configuration/ConfigurationReadTestCase.java	2008-05-26 23:04:52 UTC (rev 14690)
@@ -8,6 +8,7 @@
 import org.hibernate.search.engine.SearchFactoryImplementor;
 import org.hibernate.search.impl.SearchFactoryImpl;
 import org.hibernate.search.test.SearchTestCase;
+import org.hibernate.search.util.FileHelper;
 
 /**
  * Contains some utility methods to simplify coding of
@@ -17,7 +18,7 @@
  */
 public abstract class ConfigurationReadTestCase extends SearchTestCase {
 	
-	private static final File INDEX_DIR = new File( new File("."), "indextemp" );
+	private static final File INDEX_DIR = new File( new File( "." ), "indextemp" );
 
 	private SearchFactoryImplementor searchFactory;
 
@@ -31,7 +32,7 @@
 		FullTextSession fullTextSession = Search.createFullTextSession( openSession() );
 		searchFactory = (SearchFactoryImpl) fullTextSession.getSearchFactory();
 		fullTextSession.close();
-		remove(INDEX_DIR);
+		FileHelper.delete( INDEX_DIR );
 		INDEX_DIR.mkdirs();
 	}
 
@@ -83,19 +84,7 @@
 	
 	protected void tearDown() throws Exception {
 		super.tearDown();
-		remove(INDEX_DIR);
+		FileHelper.delete( INDEX_DIR );
 	}
-	
-	private void remove(File indexDir) {
-		if (indexDir.exists()) {
-			File[] containing = indexDir.listFiles();
-			if ( containing != null ) { //is a directory
-				for (int i=0; i<containing.length; i++ ){
-					remove(containing[i]);
-				}
-			}
-			indexDir.delete();
-		}
-	}
 
 }

Modified: search/trunk/src/test/org/hibernate/search/test/configuration/LuceneIndexingParametersTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/configuration/LuceneIndexingParametersTest.java	2008-05-23 21:42:40 UTC (rev 14689)
+++ search/trunk/src/test/org/hibernate/search/test/configuration/LuceneIndexingParametersTest.java	2008-05-26 23:04:52 UTC (rev 14690)
@@ -28,9 +28,10 @@
 		
 		cfg.setProperty( "hibernate.search.Book.batch.max_merge_docs", "12" );
 		cfg.setProperty( "hibernate.search.Book.batch.merge_factor", "13" );
-		cfg.setProperty( "hibernate.search.Book.batch.max_buffered_docs", "14" );
+		// new keyword "indexwriter" is also supported to group parameters:
+		cfg.setProperty( "hibernate.search.Book.indexwriter.batch.max_buffered_docs", "14" );
 		
-		cfg.setProperty( "hibernate.search.Book.transaction.ram_buffer_size", "4" );
+		cfg.setProperty( "hibernate.search.Book.indexwriter.transaction.ram_buffer_size", "4" );
 		cfg.setProperty( "hibernate.search.Book.transaction.max_merge_docs", "15" );
 		cfg.setProperty( "hibernate.search.Book.transaction.merge_factor", "16" );
 		cfg.setProperty( "hibernate.search.Book.transaction.max_buffered_docs", "17" );
@@ -59,7 +60,7 @@
 	
 	public void testUnsetBatchValueTakesTransaction() throws Exception {
 		assertValueIsSet( Document.class, BATCH, MERGE_FACTOR, 6 );
-		assertValueIsSet( Document.class, BATCH, MAX_BUFFERED_DOCS, 7 );
+		assertValueIsSet( Document.class, BATCH, MAX_BUFFERED_DOCS, 1000 );
 	}
 	
 	public void testExplicitBatchParameters() throws Exception {
@@ -69,7 +70,7 @@
 	}
 	
 	public void testInheritedBatchParametersFromTranscation() throws Exception {
-		assertValueIsSet( Book.class, BATCH, RAM_BUFFER_SIZE, 4 );
+		assertValueIsSet( Book.class, BATCH, RAM_BUFFER_SIZE, 1 );
 	}
 	
 	public void testTransactionParameters() throws Exception {

Added: search/trunk/src/test/org/hibernate/search/test/configuration/MaskedPropertiesTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/configuration/MaskedPropertiesTest.java	                        (rev 0)
+++ search/trunk/src/test/org/hibernate/search/test/configuration/MaskedPropertiesTest.java	2008-05-26 23:04:52 UTC (rev 14690)
@@ -0,0 +1,40 @@
+package org.hibernate.search.test.configuration;
+
+import java.util.Properties;
+
+import org.hibernate.search.backend.configuration.MaskedProperty;
+
+/**
+ * @author Sanne Grinovero
+ */
+public class MaskedPropertiesTest extends junit.framework.TestCase {
+
+	public void testConfigurationParsingPrecedence() {
+		Properties cfg = new Properties();
+		cfg.put( "hibernate.search.Animals.transaction.indexwriter.max_merge_docs", "1" );
+		cfg.put( "hibernate.search.Animals.2.transaction.indexwriter.max_merge_docs", "2" );
+		cfg.put( "hibernate.search.Animals.2.transaction.max_merge_docs", "3" );
+		cfg.put( "hibernate.search.Animals.transaction.max_merge_docs", "5" );
+		cfg.put( "hibernate.search.default.transaction.max_merge_docs", "6" );
+		cfg.put( "hibernate.search.default.transaction.indexwriter.max_field_length", "7" );
+
+		//this is more a "concept demo" than a test:
+		Properties root = new MaskedProperty( cfg, "hibernate.search" );
+		//only keys starting as "hibernate.search.default" are exposed:
+		Properties common = new MaskedProperty( root, "default" );
+		//now as "hibernate.search.Animals" or "hibernate.search.default" if first fails:
+		Properties dirProvider = new MaskedProperty( root, "Animals", common );
+		//this narrows visibility to "hibernate.search.<providername|default>.transaction":
+		Properties transaction = new MaskedProperty( dirProvider, "transaction" );
+		Properties shard2 = new MaskedProperty( dirProvider, "2", dirProvider );
+		Properties transactionInShard2 = new MaskedProperty( shard2, "transaction", transaction );
+		Properties newStyleTransaction = new MaskedProperty( transaction, "indexwriter", transaction );
+		Properties newStyleTransactionInShard2 = new MaskedProperty(
+				transactionInShard2, "indexwriter", transactionInShard2 );
+		
+		assertEquals( "7" , newStyleTransaction.getProperty( "max_field_length" ) );
+		assertEquals( "7" , newStyleTransactionInShard2.getProperty( "max_field_length" ) );
+		assertEquals( "5" , transaction.getProperty( "max_merge_docs" ) );
+	}
+	
+}

Modified: search/trunk/src/test/org/hibernate/search/test/configuration/ShardsConfigurationTest.java
===================================================================
--- search/trunk/src/test/org/hibernate/search/test/configuration/ShardsConfigurationTest.java	2008-05-23 21:42:40 UTC (rev 14689)
+++ search/trunk/src/test/org/hibernate/search/test/configuration/ShardsConfigurationTest.java	2008-05-26 23:04:52 UTC (rev 14690)
@@ -4,6 +4,7 @@
 import static org.hibernate.search.backend.configuration.IndexWriterSetting.MAX_MERGE_DOCS;
 import static org.hibernate.search.backend.configuration.IndexWriterSetting.MERGE_FACTOR;
 import static org.hibernate.search.backend.configuration.IndexWriterSetting.RAM_BUFFER_SIZE;
+import static org.hibernate.search.backend.configuration.IndexWriterSetting.TERM_INDEX_INTERVAL;
 import static org.hibernate.search.test.configuration.ConfigurationReadTestCase.TransactionType.TRANSACTION;
 import static org.hibernate.search.test.configuration.ConfigurationReadTestCase.TransactionType.BATCH;
 import org.hibernate.search.store.DirectoryProvider;
@@ -37,32 +38,36 @@
 		cfg.setProperty( "hibernate.search.Documents.0.transaction.max_buffered_docs", "58" );
 		cfg.setProperty( "hibernate.search.Documents.1.batch.max_merge_docs", "11" );
 		cfg.setProperty( "hibernate.search.Documents.1.transaction.max_buffered_docs", "12" );
+		cfg.setProperty( "hibernate.search.Documents.1.transaction.term_index_interval", "12" );
 	}
 	
 	public void testCorrectNumberOfShardsDetected() throws Exception {
-		DirectoryProvider[] docDirProviders = getSearchFactory().getDirectoryProviders(Document.class);
-		assertNotNull(docDirProviders);
-		assertEquals(4, docDirProviders.length);
-		DirectoryProvider[] bookDirProviders = getSearchFactory().getDirectoryProviders(Book.class);
-		assertNotNull(bookDirProviders);
-		assertEquals(2, bookDirProviders.length);
+		DirectoryProvider[] docDirProviders = getSearchFactory()
+			.getDirectoryProviders( Document.class );
+		assertNotNull( docDirProviders);
+		assertEquals( 4, docDirProviders.length );
+		DirectoryProvider[] bookDirProviders = getSearchFactory()
+			.getDirectoryProviders( Book.class );
+		assertNotNull( bookDirProviders );
+		assertEquals( 2, bookDirProviders.length );
 	}
 	
 	public void testSelectionOfShardingStrategy() throws Exception {
-		IndexShardingStrategy shardingStrategy = getSearchFactory().getDocumentBuilders().get(Document.class).getDirectoryProviderSelectionStrategy();
+		IndexShardingStrategy shardingStrategy = getSearchFactory().getDocumentBuilders()
+			.get( Document.class ).getDirectoryProviderSelectionStrategy();
 		assertNotNull( shardingStrategy );
 		assertEquals( shardingStrategy.getClass(), UselessShardingStrategy.class );
 	}
 	
 	public void testShardingSettingsInherited() throws Exception {
-		DirectoryProvider[] docDirProviders = getSearchFactory().getDirectoryProviders(Document.class);
+		DirectoryProvider[] docDirProviders = getSearchFactory().getDirectoryProviders( Document.class );
 		assertTrue( docDirProviders[0] instanceof RAMDirectoryProvider );
 		assertTrue( docDirProviders[1] instanceof FSDirectoryProvider );
 		assertTrue( docDirProviders[2] instanceof RAMDirectoryProvider );
 	}
 	
 	public void testShardN2UsesDefaults() throws Exception {
-		assertValueIsSet( Document.class, 2, TRANSACTION, MAX_BUFFERED_DOCS, 6);
+		assertValueIsSet( Document.class, 2, TRANSACTION, MAX_BUFFERED_DOCS, 6 );
 		assertValueIsDefault( Document.class, 2, TRANSACTION, MAX_MERGE_DOCS );
 		assertValueIsSet( Document.class, 2, TRANSACTION, MERGE_FACTOR, 100 );
 		assertValueIsDefault( Document.class, 2, TRANSACTION, RAM_BUFFER_SIZE );
@@ -78,8 +83,8 @@
 	}
 	
 	public void testShard_BatchInheritedFromTransaction() throws Exception {
-		assertValueIsSet( Document.class, 1, BATCH, MAX_BUFFERED_DOCS, 12 );
-		assertValueIsSet( Document.class, 0, BATCH, MAX_BUFFERED_DOCS, 58 );
+		assertValueIsSet( Document.class, 1, BATCH, TERM_INDEX_INTERVAL, 12 );
+		assertValueIsSet( Document.class, 0, BATCH, MAX_BUFFERED_DOCS, 4 );
 	}
 	
 	protected Class[] getMappings() {




More information about the hibernate-commits mailing list