[hibernate-commits] Hibernate SVN: r11246 - in branches/Branch_3_2/HibernateExt/search: src and 9 other directories.

hibernate-commits at lists.jboss.org hibernate-commits at lists.jboss.org
Mon Feb 26 21:17:25 EST 2007


Author: epbernard
Date: 2007-02-26 21:17:25 -0500 (Mon, 26 Feb 2007)
New Revision: 11246

Added:
   branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/AbstractJMSHibernateSearchController.java
   branches/Branch_3_2/HibernateExt/search/src/test-resources/
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/FSSwitchableAndMasterDPTest.java
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/MultipleSFTestCase.java
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/SnowStorm.java
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MDBSearchController.java
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MyHibernateUtil.java
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/TShirt.java
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/JMSSlaveTest.java
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/SearchQueueChecker.java
   branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/TShirt.java
Removed:
   branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSHibernateSearchController.java
Modified:
   branches/Branch_3_2/HibernateExt/search/build.xml
   branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSBackendQueueProcessorFactory.java
   branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/query/FullTextQueryImpl.java
   branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSMasterDirectoryProvider.java
   branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSSwitchableDirectoryProvider.java
   branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/util/DirectoryProviderHelper.java
Log:
Add JMS test
Add Switchable directory test

Modified: branches/Branch_3_2/HibernateExt/search/build.xml
===================================================================
--- branches/Branch_3_2/HibernateExt/search/build.xml	2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/build.xml	2007-02-27 02:17:25 UTC (rev 11246)
@@ -22,7 +22,7 @@
 	<property name="javac.source" value="1.5"/>
 	<property name="javac.target" value="1.5"/>
     <property name="jdbc.dir" value="jdbc"/>
-	<property name="common.dir" value="${basedir}/../common"/>
+    <property name="common.dir" value="${basedir}/../common"/>
 
     <property name="jpa-api.jar" value="${basedir}/../jpa-api/build/ejb3-persistence.jar"/>
 	<property name="annotations.jar"
@@ -32,23 +32,54 @@
 
 	<import file="${common.dir}/common-build.xml"/>
 
+    
+    <property name="build.testresources.dir" value="${build.dir}/testresources"/>
+    <property name="testresources.dir" value="${basedir}/src/test-resources"/>
 
-	<path id="lib.moduleclass.path">
+    <!-- override order for JBossXB to bootstrap properly -->
+    <path id="junit.classpath">
+           <fileset dir="${lib.dir}">
+                 <include name="*.jar"/>
+           </fileset>
+           <pathelement path="${classes.dir}"/>
+           <pathelement path="${testclasses.dir}"/>
+           <path refid="junit.moduleclasspath"/>
+           <path refid="lib.class.path"/>
+           <path location="${clover.jar}"/>
+    </path>
+    <!-- override order for JBossXB to bootstrap properly -->
+    <path id="lib.class.path">
+    	<path location="${hibernate-core.jar}"></path>
+        <fileset dir="${hibernate-core.lib.dir}">
+            <include name="*.jar"/>
+            <exclude name="xml-apis.jar"/>
+            <exclude name="xerces*.jar"/>
+        </fileset>
+        <fileset dir="${lib.dir}">
+            <include name="*.jar"/>
+        </fileset>
+		<path refid="lib.moduleclass.path"/>
+		<pathelement path="${clover.jar}"/>
+    </path>
+
+
+    <path id="lib.moduleclass.path">
 		<pathelement location="${jpa-api.jar}"/>
 		<pathelement location="${commons-annotations.jar}"/>
 	</path>
 	<path id="junit.moduleclasspath">
-		<pathelement location="${src.dir}"/>
+        <!-- order matters for JBoss XB proper bootstrap -->
+        <fileset dir="${lib.dir}/test">
+			<include name="*.jar"/>
+			<include name="*.zip"/>
+		</fileset>
+        <pathelement location="${src.dir}"/>
 		<pathelement location="${test.dir}"/>
 		<pathelement location="${annotations.jar}"/>
 		<fileset dir="${jdbc.dir}">
 			<include name="*.jar"/>
 			<include name="*.zip"/>
 		</fileset>
-        <fileset dir="${lib.dir}/test">
-			<include name="*.jar"/>
-			<include name="*.zip"/>
-		</fileset>
 	</path>
 
     <target name="init">
@@ -58,6 +89,7 @@
 		<available file="${commons-annotations.jar}" type="file" property="commons-annotations.jar.available"/>
 		<available file="${annotations.jar}" type="file" property="annotations.jar.available"/>
         <mkdir dir="${lib.dir}/test"/>
+        <mkdir dir="${build.testresources.dir}"/>
     </target>
 
 	<target name="get.jpa-api" depends="init" unless="jpa-api.jar.available">
@@ -118,30 +150,48 @@
 		</javac>
 	</target>
 
+    <target name="prepare-test-resources" depends="compiletest">
+        <copy todir="${build.testresources.dir}">
+            <fileset dir="${testresources.dir}">
+                <include name="**/*.*"/>
+            </fileset>
+        </copy>
+        <mkdir dir="${build.testresources.dir}/jars"/>
+        <jar filesetmanifest="merge" jarfile="${build.testresources.dir}/jars/jms-slave.jar" >
+            <fileset dir="${testclasses.dir}">
+                <include name="org/hibernate/search/test/jms/slave/**.*"/>
+            </fileset>
+        </jar>
+        <jar filesetmanifest="merge" jarfile="${build.testresources.dir}/jars/jms-master.jar" >
+            <fileset dir="${testclasses.dir}">
+                <include name="org/hibernate/search/test/jms/master/**.*"/>
+            </fileset>
+        </jar>
+    </target>
 
-	<!-- target name="junit" depends="compiletest">
-			<mkdir dir="test_output"/>
-			<junit fork="yes" printsummary="yes" haltonfailure="yes"
-				 forkmode="perBatch">
-				<classpath>
-					<fileset dir="${jdbc.dir}">
-						<include name="**/*.jar"/>
-						<include name="**/*.zip"/>
-					</fileset>
-					<path refid="lib.class.path"/>
-					<pathelement path="${classes.dir}"/>
-					<pathelement path="${src.dir}"/>
-					<pathelement path="${test.dir}"/>
-				</classpath>
-				<formatter type="plain"/>
-				<formatter type="xml"/>
-				<batchtest fork="yes" todir="test_output" haltonfailure="no">
-					<fileset dir="${classes.dir}">
-						<include name="**/*Test.class"/>
-					</fileset>
-				</batchtest>
-			</junit>
-		</target -->
+    <target name="junit" depends="compiletest, prepare-test-resources">
+        <mkdir dir="test_output"/>
+        <junit forkmode="once" printsummary="yes" haltonfailure="yes">
+            <classpath>
+                <path path="${build.testresources.dir}"/>
+                <!-- dirset dir="${build.testresources.dir}">
+                    <include name="**/*.jar"/>
+                    <include name="**/*.par"/>
+                </dirset -->
+                <path refid="junit.classpath"/>
+                
+                <fileset dir="${jdbc.dir}">
+                    <include name="**/*.jar"/>
+                    <include name="**/*.zip"/>
+                </fileset>
+            </classpath>
+            <formatter type="plain"/>
+            <formatter type="xml"/>
+            <batchtest fork="yes" todir="test_output" haltonfailure="no">
+            	<fileset refid="junit.batchtestset"/>
+            </batchtest>
+        </junit>
+    </target>
 
 	<!-- Run a single unit test. -->
 	<target name="junitsingle" depends="compiletest"

Copied: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/AbstractJMSHibernateSearchController.java (from rev 11235, branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSHibernateSearchController.java)
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/AbstractJMSHibernateSearchController.java	                        (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/AbstractJMSHibernateSearchController.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,111 @@
+//$Id: $
+package org.hibernate.search.backend.impl.jms;
+
+import java.util.List;
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.jms.JMSException;
+import javax.jms.Message;
+import javax.jms.MessageListener;
+import javax.jms.ObjectMessage;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.hibernate.Session;
+import org.hibernate.search.SearchFactory;
+import org.hibernate.search.backend.Work;
+import org.hibernate.search.util.ContextHelper;
+
+/**
+ * Implement the Hibernate Search controller responsible for processing the
+ * work send through JMS by the slave nodes.
+ *
+ * Note the subclass implementation has to implements javax.jms.MessageListener
+ * //TODO Ask Bill why it is required
+ *
+ * @author Emmanuel Bernard
+ */
+public abstract class AbstractJMSHibernateSearchController implements MessageListener {
+	private static Log log = LogFactory.getLog( AbstractJMSHibernateSearchController.class );
+
+	/**
+	 * return the current or give a new session
+	 * This session is not used per se, but is the link to access the Search configuration
+	 *
+	 * A typical EJB 3.0 usecase would be to get the session from the container (injected)
+	 * eg in JBoss EJB 3.0
+	 * <code>
+	 * @PersistenceContext private Session session;
+	 *
+	 * protected Session getSession() {
+	 *     return session
+	 * }
+	 *
+	 * eg in any container
+	 * <code>
+	 * @PersistenceContext private EntityManager entityManager;
+	 *
+	 * protected Session getSession() {
+	 *     return (Session) entityManager.getdelegate();
+	 * }
+	 */
+	protected abstract Session getSession();
+
+	/**
+	 * Ensure to clean the resources after use.
+	 * If the session has been directly or indirectly injected, this method is empty
+	 */
+	protected abstract void cleanSessionIfNeeded(Session session);
+
+	/**
+	 * Process the Hibernate Search work queues received
+	 */
+	public void onMessage(Message message) {
+		if ( !( message instanceof ObjectMessage ) ) {
+			log.error( "Incorrect message type: " + message.getClass() );
+			return;
+		}
+		ObjectMessage objectMessage = (ObjectMessage) message;
+		List<Work> queue;
+		try {
+			queue = (List<Work>) objectMessage.getObject();
+		}
+		catch (JMSException e) {
+			log.error( "Unable to retrieve object from message: " + message.getClass(), e );
+			return;
+		}
+		catch (ClassCastException e) {
+			log.error( "Illegal object retrieved from message", e );
+			return;
+		}
+		Runnable worker = getWorker( queue );
+		worker.run();
+	}
+
+	private Runnable getWorker(List<Work> queue) {
+		//FIXME casting sucks becasue we do not control what get session from
+		Session session = getSession();
+		Runnable processor = null;
+
+		try {
+			SearchFactory factory = ContextHelper.getSearchFactory( session );
+			processor = factory.getBackendQueueProcessorFactory().getProcessor( queue );
+		}
+		finally {
+			cleanSessionIfNeeded(session);
+		}
+		return processor;
+	}
+
+	@PostConstruct
+	public void initialize() {
+		//init the source copy process
+		//TODO actually this is probably wrong since this is now part of the DP
+	}
+
+	@PreDestroy
+	public void shutdown() {
+		//stop the source copy process
+		//TODO actually this is probably wrong since this is now part of the DP
+	}
+}

Modified: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSBackendQueueProcessorFactory.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSBackendQueueProcessorFactory.java	2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSBackendQueueProcessorFactory.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -11,10 +11,9 @@
 import javax.naming.InitialContext;
 import javax.naming.NamingException;
 
-import org.hibernate.HibernateException;
 import org.hibernate.search.Environment;
+import org.hibernate.search.SearchException;
 import org.hibernate.search.SearchFactory;
-import org.hibernate.search.SearchException;
 import org.hibernate.search.backend.BackendQueueProcessorFactory;
 import org.hibernate.search.backend.Work;
 
@@ -40,7 +39,7 @@
 	}
 
 	public Runnable getProcessor(List<Work> queue) {
-		return new JMSBackendQueueProcessor(queue, this);
+		return new JMSBackendQueueProcessor( queue, this );
 	}
 
 
@@ -58,22 +57,30 @@
 	}
 
 	public void prepareJMSTools() {
-		if (jmsQueue != null && factory != null) return;
+		if ( jmsQueue != null && factory != null ) return;
 		try {
-			InitialContext initialContext = getInitialContext(properties);
+			InitialContext initialContext = getInitialContext( properties );
 			factory = (QueueConnectionFactory) initialContext.lookup( jmsConnectionFactoryName );
 			jmsQueue = (Queue) initialContext.lookup( jmsQueueName );
 
 		}
 		catch (NamingException e) {
-			throw new SearchException("Unable to lookup Search queue and connection factory", e);
+			throw new SearchException( "Unable to lookup Search queue ("
+					+ ( jmsQueueName != null ?
+					jmsQueueName :
+					"null" ) + ") and connection factory ("
+					+ ( jmsConnectionFactoryName != null ?
+					jmsConnectionFactoryName :
+					"null" ) + ")",
+					e
+			);
 		}
 	}
 
 	private InitialContext getInitialContext(Properties properties) throws NamingException {
 		Properties jndiProps = getJndiProperties( properties );
-		if (jndiProps.size() == 0) {
-			return new InitialContext(  );
+		if ( jndiProps.size() == 0 ) {
+			return new InitialContext();
 		}
 		else {
 			return new InitialContext( jndiProps );
@@ -90,21 +97,21 @@
 		Properties result = new Properties();
 		while ( iter.hasNext() ) {
 			String prop = (String) iter.next();
-			if ( prop.indexOf(JNDI_PREFIX) > -1 && !specialProps.contains(prop) ) {
+			if ( prop.indexOf( JNDI_PREFIX ) > -1 && !specialProps.contains( prop ) ) {
 				result.setProperty(
-						prop.substring( JNDI_PREFIX.length()+1 ),
-						properties.getProperty(prop)
-					);
+						prop.substring( JNDI_PREFIX.length() + 1 ),
+						properties.getProperty( prop )
+				);
 			}
 		}
 
-		String jndiClass = properties.getProperty(JNDI_PREFIX + "class");
-		String jndiURL = properties.getProperty(JNDI_PREFIX + "url");
+		String jndiClass = properties.getProperty( JNDI_PREFIX + "class" );
+		String jndiURL = properties.getProperty( JNDI_PREFIX + "url" );
 		// we want to be able to just use the defaults,
 		// if JNDI environment properties are not supplied
 		// so don't put null in anywhere
-		if (jndiClass != null) result.put( Context.INITIAL_CONTEXT_FACTORY, jndiClass);
-		if (jndiURL != null) result.put(Context.PROVIDER_URL, jndiURL);
+		if ( jndiClass != null ) result.put( Context.INITIAL_CONTEXT_FACTORY, jndiClass );
+		if ( jndiURL != null ) result.put( Context.PROVIDER_URL, jndiURL );
 
 		return result;
 	}

Deleted: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSHibernateSearchController.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSHibernateSearchController.java	2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/backend/impl/jms/JMSHibernateSearchController.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -1,67 +0,0 @@
-//$Id: $
-package org.hibernate.search.backend.impl.jms;
-
-import java.util.List;
-import javax.annotation.PostConstruct;
-import javax.annotation.PreDestroy;
-import javax.jms.JMSException;
-import javax.jms.Message;
-import javax.jms.MessageListener;
-import javax.jms.ObjectMessage;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.hibernate.Session;
-import org.hibernate.engine.SessionImplementor;
-import org.hibernate.search.SearchFactory;
-import org.hibernate.search.backend.Work;
-import org.hibernate.search.util.ContextHelper;
-
-/**
- * @author Emmanuel Bernard
- */
-public abstract class JMSHibernateSearchController implements MessageListener {
-	private static Log log = LogFactory.getLog( JMSHibernateSearchController.class );
-
-	protected abstract Session getSession();
-
-	public void onMessage(Message message) {
-		if ( !( message instanceof ObjectMessage ) ) {
-			log.error( "Incorrect message type: " + message.getClass() );
-			return;
-		}
-		ObjectMessage objectMessage = (ObjectMessage) message;
-		List<Work> queue;
-		try {
-			queue = (List<Work>) objectMessage.getObject();
-		}
-		catch (JMSException e) {
-			log.error( "Unable to retrieve object from message: " + message.getClass(), e );
-			return;
-		}
-		catch (ClassCastException e) {
-			log.error( "Illegal object retrieved from message", e );
-			return;
-		}
-		Runnable worker = getWorker( queue );
-		worker.run();
-	}
-
-	private Runnable getWorker(List<Work> queue) {
-		//FIXME casting sucks becasue we do not control what get session from
-		SearchFactory factory = ContextHelper.getSearchFactoryBySFI( (SessionImplementor) getSession() );
-		return factory.getBackendQueueProcessorFactory().getProcessor( queue );
-	}
-
-	@PostConstruct
-	public void initialize() {
-		//init the source copy process
-		//TODO actually this is probably wrong since this is now part of the DP
-	}
-
-	@PreDestroy
-	public void shutdown() {
-		//stop the source copy process
-		//TODO actually this is probably wrong since this is now part of the DP
-	}
-}

Modified: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/query/FullTextQueryImpl.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/query/FullTextQueryImpl.java	2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/query/FullTextQueryImpl.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -55,7 +55,7 @@
 	 * classes must be immutable
 	 */
 	public FullTextQueryImpl(org.apache.lucene.search.Query query, Class[] classes, SessionImplementor session,
-						   ParameterMetadata parameterMetadata) {
+							 ParameterMetadata parameterMetadata) {
 		//TODO handle flushMode
 		super( query.toString(), null, session, parameterMetadata );
 		this.luceneQuery = query;
@@ -75,6 +75,9 @@
 		SearchFactory searchFactory = ContextHelper.getSearchFactoryBySFI( session );
 		//find the directories
 		Searcher searcher = buildSearcher( searchFactory );
+		if ( searcher == null ) {
+		   	return new IteratorImpl( new ArrayList<EntityInfo>(0), (Session) this.session);
+		}
 		try {
 			org.apache.lucene.search.Query query = filterQueryByClasses( luceneQuery );
 			Hits hits = searcher.search( query );
@@ -95,20 +98,24 @@
 			throw new HibernateException( "Unable to query Lucene index", e );
 		}
 		finally {
-			if ( searcher != null ) try {
-				searcher.close();
+			if ( searcher != null ) {
+				try {
+					searcher.close();
+				}
+				catch (IOException e) {
+					log.warn( "Unable to properly close searcher during lucene query: " + getQueryString(), e );
+				}
 			}
-			catch (IOException e) {
-				log.warn( "Unable to properly close searcher during lucene query: " + getQueryString(), e );
-			}
 		}
 	}
 
-		public ScrollableResults scroll() throws HibernateException {
+	public ScrollableResults scroll() throws HibernateException {
 		//keep the searcher open until the resultset is closed
-		SearchFactory searchFactory  = ContextHelper.getSearchFactoryBySFI( session );;
+		SearchFactory searchFactory = ContextHelper.getSearchFactoryBySFI( session );
+		;
 		//find the directories
 		Searcher searcher = buildSearcher( searchFactory );
+		//FIXME: handle null searcher
 		Hits hits;
 		try {
 			org.apache.lucene.search.Query query = filterQueryByClasses( luceneQuery );
@@ -135,9 +142,10 @@
 	}
 
 	public List list() throws HibernateException {
-		SearchFactory searchFactory = ContextHelper.getSearchFactoryBySFI( session );;
+		SearchFactory searchFactory = ContextHelper.getSearchFactoryBySFI( session );
 		//find the directories
 		Searcher searcher = buildSearcher( searchFactory );
+		if (searcher == null) return new ArrayList(0);
 		Hits hits;
 		try {
 			org.apache.lucene.search.Query query = filterQueryByClasses( luceneQuery );
@@ -156,21 +164,21 @@
 				//we don't face proxy casting issues since the exact class is extracted from the index
 			}
 			//then initialize the objects
-         List excludedObects = new ArrayList();
-         for ( Object element : result ) {
-            try {
-               Hibernate.initialize( element );
-            }
-            catch (ObjectNotFoundException e) {
-               log.debug( "Object found in Search index but not in database: "
-                       + e.getEntityName() + " wih id " + e.getIdentifier() );
-               excludedObects.add( element );
-            }
-         }
-         if (excludedObects.size() > 0) {
-            result.removeAll( excludedObects );
-         }
-         return result;
+			List excludedObects = new ArrayList();
+			for ( Object element : result ) {
+				try {
+					Hibernate.initialize( element );
+				}
+				catch (ObjectNotFoundException e) {
+					log.debug( "Object found in Search index but not in database: "
+							+ e.getEntityName() + " wih id " + e.getIdentifier() );
+					excludedObects.add( element );
+				}
+			}
+			if ( excludedObects.size() > 0 ) {
+				result.removeAll( excludedObects );
+			}
+			return result;
 		}
 		catch (IOException e) {
 			throw new HibernateException( "Unable to query Lucene index", e );
@@ -264,7 +272,7 @@
 				throw new HibernateException( "Unable to read Lucene directory", e );
 			}
 		}
-		else {
+		else if ( dirNbr == 1 ) {
 			try {
 				searcher = new IndexSearcher( directories.iterator().next() );
 			}
@@ -272,6 +280,9 @@
 				throw new HibernateException( "Unable to read Lucene directory", e );
 			}
 		}
+		else {
+			return null; //no indexed entity set up
+		}
 		return searcher;
 	}
 

Modified: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSMasterDirectoryProvider.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSMasterDirectoryProvider.java	2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSMasterDirectoryProvider.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -24,9 +24,12 @@
  * Use a Lucene FSDirectory
  * The base directory is represented by hibernate.search.<index>.indexBase
  * The index is created in <base directory>/<index name>
+ * The copy directory is built from <sourceBase>/<index name>
+ * TODO explose source
  *
  * @author Emmanuel Bernard
  */
+//TODO rename copy?
 public class FSMasterDirectoryProvider implements DirectoryProvider<FSDirectory> {
 	private static Log log = LogFactory.getLog( FSMasterDirectoryProvider.class );
 	private FSDirectory directory;
@@ -37,13 +40,16 @@
 
 	public void initialize(String directoryProviderName, Properties properties, SearchFactory searchFactory) {
 		//source guessing
-		String source = DirectoryProviderHelper.getSourceDirectory( "sourceBase", "source", properties );
-		if (source == null)
+		String source = DirectoryProviderHelper.getSourceDirectory( "sourceBase", "source", directoryProviderName, properties );
+		if ( source == null)
 			throw new IllegalStateException("FSMasterDirectoryProvider requires a viable source directory");
+		log.debug( "Source directory: " + source );
 		File indexDir = DirectoryProviderHelper.determineIndexDir( directoryProviderName, properties );
+		log.debug( "Index directory: " + indexDir );
 		String refreshPeriod = properties.getProperty( "refresh", "60" );
 		long period = Long.parseLong( refreshPeriod );
-
+		period *= 100 * 60; //per minute
+		log.debug("Refresh period " + period / 1000 + " mins");
 		try {
 			boolean create = !indexDir.exists();
 			indexName = indexDir.getCanonicalPath();
@@ -58,10 +64,10 @@
 			}
 
 			//copy to source
-			if ( new File(source, "current1").exists() ) {
+			if ( new File( source, "current1").exists() ) {
 				current = 2;
 			}
-			else if ( new File(source, "current2").exists() ) {
+			else if ( new File( source, "current2").exists() ) {
 				current = 1;
 			}
 			else {
@@ -69,12 +75,12 @@
 				current = 1;
 			}
 			String currentString = Integer.valueOf( current ).toString();
-			File subDir = new File(source, currentString );
+			File subDir = new File( source, currentString );
 			FileHelper.synchronize( indexDir, subDir, true );
-			new File(source, "current1").delete();
-			new File(source, "current2").delete();
+			new File( source, "current1").delete();
+			new File( source, "current2").delete();
 			//TODO small hole, no file can be found here
-			new File(source, "current" + currentString).createNewFile();
+			new File( source, "current" + currentString).createNewFile();
 			log.debug( "Current directory: " + current);
 		}
 		catch (IOException e) {
@@ -144,6 +150,7 @@
 
 		public void run() {
 			//TODO get rid of current and use the marker file instead?
+			long start = System.currentTimeMillis();
 			inProgress = true;
 			if (directoryProviderLock == null) {
 				directoryProviderLock = searchFactory.getLockableDirectoryProviders().get( directoryProvider );
@@ -159,6 +166,7 @@
 				File destinationFile = new File(destination, Integer.valueOf(index).toString() );
 				//TODO make smart a parameter
 				try {
+					log.trace("Copying " + sourceFile + " into " + destinationFile);
 					FileHelper.synchronize( sourceFile, destinationFile, true);
 					current = index;
 				}
@@ -168,11 +176,11 @@
 					inProgress = false;
 					return;
 				}
-				if ( ! new File(source, "current" + oldIndex).delete() ) {
+				if ( ! new File(destination, "current" + oldIndex).delete() ) {
 					log.warn( "Unable to remove previous marker file from source of " + indexName );
 				}
 				try {
-					new File(source, "current" + index).createNewFile();
+					new File(destination, "current" + index).createNewFile();
 				}
 				catch( IOException e ) {
 					log.warn( "Unable to create current marker in source of " + indexName, e );
@@ -182,6 +190,7 @@
 				directoryProviderLock.unlock();
 				inProgress = false;
 			}
+			log.trace( "Copy for " + indexName + " took " + (System.currentTimeMillis() - start) + " ms");
 		}
 	}
 

Modified: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSSwitchableDirectoryProvider.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSSwitchableDirectoryProvider.java	2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/store/FSSwitchableDirectoryProvider.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -37,20 +37,26 @@
 
 	public void initialize(String directoryProviderName, Properties properties, SearchFactory searchFactory) {
 		//source guessing
-		String source = DirectoryProviderHelper.getSourceDirectory( "sourceBase", "source", properties );
+		String source = DirectoryProviderHelper.getSourceDirectory( "sourceBase", "source", directoryProviderName, properties );
 		if (source == null)
 			throw new IllegalStateException("FSSwitchableDirectoryProvider requires a viable source directory");
 		if ( ! new File(source, "current1").exists() && ! new File(source, "current2").exists() ) {
 			throw new IllegalStateException("No current marker in source directory");
 		}
+		log.debug( "Source directory: " + source );
 		File indexDir = DirectoryProviderHelper.determineIndexDir( directoryProviderName, properties );
+		log.debug( "Index directory: " + indexDir.getPath() );
 		String refreshPeriod = properties.getProperty( "refresh", "60" );
 		long period = Long.parseLong( refreshPeriod );
-
+		period *= 100 * 60; //per minute
+		log.debug("Refresh period " + period / 60000 + " mins");
 		try {
 			boolean create = !indexDir.exists();
 			indexName = indexDir.getCanonicalPath();
-			if (create) indexDir.mkdir();
+			if (create) {
+				indexDir.mkdir();
+				log.debug("Initializing index directory " + indexName);
+			}
 
 			File subDir = new File( indexName, "1" );
 			create = ! subDir.exists();
@@ -81,9 +87,18 @@
 				current = 1;
 				File sourceFile = new File(source);
 				File destinationFile = new File(indexName, Integer.valueOf(current).toString() );
-				//TODO make smart a parameter
+				int sourceCurrent;
+				if ( new File(sourceFile, "current1").exists() ) {
+					sourceCurrent = 1;
+				}
+				else if ( new File(sourceFile, "current2").exists() ) {
+					sourceCurrent = 2;
+				}
+				else {
+					throw new AssertionFailure("No current file marker found in source directory: " + source);
+				}
 				try {
-					FileHelper.synchronize( sourceFile, destinationFile, true);
+					FileHelper.synchronize( new File(sourceFile, String.valueOf(sourceCurrent) ), destinationFile, true);
 				}
 				catch (IOException e) {
 					throw new HibernateException("Umable to synchonize directory: " + indexName, e);
@@ -148,7 +163,7 @@
 				executor.execute( copyTask );
 			}
 			else {
-				log.info( "Skipping directory synchronization, previous work still in progress: " + indexName);
+				log.trace( "Skipping directory synchronization, previous work still in progress: " + indexName);
 			}
 		}
 	}
@@ -164,6 +179,7 @@
 		}
 
 		public void run() {
+			long start = System.currentTimeMillis();
 			try {
 				inProgress = true;
 				int oldIndex = current;
@@ -184,6 +200,7 @@
 				File destinationFile = new File(destination, Integer.valueOf(index).toString() );
 				//TODO make smart a parameter
 				try {
+					log.trace("Copying " + sourceFile + " into " + destinationFile);
 					FileHelper.synchronize( sourceFile, destinationFile, true);
 					current = index;
 				}
@@ -206,6 +223,7 @@
 			finally {
 				inProgress = false;
 			}
+			log.trace( "Copy for " + indexName + " took " + (System.currentTimeMillis() - start) + " ms");
 		}
 	}
 

Modified: branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/util/DirectoryProviderHelper.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/util/DirectoryProviderHelper.java	2007-02-25 10:26:24 UTC (rev 11245)
+++ branches/Branch_3_2/HibernateExt/search/src/java/org/hibernate/search/util/DirectoryProviderHelper.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -8,6 +8,8 @@
 
 import org.hibernate.HibernateException;
 import org.hibernate.AssertionFailure;
+import org.hibernate.search.SearchException;
+import org.hibernate.annotations.common.util.StringHelper;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
@@ -21,7 +23,8 @@
 	 * and checking for the file availability
 	 * 
 	 */
-	public static String getSourceDirectory(String rootPropertyName, String relativePropertyName, Properties properties) {
+	public static String getSourceDirectory(String rootPropertyName, String relativePropertyName,
+											String directoryProviderName, Properties properties) {
 		//TODO check that it's a directory
 		String root = properties.getProperty( rootPropertyName );
 		String relative = properties.getProperty( relativePropertyName );
@@ -31,8 +34,8 @@
 							+ " and " + relativePropertyName + " " + relative != null ? relative : "<null>"
 			);
 		}
-		if (relative == null) throw new HibernateException("source property mandatory");
-		if (root == null) {
+		if (relative == null) relative = directoryProviderName;
+		if ( StringHelper.isEmpty( root ) ) {
 			log.debug( "No root directory, go with relative " + relative );
 			File sourceFile = new File(relative);
 			if ( ! sourceFile.exists() ) {
@@ -41,8 +44,10 @@
 			//else keep source as it
 		}
 		else {
-			File sourceFile = new File(root, relative);
-			if ( sourceFile.exists() ) {
+			File rootDir = new File(root);
+			if ( rootDir.exists() ) {
+				File sourceFile = new File(root, relative);
+				if (! sourceFile.exists() ) sourceFile.mkdir();
 				log.debug( "Get directory from root + relative");
 				try {
 					relative = sourceFile.getCanonicalPath();
@@ -52,12 +57,7 @@
 				}
 			}
 			else {
-				sourceFile = new File(relative);
-				log.debug( "Get directory from relative only");
-				if ( ! sourceFile.exists() ) {
-					throw new HibernateException("Unable to read source directory: " + relative);
-				}
-				//else keep source as it
+				throw new SearchException(rootPropertyName + " does not exist");
 			}
 		}
 		return relative;

Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/FSSwitchableAndMasterDPTest.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/FSSwitchableAndMasterDPTest.java	                        (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/FSSwitchableAndMasterDPTest.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,143 @@
+//$Id: $
+package org.hibernate.search.test.directoryProvider;
+
+import java.io.File;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.lucene.analysis.StopAnalyzer;
+import org.apache.lucene.queryParser.QueryParser;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.event.PostDeleteEventListener;
+import org.hibernate.event.PostInsertEventListener;
+import org.hibernate.event.PostUpdateEventListener;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.search.util.FileHelper;
+import org.hibernate.search.event.FullTextIndexEventListener;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class FSSwitchableAndMasterDPTest extends MultipleSFTestCase {
+
+	public void testProperCopy() throws Exception {
+		Session s1 = getSessionFactories()[0].openSession( );
+		SnowStorm sn = new SnowStorm();
+		sn.setDate( new Date() );
+		sn.setLocation( "Dallas, TX, USA");
+
+		FullTextSession fts2 = Search.createFullTextSession( getSessionFactories()[1].openSession( ) );
+		QueryParser parser = new QueryParser("id", new StopAnalyzer() );
+		List result = fts2.createFullTextQuery( parser.parse( "location:texas" ) ).list();
+		assertEquals( "No copy yet, fresh index expected", 0, result.size() );
+
+		s1.persist( sn );
+		s1.flush(); //we don' commit so we need to flush manually
+
+		fts2.close();
+		s1.close();
+
+		Thread.sleep( 2 * 60 * 100 + 10); //wait a bit more than 2 refresh (one master / one slave)
+
+		//temp test original
+		fts2 = Search.createFullTextSession( getSessionFactories()[0].openSession( ) );
+		result = fts2.createFullTextQuery( parser.parse( "location:dallas" ) ).list();
+		assertEquals( "Original should get one", 1, result.size() );
+
+		fts2 = Search.createFullTextSession( getSessionFactories()[1].openSession( ) );
+		result = fts2.createFullTextQuery( parser.parse( "location:dallas" ) ).list();
+		assertEquals("First copy did not work out", 1, result.size() );
+
+		s1 = getSessionFactories()[0].openSession( );
+		sn = new SnowStorm();
+		sn.setDate( new Date() );
+		sn.setLocation( "Chennai, India");
+
+		s1.persist( sn );
+		s1.flush(); //we don' commit so we need to flush manually
+
+		fts2.close();
+		s1.close();
+
+		Thread.sleep( 2 * 60 * 100 + 10); //wait a bit more than 2 refresh (one master / one slave)
+
+		fts2 = Search.createFullTextSession( getSessionFactories()[1].openSession( ) );
+		result = fts2.createFullTextQuery( parser.parse( "location:chennai" ) ).list();
+		assertEquals("Second copy did not work out", 1, result.size() );
+
+		s1 = getSessionFactories()[0].openSession( );
+		sn = new SnowStorm();
+		sn.setDate( new Date() );
+		sn.setLocation( "Melbourne, Australia");
+
+		s1.persist( sn );
+		s1.flush(); //we don' commit so we need to flush manually
+
+		fts2.close();
+		s1.close();
+
+		Thread.sleep( 2 * 60 * 100 + 10); //wait a bit more than 2 refresh (one master / one slave)
+
+		fts2 = Search.createFullTextSession( getSessionFactories()[1].openSession( ) );
+		result = fts2.createFullTextQuery( parser.parse( "location:melbourne" ) ).list();
+		assertEquals("Third copy did not work out", 1, result.size() );
+
+		fts2.close();
+	}
+
+
+	protected void setUp() throws Exception {
+		File base = new File(".");
+		File root = new File(base, "lucenedirs");
+		root.mkdir();
+
+		File master = new File(root, "master/main");
+		master.mkdirs();
+		master = new File(root, "master/copy");
+		master.mkdirs();
+
+		File slave = new File(root, "slave");
+		slave.mkdir();
+
+		super.setUp();
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+		File base = new File(".");
+		File root = new File(base, "lucenedirs");
+		FileHelper.delete( root );
+	}
+
+	protected int getSFNbrs() {
+		return 2;
+	}
+
+	protected Class[] getMappings() {
+		return new Class[] {
+				SnowStorm.class
+		};
+	}
+
+	protected void configure(Configuration[] cfg) {
+		//master
+		cfg[0].setProperty( "hibernate.search.default.sourceBase", "./lucenedirs/master/copy");
+		cfg[0].setProperty( "hibernate.search.default.indexBase", "./lucenedirs/master/main");
+		cfg[0].setProperty( "hibernate.search.default.refresh", "1"); //every minute
+		cfg[0].setProperty( "hibernate.search.default.directory_provider", "org.hibernate.search.store.FSMasterDirectoryProvider");
+		cfg[0].getEventListeners().setPostDeleteEventListeners( new PostDeleteEventListener[]{ new FullTextIndexEventListener() } );
+		cfg[0].getEventListeners().setPostUpdateEventListeners( new PostUpdateEventListener[]{ new FullTextIndexEventListener() } );
+		cfg[0].getEventListeners().setPostInsertEventListeners( new PostInsertEventListener[]{ new FullTextIndexEventListener() } );
+
+		//slave(s)
+		cfg[1].setProperty( "hibernate.search.default.sourceBase", "./lucenedirs/master/copy");
+		cfg[1].setProperty( "hibernate.search.default.indexBase", "./lucenedirs/slave");
+		cfg[1].setProperty( "hibernate.search.default.refresh", "1"); //every minute
+		cfg[1].setProperty( "hibernate.search.default.directory_provider", "org.hibernate.search.store.FSSwitchableDirectoryProvider");
+		cfg[1].getEventListeners().setPostDeleteEventListeners( new PostDeleteEventListener[]{ new FullTextIndexEventListener() } );
+		cfg[1].getEventListeners().setPostUpdateEventListeners( new PostUpdateEventListener[]{ new FullTextIndexEventListener() } );
+		cfg[1].getEventListeners().setPostInsertEventListeners( new PostInsertEventListener[]{ new FullTextIndexEventListener() } );
+	}
+}

Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/MultipleSFTestCase.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/MultipleSFTestCase.java	                        (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/MultipleSFTestCase.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,95 @@
+//$Id: $
+package org.hibernate.search.test.directoryProvider;
+
+import java.io.InputStream;
+
+import junit.framework.TestCase;
+import org.hibernate.SessionFactory;
+import org.hibernate.cfg.AnnotationConfiguration;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * Build multiple session factories from the same set of classes
+ * The configuration can be altered overriding the #configure() method
+ *
+ * @author Emmanuel Bernard
+ */
+public abstract class MultipleSFTestCase extends TestCase {
+
+	private static SessionFactory[] sessionFactories;
+	private static AnnotationConfiguration[] cfgs;
+	private static Dialect dialect;
+	private static Class lastTestClass;
+
+	protected abstract int getSFNbrs();
+
+	protected void buildSessionFactories(Class[] classes, String[] packages, String[] xmlFiles) throws Exception {
+		if (sessionFactories == null) sessionFactories = new SessionFactory[ getSFNbrs() ];
+		if (cfgs == null) cfgs = new AnnotationConfiguration[ getSFNbrs() ];
+		for (SessionFactory sf : sessionFactories ) if ( sf != null ) sf.close();
+		for (int sfIndex = 0 ; sfIndex < getSFNbrs() ; sfIndex++ ) {
+			cfgs[sfIndex] = new AnnotationConfiguration();
+		}
+		configure( cfgs );
+		for (int sfIndex = 0 ; sfIndex < getSFNbrs() ; sfIndex++ ) {
+			try {
+				if ( recreateSchema() ) {
+					cfgs[sfIndex].setProperty( Environment.HBM2DDL_AUTO, "create-drop" );
+				}
+				for ( int i = 0; i < packages.length; i++ ) {
+					cfgs[sfIndex].addPackage( packages[i] );
+				}
+				for ( int i = 0; i < classes.length; i++ ) {
+					cfgs[sfIndex].addAnnotatedClass( classes[i] );
+				}
+				for ( int i = 0; i < xmlFiles.length; i++ ) {
+					InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream( xmlFiles[i] );
+					cfgs[sfIndex].addInputStream( is );
+				}
+				setDialect( Dialect.getDialect() );
+				sessionFactories[sfIndex] = cfgs[sfIndex].buildSessionFactory( /*new TestInterceptor()*/ );
+			}
+			catch (Exception e) {
+				e.printStackTrace();
+				throw e;
+			}
+		}
+	}
+
+	protected void setUp() throws Exception {
+		if ( sessionFactories == null || sessionFactories[0] == null || lastTestClass != getClass() ) {
+			buildSessionFactories( getMappings(), getAnnotatedPackages(), getXmlFiles() );
+			lastTestClass = getClass();
+		}
+	}
+
+	protected abstract Class[] getMappings();
+
+	protected String[] getAnnotatedPackages() {
+		return new String[]{};
+	}
+
+	protected String[] getXmlFiles() {
+		return new String[]{};
+	}
+
+	private void setDialect(Dialect dialect) {
+		MultipleSFTestCase.dialect = dialect;
+	}
+
+	protected Dialect getDialect() {
+		return dialect;
+	}
+
+	protected abstract void configure(Configuration[] cfg) ;
+
+	protected boolean recreateSchema() {
+		return true;
+	}
+
+	public static SessionFactory[] getSessionFactories() {
+		return sessionFactories;
+	}
+}

Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/SnowStorm.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/SnowStorm.java	                        (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/directoryProvider/SnowStorm.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,58 @@
+//$Id: $
+package org.hibernate.search.test.directoryProvider;
+
+import java.util.Date;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Entity;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+import org.hibernate.search.annotations.DateBridge;
+import org.hibernate.search.annotations.Resolution;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Indexed
+ at Entity
+public class SnowStorm {
+	@Id
+	@GeneratedValue
+	@DocumentId
+	private Long id;
+
+	@Field(index = Index.UN_TOKENIZED)
+	@DateBridge( resolution = Resolution.DAY )
+	private Date date;
+
+	@Field(index = Index.TOKENIZED)
+	private String location;
+
+
+	public Long getId() {
+		return id;
+	}
+
+	public void setId(Long id) {
+		this.id = id;
+	}
+
+	public Date getDate() {
+		return date;
+	}
+
+	public void setDate(Date date) {
+		this.date = date;
+	}
+
+	public String getLocation() {
+		return location;
+	}
+
+	public void setLocation(String location) {
+		this.location = location;
+	}
+}

Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java	                        (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/JMSMasterTest.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,152 @@
+//$Id: $
+package org.hibernate.search.test.jms.master;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import javax.jms.ObjectMessage;
+import javax.jms.Queue;
+import javax.jms.QueueConnection;
+import javax.jms.QueueConnectionFactory;
+import javax.jms.QueueSender;
+import javax.jms.QueueSession;
+import javax.naming.InitialContext;
+
+import org.apache.lucene.analysis.StopAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.Query;
+import org.hibernate.Session;
+import org.hibernate.cfg.Configuration;
+import org.hibernate.search.Environment;
+import org.hibernate.search.FullTextSession;
+import org.hibernate.search.Search;
+import org.hibernate.search.engine.DocumentBuilder;
+import org.hibernate.search.backend.AddWork;
+import org.hibernate.search.backend.Work;
+import org.hibernate.search.backend.impl.jms.JMSBackendQueueProcessorFactory;
+import org.hibernate.search.test.SearchTestCase;
+import org.jboss.deployers.spi.DeploymentException;
+import org.jboss.embedded.Bootstrap;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class JMSMasterTest extends SearchTestCase {
+
+	private Bootstrap bootstrap;
+
+	public void testMessageSending() throws Exception {
+		MyHibernateUtil.sessionFactory = getSessions();
+
+		//create an object wo trigggering indexing
+		Session s = openSession( );
+		s.getTransaction().begin();
+		s.connection().createStatement().executeUpdate(
+				"insert into TShirt_Master(id, logo, size) values( '1', 'JBoss balls', 'large')"
+		);
+		TShirt ts = (TShirt) s.get(TShirt.class, 1);
+		s.getTransaction().commit();
+		s.close();
+		//create the work queue to send
+		Document doc = new Document();
+		Field field = new Field( DocumentBuilder.CLASS_FIELDNAME, ts.getClass().getName(), Field.Store.YES, Field.Index.UN_TOKENIZED );
+		doc.add( field );
+		field = new Field("id", "1", Field.Store.YES, Field.Index.UN_TOKENIZED );
+		doc.add( field );
+		field = new Field("logo", ts.getLogo(), Field.Store.NO, Field.Index.TOKENIZED );
+		doc.add( field );
+		Work work = new AddWork(ts.getId(), ts.getClass(), doc );
+		List<Work> queue = new ArrayList<Work>();
+		queue.add( work );
+
+		//send the queue
+		InitialContext context = new InitialContext();
+		QueueConnectionFactory factory = (QueueConnectionFactory) context.lookup( "java:/ConnectionFactory" );
+		Queue jmsQueue = (Queue) context.lookup( "queue/searchtest" );
+		QueueConnection cnn;
+		QueueSender sender;
+		QueueSession session;
+		cnn = factory.createQueueConnection();
+		//TODO make transacted parameterized
+		session = cnn.createQueueSession( false, QueueSession.AUTO_ACKNOWLEDGE );
+
+		ObjectMessage message = session.createObjectMessage();
+		message.setObject( (Serializable) queue );
+
+		sender = session.createSender( jmsQueue );
+		sender.send( message );
+
+		session.close();
+		cnn.close();
+
+		//wait for the message to be processed
+		Thread.sleep( 1000 );
+
+		FullTextSession ftSess = Search.createFullTextSession( openSession( ) );
+		ftSess.getTransaction().begin();
+		QueryParser parser = new QueryParser( "id", new StopAnalyzer() );
+		Query luceneQuery = parser.parse( "logo:jboss" );
+		org.hibernate.Query query = ftSess.createFullTextQuery( luceneQuery );
+		List result = query.list();
+		assertEquals( 1, result.size() );
+		ftSess.delete( result.get( 0 ) );
+		ftSess.getTransaction().commit();
+		ftSess.close();
+	}
+
+	protected void setUp() throws Exception {
+		bootstrap = startupEmbeddedJBoss();
+		try {
+			super.setUp();
+		}
+		catch( RuntimeException e ) {
+			try {
+				shutdownEmbeddedJBoss(bootstrap);
+			}
+			catch( Exception ee ) {
+				//swallow
+			}
+			throw e;
+		}
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+		shutdownEmbeddedJBoss(bootstrap);
+	}
+
+	public static Bootstrap startupEmbeddedJBoss() {
+		try {
+			long start = System.currentTimeMillis();
+			Bootstrap bootstrap = new Bootstrap();
+			bootstrap.bootstrap();
+			bootstrap.deployResource( "jars/jms-master.jar" );
+			System.out.println("JBoss Embedded boot time: " + (System.currentTimeMillis() - start) + " ms");
+			return bootstrap;
+		}
+		catch (DeploymentException e) {
+			throw new RuntimeException( "Failed to bootstrap", e );
+		}
+	}
+
+	public static void shutdownEmbeddedJBoss(Bootstrap bootstrap) {
+		bootstrap.shutdown();
+	}
+
+
+	protected void configure(Configuration cfg) {
+		super.configure( cfg );
+		cfg.setProperty( Environment.WORKER_BACKEND, "lucene" );
+		cfg.setProperty( JMSBackendQueueProcessorFactory.JMS_CONNECTION_FACTORY, "java:/ConnectionFactory" );
+		cfg.setProperty( JMSBackendQueueProcessorFactory.JMS_QUEUE, "queue/searchtest" );
+
+	}
+
+	protected Class[] getMappings() {
+		return new Class[] {
+				TShirt.class
+		};
+	}
+}

Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MDBSearchController.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MDBSearchController.java	                        (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MDBSearchController.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,27 @@
+//$Id: $
+package org.hibernate.search.test.jms.master;
+
+import javax.ejb.MessageDriven;
+import javax.ejb.ActivationConfigProperty;
+import javax.jms.MessageListener;
+
+import org.hibernate.search.backend.impl.jms.AbstractJMSHibernateSearchController;
+import org.hibernate.Session;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at MessageDriven(activationConfig = {
+      @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
+      @ActivationConfigProperty(propertyName="destination", propertyValue="queue/searchtest"),
+      @ActivationConfigProperty(propertyName="DLQMaxResent", propertyValue="1")
+   } )
+public class MDBSearchController extends AbstractJMSHibernateSearchController implements MessageListener {
+	protected Session getSession() {
+		return MyHibernateUtil.sessionFactory.openSession( );
+	}
+
+	protected void cleanSessionIfNeeded(Session session) {
+		session.close();
+	}
+}

Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MyHibernateUtil.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MyHibernateUtil.java	                        (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/MyHibernateUtil.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,12 @@
+//$Id: $
+package org.hibernate.search.test.jms.master;
+
+import org.hibernate.SessionFactory;
+
+/**
+ * Don't do it at home ;-)
+ * @author Emmanuel Bernard
+ */
+public class MyHibernateUtil {
+	public static SessionFactory sessionFactory;
+}

Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/TShirt.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/TShirt.java	                        (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/master/TShirt.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,52 @@
+//$Id: $
+package org.hibernate.search.test.jms.master;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Table;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+ at Indexed
+ at Table(name="TShirt_Master")
+public class TShirt {
+	@Id
+	@DocumentId
+	private int id;
+	@Field(index= Index.TOKENIZED)
+	private String logo;
+	private String size;
+
+	public int getId() {
+		return id;
+	}
+
+	public void setId(int id) {
+		this.id = id;
+	}
+
+	public String getLogo() {
+		return logo;
+	}
+
+	public void setLogo(String logo) {
+		this.logo = logo;
+	}
+
+	public String getSize() {
+		return size;
+	}
+
+	public void setSize(String size) {
+		this.size = size;
+	}
+}
+

Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/JMSSlaveTest.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/JMSSlaveTest.java	                        (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/JMSSlaveTest.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,116 @@
+//$Id: $
+package org.hibernate.search.test.jms.slave;
+
+import org.jboss.embedded.Bootstrap;
+import org.jboss.deployers.spi.DeploymentException;
+import org.hibernate.search.test.SearchTestCase;
+import org.hibernate.search.Environment;
+import org.hibernate.search.backend.impl.jms.JMSBackendQueueProcessorFactory;
+import org.hibernate.Session;
+import org.hibernate.Transaction;
+import org.hibernate.cfg.Configuration;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class JMSSlaveTest extends SearchTestCase {
+
+	private Bootstrap bootstrap;
+
+
+	protected void setUp() throws Exception {
+		bootstrap = startupEmbeddedJBoss();
+		try {
+			super.setUp();
+		}
+		catch( RuntimeException e ) {
+			try {
+				shutdownEmbeddedJBoss(bootstrap);
+			}
+			catch( Exception ee ) {
+				//swallow
+			}
+			throw e;
+		}
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+		shutdownEmbeddedJBoss(bootstrap);
+	}
+
+	public void testMessageSend() throws Exception {
+		SearchQueueChecker.reset();
+		Session s = openSession();
+		Transaction tx = s.beginTransaction();
+		TShirt ts = new TShirt();
+		ts.setLogo( "Boston" );
+		ts.setSize( "XXL" );
+		TShirt ts2 = new TShirt();
+		ts2.setLogo( "Mapple leaves" );
+		ts2.setSize( "L" );
+		s.persist( ts );
+		s.persist( ts2 );
+		tx.commit();
+		//need to sleep for the message consumption
+		Thread.sleep(500);
+		assertEquals( 1, SearchQueueChecker.queues );
+		assertEquals( 2, SearchQueueChecker.works );
+
+		SearchQueueChecker.reset();
+		s = openSession();
+		tx = s.beginTransaction();
+		ts = (TShirt) s.get( TShirt.class, ts.getId() );
+		ts.setLogo( "Peter pan" );
+		tx.commit();
+		//need to sleep for the message consumption
+		Thread.sleep(500);
+		assertEquals( 1, SearchQueueChecker.queues );
+		assertEquals( 2, SearchQueueChecker.works ); //one update = 2 works
+
+		SearchQueueChecker.reset();
+		s = openSession();
+		tx = s.beginTransaction();
+		s.delete( s.get( TShirt.class, ts.getId() ) );
+		s.delete( s.get( TShirt.class, ts2.getId() ) );
+		tx.commit();
+		//Need to sleep for the message consumption
+		Thread.sleep(500);
+		assertEquals( 1, SearchQueueChecker.queues );
+		assertEquals( 2, SearchQueueChecker.works );
+		s.close();
+	}
+
+	public static Bootstrap startupEmbeddedJBoss() {
+		try {
+			long start = System.currentTimeMillis();
+			Bootstrap bootstrap = new Bootstrap();
+			bootstrap.bootstrap();
+			bootstrap.deployResource( "jars/jms-slave.jar" );
+			System.out.println("JBoss Embedded boot time: " + (System.currentTimeMillis() - start) + " ms");
+			return bootstrap;
+		}
+		catch (DeploymentException e) {
+			throw new RuntimeException( "Failed to bootstrap", e );
+		}
+	}
+
+	public static void shutdownEmbeddedJBoss(Bootstrap bootstrap) {
+		bootstrap.shutdown();
+	}
+
+
+	protected void configure(Configuration cfg) {
+		super.configure( cfg );
+		cfg.setProperty( Environment.WORKER_BACKEND, "jms" );
+		cfg.setProperty( JMSBackendQueueProcessorFactory.JMS_CONNECTION_FACTORY, "java:/ConnectionFactory" );
+		cfg.setProperty( JMSBackendQueueProcessorFactory.JMS_QUEUE, "queue/searchtest" );
+
+	}
+
+	protected Class[] getMappings() {
+		return new Class[]{
+				TShirt.class
+		};
+	}
+}

Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/SearchQueueChecker.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/SearchQueueChecker.java	                        (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/SearchQueueChecker.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,49 @@
+//$Id: $
+package org.hibernate.search.test.jms.slave;
+
+import java.util.List;
+import javax.jms.MessageListener;
+import javax.jms.Message;
+import javax.jms.ObjectMessage;
+import javax.jms.JMSException;
+import javax.ejb.MessageDriven;
+import javax.ejb.ActivationConfigProperty;
+
+import org.hibernate.search.backend.Work;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at MessageDriven(activationConfig = {
+      @ActivationConfigProperty(propertyName="destinationType", propertyValue="javax.jms.Queue"),
+      @ActivationConfigProperty(propertyName="destination", propertyValue="queue/searchtest"),
+      @ActivationConfigProperty(propertyName="DLQMaxResent", propertyValue="1")
+   } )
+public class SearchQueueChecker implements MessageListener {
+	public static int queues;
+	public static int works;
+
+	public static void reset() {
+		queues = 0;
+		works = 0;
+	}
+
+	public void onMessage(Message message) {
+		if (! (message instanceof ObjectMessage ) ) {
+			return;
+		}
+		ObjectMessage objectMessage = (ObjectMessage) message;
+		List<Work> queue;
+		try {
+			queue = (List<Work>) objectMessage.getObject();
+		}
+		catch (JMSException e) {
+			return;
+		}
+		catch( ClassCastException e ) {
+			return;
+		}
+		queues++;
+		works+=queue.size();
+	}
+}

Added: branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/TShirt.java
===================================================================
--- branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/TShirt.java	                        (rev 0)
+++ branches/Branch_3_2/HibernateExt/search/src/test/org/hibernate/search/test/jms/slave/TShirt.java	2007-02-27 02:17:25 UTC (rev 11246)
@@ -0,0 +1,50 @@
+//$Id: $
+package org.hibernate.search.test.jms.slave;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+
+import org.hibernate.search.annotations.Indexed;
+import org.hibernate.search.annotations.DocumentId;
+import org.hibernate.search.annotations.Field;
+import org.hibernate.search.annotations.Index;
+
+/**
+ * @author Emmanuel Bernard
+ */
+ at Entity
+ at Indexed
+public class TShirt {
+	@Id
+	@GeneratedValue
+	@DocumentId
+	private int id;
+	@Field(index= Index.TOKENIZED)
+	private String logo;
+	private String size;
+
+	public int getId() {
+		return id;
+	}
+
+	public void setId(int id) {
+		this.id = id;
+	}
+
+	public String getLogo() {
+		return logo;
+	}
+
+	public void setLogo(String logo) {
+		this.logo = logo;
+	}
+
+	public String getSize() {
+		return size;
+	}
+
+	public void setSize(String size) {
+		this.size = size;
+	}
+}




More information about the hibernate-commits mailing list