From hibernate-commits at lists.jboss.org Tue Apr 7 16:49:33 2009 Content-Type: multipart/mixed; boundary="===============8059118933101091560==" MIME-Version: 1.0 From: hibernate-commits at lists.jboss.org To: hibernate-commits at lists.jboss.org Subject: [hibernate-commits] Hibernate SVN: r16272 - in search/branches/Branch_3_1: src/java/org/hibernate/search/backend/impl and 4 other directories. Date: Tue, 07 Apr 2009 16:49:33 -0400 Message-ID: --===============8059118933101091560== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Author: sannegrinovero Date: 2009-04-07 16:49:32 -0400 (Tue, 07 Apr 2009) New Revision: 16272 Added: search/branches/Branch_3_1/src/java/org/hibernate/search/event/IndexWork= FlushEventListener.java search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/ search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/Bus= Line.java search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/Bus= Stop.java search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/Laz= yCollectionsUpdatingTest.java search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/Rol= lbackTransactionTest.java Modified: search/branches/Branch_3_1/doc/reference/en/modules/configuration.xml search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/Ev= entSourceTransactionContext.java search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/Tr= ansactionalWorker.java search/branches/Branch_3_1/src/java/org/hibernate/search/event/EventList= enerRegister.java search/branches/Branch_3_1/src/test/org/hibernate/search/test/TestCase.j= ava search/branches/Branch_3_1/src/test/org/hibernate/search/test/classloadi= ng/NoAnnotationsTest.java Log: HSEARCH-178 for 3.1 branch Modified: search/branches/Branch_3_1/doc/reference/en/modules/configuration= .xml =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- search/branches/Branch_3_1/doc/reference/en/modules/configuration.xml 2= 009-04-07 20:20:12 UTC (rev 16271) +++ search/branches/Branch_3_1/doc/reference/en/modules/configuration.xml 2= 009-04-07 20:49:32 UTC (rev 16272) @@ -600,7 +600,8 @@ To enable Hibernate Search in Hibernate Core (ie. if you don't= use Hibernate Annotations), add the FullTextIndexEventListener for the following six - Hibernate events. + Hibernate events and add the IndexWorkFlushEventListener after + the default DefaultFlushEventListener, as in the = following example. = Explicitly enabling Hibernate Search by configuring the @@ -609,24 +610,28 @@ <programlisting><hibernate-configuration> <session-factory> ... - <event type=3D"post-update"/> + <event type=3D"post-update"> <listener class=3D"org.hibernate.search.event.FullTextIndex= EventListener"/> </event> - <event type=3D"post-insert"/> + <event type=3D"post-insert"> <listener class=3D"org.hibernate.search.event.FullTextIndex= EventListener"/> </event> - <event type=3D"post-delete"/> + <event type=3D"post-delete"> <listener class=3D"org.hibernate.search.event.FullTextIndex= EventListener"/> </event> - <event type=3D"post-collection-recreate"/> + <event type=3D"post-collection-recreate"> <listener class=3D"org.hibernate.search.event.FullTextIndex= EventListener"/> </event> - <event type=3D"post-collection-remove"/> + <event type=3D"post-collection-remove"> <listener class=3D"org.hibernate.search.event.FullTextIndex= EventListener"/> </event> - <event type=3D"post-collection-update"/> + <event type=3D"post-collection-update"> <listener class=3D"org.hibernate.search.event.FullTextIndex= EventListener"/> </event> + <event type=3D"flush"> + <listener class=3D"org.hibernate.event.def.DefaultFlushEven= tListener"/> + <listener class=3D"org.hibernate.search.event.IndexWorkFlus= hEventListener"/> + </event> </session-factory> </hibernate-configuration></programlisting> </example> Modified: search/branches/Branch_3_1/src/java/org/hibernate/search/backend/= impl/EventSourceTransactionContext.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/E= ventSourceTransactionContext.java 2009-04-07 20:20:12 UTC (rev 16271) +++ search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/E= ventSourceTransactionContext.java 2009-04-07 20:49:32 UTC (rev 16272) @@ -2,35 +2,93 @@ package org.hibernate.search.backend.impl; = import java.io.Serializable; + import javax.transaction.Synchronization; = +import org.hibernate.AssertionFailure; import org.hibernate.Transaction; +import org.hibernate.event.EventSource; +import org.hibernate.event.FlushEventListener; import org.hibernate.search.backend.TransactionContext; -import org.hibernate.event.EventSource; +import org.hibernate.search.event.IndexWorkFlushEventListener; +import org.hibernate.search.util.LoggerFactory; +import org.slf4j.Logger; = /** * Implementation of the transactional context on top of an EventSource (S= ession) * = * @author Navin Surtani - navin(a)surtani.org + * @author Emmanuel Bernard + * @author Sanne Grinovero */ public class EventSourceTransactionContext implements TransactionContext, = Serializable { - EventSource eventSource; + = + private static final Logger log =3D LoggerFactory.make(); + = + private final EventSource eventSource; + private final IndexWorkFlushEventListener flushListener; = + //constructor time is too early to define the value of realTxInProgress, + //postpone it, otherwise doing + // " openSession - beginTransaction " + //will behave as "out of transaction" in the whole session lifespan. + private Boolean realTxInProgress =3D null; + = public EventSourceTransactionContext(EventSource eventSource) { this.eventSource =3D eventSource; + this.flushListener =3D findIndexWorkFlushEventListener(); } = public Object getTransactionIdentifier() { - return eventSource.getTransaction(); + if ( isRealTransactionInProgress() ) { + return eventSource.getTransaction(); + } + else { + return eventSource; + } } = public void registerSynchronization(Synchronization synchronization) { - Transaction transaction =3D eventSource.getTransaction(); - transaction.registerSynchronization( synchronization ); + if ( isRealTransactionInProgress() ) { + Transaction transaction =3D eventSource.getTransaction(); + transaction.registerSynchronization( synchronization ); + } + else { + if ( flushListener !=3D null ) { + flushListener.addSynchronization( eventSource, synchronization ); + } + else { + //It appears we are flushing out of transaction and have no way to per= form the index update + //Not expected: see check in isTransactionInProgress() + throw new AssertionFailure( "On flush out of transaction: IndexWorkFlu= shEventListener not registered" ); + } + } } + = + private IndexWorkFlushEventListener findIndexWorkFlushEventListener() { + FlushEventListener[] flushEventListeners =3D eventSource.getListeners().= getFlushEventListeners(); + for (FlushEventListener listener : flushEventListeners) { + if ( listener.getClass().equals( IndexWorkFlushEventListener.class ) ) { + return (IndexWorkFlushEventListener) listener; + } + } + log.debug( "No IndexWorkFlushEventListener was registered" ); + return null; + } = + //The code is not really fitting the method name; + //(unless you consider a flush as a mini-transaction) + //This is because we want to behave as "inTransaction" if the flushListen= er is registered. public boolean isTransactionInProgress() { - return eventSource.isTransactionInProgress(); + // either it is a real transaction, or if we are capable to manage this = in the IndexWorkFlushEventListener + return isRealTransactionInProgress() || flushListener !=3D null; } - + = + private boolean isRealTransactionInProgress() { + if ( realTxInProgress =3D=3D null ) { + realTxInProgress =3D eventSource.isTransactionInProgress(); + } + return realTxInProgress; + } + = } Modified: search/branches/Branch_3_1/src/java/org/hibernate/search/backend/= impl/TransactionalWorker.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/T= ransactionalWorker.java 2009-04-07 20:20:12 UTC (rev 16271) +++ search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/T= ransactionalWorker.java 2009-04-07 20:49:32 UTC (rev 16272) @@ -1,71 +1,83 @@ -//$Id$ -package org.hibernate.search.backend.impl; - -import java.util.Properties; - -import javax.transaction.Synchronization; - -import org.hibernate.search.backend.QueueingProcessor; -import org.hibernate.search.backend.Work; -import org.hibernate.search.backend.WorkQueue; -import org.hibernate.search.backend.Worker; -import org.hibernate.search.backend.TransactionContext; -import org.hibernate.search.engine.SearchFactoryImplementor; -import org.hibernate.search.util.WeakIdentityHashMap; - -/** - * Queue works per transaction. - * If out of transaction, the work is executed right away - * <p/> - * When <code>hibernate.search.worker.type</code> is set to <code>async</c= ode> - * the work is done in a separate thread (threads are pooled) - * - * @author Emmanuel Bernard - */ -public class TransactionalWorker implements Worker { - //not a synchronized map since for a given transaction, we have not concu= rrent access - protected final WeakIdentityHashMap<Object, Synchronization> synchronizat= ionPerTransaction =3D new WeakIdentityHashMap<Object, Synchronization>(); - private QueueingProcessor queueingProcessor; - - public void performWork(Work work, TransactionContext transactionContext)= { - if ( transactionContext.isTransactionInProgress() ) { - Object transaction =3D transactionContext.getTransactionIdentifier(); - PostTransactionWorkQueueSynchronization txSync =3D ( PostTransactionWor= kQueueSynchronization ) - synchronizationPerTransaction.get( transaction ); - if ( txSync =3D=3D null || txSync.isConsumed() ) { - txSync =3D new PostTransactionWorkQueueSynchronization( - queueingProcessor, synchronizationPerTransaction - ); - transactionContext.registerSynchronization( txSync ); - synchronizationPerTransaction.put( transaction, txSync ); - } - txSync.add( work ); - } - else { - WorkQueue queue =3D new WorkQueue( 2 ); //one work can be split - queueingProcessor.add( work, queue ); - queueingProcessor.prepareWorks( queue ); - queueingProcessor.performWorks( queue ); - } - } - - public void initialize(Properties props, SearchFactoryImplementor searchF= actory) { - this.queueingProcessor =3D new BatchedQueueingProcessor( searchFactory, = props ); - } - - public void close() { - queueingProcessor.close(); - } - - public void flushWorks(TransactionContext transactionContext) { - if ( transactionContext.isTransactionInProgress() ) { - Object transaction =3D transactionContext.getTransactionIdentifier(); - PostTransactionWorkQueueSynchronization txSync =3D ( PostTransactionWor= kQueueSynchronization ) - synchronizationPerTransaction.get( transaction ); - if ( txSync !=3D null && !txSync.isConsumed() ) { - txSync.flushWorks(); - } - } - } - -} +//$Id$ +package org.hibernate.search.backend.impl; + +import java.util.Properties; + +import javax.transaction.Synchronization; + +import org.hibernate.search.backend.QueueingProcessor; +import org.hibernate.search.backend.Work; +import org.hibernate.search.backend.WorkQueue; +import org.hibernate.search.backend.Worker; +import org.hibernate.search.backend.TransactionContext; +import org.hibernate.search.engine.SearchFactoryImplementor; +import org.hibernate.search.util.LoggerFactory; +import org.hibernate.search.util.WeakIdentityHashMap; +import org.slf4j.Logger; + +/** + * Queue works per transaction. + * If out of transaction, the work is executed right away + * <p/> + * When <code>hibernate.search.worker.type</code> is set to <code>async</c= ode> + * the work is done in a separate thread (threads are pooled) + * = + * @author Emmanuel Bernard + */ +public class TransactionalWorker implements Worker { + = + //note: there is one Worker instance per SearchFactory, reused concurrent= ly for all sessions. + = + private static final Logger log =3D LoggerFactory.make(); + = + //FIXME: discuss the next line! it looks like there actually is concurren= t access + //not a synchronized map since for a given transaction, we have not concu= rrent access + protected final WeakIdentityHashMap<Object, Synchronization> synchronizat= ionPerTransaction =3D new WeakIdentityHashMap<Object, Synchronization>(); + private QueueingProcessor queueingProcessor; + + public void performWork(Work work, TransactionContext transactionContext)= { + if ( transactionContext.isTransactionInProgress() ) { + Object transactionIdentifier =3D transactionContext.getTransactionIdent= ifier(); + PostTransactionWorkQueueSynchronization txSync =3D ( PostTransactionWor= kQueueSynchronization ) + synchronizationPerTransaction.get( transactionIdentifier ); + if ( txSync =3D=3D null || txSync.isConsumed() ) { + txSync =3D new PostTransactionWorkQueueSynchronization( + queueingProcessor, synchronizationPerTransaction + ); + transactionContext.registerSynchronization( txSync ); + synchronizationPerTransaction.put( transactionIdentifier, txSync ); + } + txSync.add( work ); + } + else { + // this is a workaround: isTransactionInProgress should return "true" + // for correct configurations. + log.warn( "It appears changes are being pushed to the index out of a tr= ansaction. " + + "Register the IndexWorkFlushEventListener listener on flush to correc= tly manage Collections!" ); + WorkQueue queue =3D new WorkQueue( 2 ); //one work can be split + queueingProcessor.add( work, queue ); + queueingProcessor.prepareWorks( queue ); + queueingProcessor.performWorks( queue ); + } + } + + public void initialize(Properties props, SearchFactoryImplementor searchF= actory) { + this.queueingProcessor =3D new BatchedQueueingProcessor( searchFactory, = props ); + } + + public void close() { + queueingProcessor.close(); + } + + public void flushWorks(TransactionContext transactionContext) { + if ( transactionContext.isTransactionInProgress() ) { + Object transaction =3D transactionContext.getTransactionIdentifier(); + PostTransactionWorkQueueSynchronization txSync =3D ( PostTransactionWor= kQueueSynchronization ) + synchronizationPerTransaction.get( transaction ); + if ( txSync !=3D null && !txSync.isConsumed() ) { + txSync.flushWorks(); + } + } + } + +} Modified: search/branches/Branch_3_1/src/java/org/hibernate/search/event/Ev= entListenerRegister.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- search/branches/Branch_3_1/src/java/org/hibernate/search/event/EventLis= tenerRegister.java 2009-04-07 20:20:12 UTC (rev 16271) +++ search/branches/Branch_3_1/src/java/org/hibernate/search/event/EventLis= tenerRegister.java 2009-04-07 20:49:32 UTC (rev 16272) @@ -94,7 +94,13 @@ new PostCollectionUpdateEventListener[] { searchListener } ) ); - + // Adding IndexWorkFlushEventListener to manage events out-of-transaction + if ( ! isFlushEventListenerRegistered( listeners.getFlushEventListeners(= ) ) ) { + listeners.setFlushEventListeners( appendToArray( + listeners.getFlushEventListeners(), + new IndexWorkFlushEventListener() + ) ); + } } = /** @@ -159,4 +165,19 @@ } return false; } + = + /** + * Verifies if an IndexWorkFlushEventListener is contained in the array o= f listeners. + * @param listeners + * @return true if it found in the listeners, false otherwise. + */ + private static boolean isFlushEventListenerRegistered(Object[] listeners)= { + for ( Object eventListener : listeners ) { + if ( IndexWorkFlushEventListener.class =3D=3D eventListener.getClass() = ) { + return true; + } + } + return false; + } + = } Added: search/branches/Branch_3_1/src/java/org/hibernate/search/event/Index= WorkFlushEventListener.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- search/branches/Branch_3_1/src/java/org/hibernate/search/event/IndexWor= kFlushEventListener.java (rev 0) +++ search/branches/Branch_3_1/src/java/org/hibernate/search/event/IndexWor= kFlushEventListener.java 2009-04-07 20:49:32 UTC (rev 16272) @@ -0,0 +1,61 @@ +// $Id$ +package org.hibernate.search.event; + +import java.io.Serializable; +import java.util.concurrent.ConcurrentHashMap; + +import javax.transaction.Status; +import javax.transaction.Synchronization; + +import org.hibernate.AssertionFailure; +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.event.EventSource; +import org.hibernate.event.FlushEvent; +import org.hibernate.event.FlushEventListener; +import org.hibernate.search.util.LoggerFactory; +import org.slf4j.Logger; + +/** + * FlushEventListener to make sure the indexes are updated right after the= hibernate flush, + * avoiding object loading during a flush. Not needed during transactions. + * = + * @author Sanne Grinovero + */ +public final class IndexWorkFlushEventListener implements FlushEventListen= er, Serializable { + = + private static final Logger log =3D LoggerFactory.make(); + = + private final ConcurrentHashMap<Session, Synchronization> synchronization= PerTransaction + =3D new ConcurrentHashMap<Session, Synchronization>(); + = + public IndexWorkFlushEventListener() { + } + + public void onFlush(FlushEvent event) throws HibernateException { + Session session =3D event.getSession(); + Synchronization synchronization =3D synchronizationPerTransaction.get( s= ession ); + if ( synchronization !=3D null ) { + log.debug( "flush event causing index update out of transaction" ); + synchronizationPerTransaction.remove( session ); + synchronization.beforeCompletion(); + synchronization.afterCompletion( Status.STATUS_COMMITTED ); + } + } + + public void addSynchronization(EventSource eventSource, Synchronization s= ynchronization) { + Synchronization previousSync =3D synchronizationPerTransaction.put( even= tSource, synchronization ); + if ( previousSync !=3D null ) { + throw new AssertionFailure( "previous registered sync not discarded in = IndexWorkFlushEventListener" ); + } + } + + /* + * Might want to implement AutoFlushEventListener in future? + public void onAutoFlush(AutoFlushEvent event) throws HibernateException { + // Currently not needed as auto-flush is not happening + // when out of transaction. + } + */ + +} Property changes on: search/branches/Branch_3_1/src/java/org/hibernate/sear= ch/event/IndexWorkFlushEventListener.java ___________________________________________________________________ Name: svn:keywords + Id Modified: search/branches/Branch_3_1/src/test/org/hibernate/search/test/Tes= tCase.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- search/branches/Branch_3_1/src/test/org/hibernate/search/test/TestCase.= java 2009-04-07 20:20:12 UTC (rev 16271) +++ search/branches/Branch_3_1/src/test/org/hibernate/search/test/TestCase.= java 2009-04-07 20:49:32 UTC (rev 16272) @@ -13,6 +13,9 @@ import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; +import org.hibernate.event.FlushEventListener; +import org.hibernate.event.def.DefaultFlushEventListener; +import org.hibernate.search.event.IndexWorkFlushEventListener; = /** * A modified base class for tests without annotations. @@ -143,9 +146,15 @@ } = protected void configure(Configuration cfg) { + //needs to register all event listeners: cfg.setListener( "post-update", "org.hibernate.search.event.FullTextInde= xEventListener" ); cfg.setListener( "post-insert", "org.hibernate.search.event.FullTextInde= xEventListener" ); cfg.setListener( "post-delete", "org.hibernate.search.event.FullTextInde= xEventListener" ); + cfg.setListener( "post-collection-recreate", "org.hibernate.search.event= .FullTextIndexEventListener" ); + cfg.setListener( "post-collection-remove", "org.hibernate.search.event.F= ullTextIndexEventListener" ); + cfg.setListener( "post-collection-update", "org.hibernate.search.event.F= ullTextIndexEventListener" ); + = + cfg.setListeners( "flush", new FlushEventListener[]{new DefaultFlushEven= tListener(), new IndexWorkFlushEventListener()} ); = cfg.setProperty( "hibernate.search.default.directory_provider", RAMDirec= toryProvider.class.getName() ); cfg.setProperty( org.hibernate.search.Environment.ANALYZER_CLASS, StopAn= alyzer.class.getName() ); Modified: search/branches/Branch_3_1/src/test/org/hibernate/search/test/cla= ssloading/NoAnnotationsTest.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- search/branches/Branch_3_1/src/test/org/hibernate/search/test/classload= ing/NoAnnotationsTest.java 2009-04-07 20:20:12 UTC (rev 16271) +++ search/branches/Branch_3_1/src/test/org/hibernate/search/test/classload= ing/NoAnnotationsTest.java 2009-04-07 20:49:32 UTC (rev 16272) @@ -10,7 +10,6 @@ import org.hibernate.Transaction; import org.hibernate.search.Search; = - /** * @author Hardy Ferentschik */ @@ -39,7 +38,28 @@ tx.commit(); s.close(); } + = + public void testFlushListenerRegistrationWithoutAnnotations() throws Exce= ption { + // This test should pass even if the flushListener is not registered, + // as a workaround is done in code (you'll see a warning in logs). + Animal pinguin =3D new Animal(); + pinguin.setName( "Penguin" ); = + Session s =3D openSession(); + s.save( pinguin ); + s.flush(); + s.clear(); + + Transaction tx =3D s.beginTransaction(); + tx =3D s.beginTransaction(); + List results =3D Search.getFullTextSession( s ).createFullTextQuery( + new TermQuery( new Term( "name", "penguin" ) ) + ).list(); + assertEquals( 1, results.size() ); + tx.commit(); + s.close(); + } + protected String[] getXmlFiles() { return new String[] { "org/hibernate/search/test/classloading/Animal.hbm.xml" Added: search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine= /BusLine.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/Bu= sLine.java (rev 0) +++ search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/Bu= sLine.java 2009-04-07 20:49:32 UTC (rev 16272) @@ -0,0 +1,87 @@ +// $Id$ +package org.hibernate.search.test.engine; + +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToMany; + +import org.hibernate.search.annotations.Field; +import org.hibernate.search.annotations.Index; +import org.hibernate.search.annotations.Indexed; +import org.hibernate.search.annotations.IndexedEmbedded; +import org.hibernate.search.annotations.Store; + +/** + * Test entity: BusLine have many BusStops: needed to verify + * indexing of a lazy-loaded collection in out-of-transaction use case. + * = + * @author Sanne Grinovero + */ +(a)Entity +(a)Indexed +public class BusLine { + = + private Long id; + private String busLineName; + private Set<BusStop> stops =3D new HashSet<BusStop>(); + + @Id + @GeneratedValue = + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id =3D id; + } + + @Field(index=3DIndex.NO,store=3DStore.YES) + public String getBusLineName() { + return busLineName; + } + + public void setBusLineName(String busLine) { + this.busLineName =3D busLine; + } + + @ManyToMany(cascade=3DCascadeType.PERSIST) + @IndexedEmbedded + public Set<BusStop> getStops() { + return stops; + } + = + public void setStops(Set<BusStop> stops) { + this.stops =3D stops; + } + + @Override + public int hashCode() { + final int prime =3D 31; + int result =3D 1; + result =3D prime * result + ((busLineName =3D=3D null) ? 0 : busLineName= .hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this =3D=3D obj) + return true; + if (obj =3D=3D null) + return false; + if (getClass() !=3D obj.getClass()) + return false; + BusLine other =3D (BusLine) obj; + if (busLineName =3D=3D null) { + if (other.busLineName !=3D null) + return false; + } else if (!busLineName.equals(other.busLineName)) + return false; + return true; + } + = +} Added: search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine= /BusStop.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/Bu= sStop.java (rev 0) +++ search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/Bu= sStop.java 2009-04-07 20:49:32 UTC (rev 16272) @@ -0,0 +1,82 @@ +// $Id$ +package org.hibernate.search.test.engine; + +import java.util.HashSet; +import java.util.Set; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToMany; + +import org.hibernate.search.annotations.ContainedIn; +import org.hibernate.search.annotations.Field; + +/** + * Test entity: BusStop is @ContainedIn BusLine + * = + * @author Sanne Grinovero + */ +(a)Entity +public class BusStop { + = + private Long id; + private String roadName; + private Set<BusLine> busses =3D new HashSet<BusLine>(); + + @Id + @GeneratedValue + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id =3D id; + } + + @Field + public String getRoadName() { + return roadName; + } + + public void setRoadName(String roadName) { + this.roadName =3D roadName; + } + + @ManyToMany(mappedBy=3D"stops") + @ContainedIn + public Set<BusLine> getBusses() { + return busses; + } + + public void setBusses(Set<BusLine> busses) { + this.busses =3D busses; + } + + @Override + public int hashCode() { + final int prime =3D 31; + int result =3D 1; + result =3D prime * result + + ((roadName =3D=3D null) ? 0 : roadName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this =3D=3D obj) + return true; + if (obj =3D=3D null) + return false; + if (getClass() !=3D obj.getClass()) + return false; + BusStop other =3D (BusStop) obj; + if (roadName =3D=3D null) { + if (other.roadName !=3D null) + return false; + } else if (!roadName.equals(other.roadName)) + return false; + return true; + } + = +} Property changes on: search/branches/Branch_3_1/src/test/org/hibernate/sear= ch/test/engine/BusStop.java ___________________________________________________________________ Name: svn:keywords + Id Added: search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine= /LazyCollectionsUpdatingTest.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/La= zyCollectionsUpdatingTest.java (rev 0) +++ search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/La= zyCollectionsUpdatingTest.java 2009-04-07 20:49:32 UTC (rev 16272) @@ -0,0 +1,119 @@ +// $Id$ +package org.hibernate.search.test.engine; + +import java.util.List; + +import org.apache.lucene.analysis.SimpleAnalyzer; +import org.apache.lucene.index.Term; +import org.apache.lucene.search.TermQuery; +import org.hibernate.Transaction; +import org.hibernate.search.Environment; +import org.hibernate.search.FullTextQuery; +import org.hibernate.search.FullTextSession; +import org.hibernate.search.Search; +import org.hibernate.search.store.RAMDirectoryProvider; +import org.hibernate.search.test.SearchTestCase; + +/** + * TestCase for HSEARCH-178 (Search hitting HHH-2763) + * Verifies that it's possible to index lazy loaded collections from + * indexed entities even when no transactions are used. + * + * @author Sanne Grinovero + */ +public class LazyCollectionsUpdatingTest extends SearchTestCase { + = + public void testUpdatingInTransaction() { + assertFindsByRoadName( "buonarroti" ); + FullTextSession fullTextSession =3D Search.getFullTextSession( sessions.= openSession() ); + try { + Transaction tx =3D fullTextSession.beginTransaction(); + BusStop busStop =3D (BusStop) fullTextSession.get( BusStop.class, 1L ); + busStop.setRoadName( "new road" ); + tx.commit(); + } + catch (org.hibernate.AssertionFailure ass) { + fail( ass.getMessage() ); + } + finally { + fullTextSession.close(); + } + assertFindsByRoadName( "new" ); + } + = + public void testUpdatingOutOfTransaction() { + assertFindsByRoadName( "buonarroti" ); + FullTextSession fullTextSession =3D Search.getFullTextSession( sessions.= openSession() ); + try { + BusStop busStop =3D (BusStop) fullTextSession.get( BusStop.class, 1L ); + busStop.setRoadName( "new road" ); + fullTextSession.flush(); + } + catch (org.hibernate.AssertionFailure ass) { + fail( ass.getMessage() ); + } + finally { + fullTextSession.close(); + } + assertFindsByRoadName( "new" ); + } + = + public void assertFindsByRoadName(String analyzedRoadname) { + FullTextSession fullTextSession =3D Search.getFullTextSession( sessions.= openSession() ); + Transaction tx =3D fullTextSession.beginTransaction(); + TermQuery ftQuery =3D new TermQuery( new Term( "stops.roadName", analyze= dRoadname ) ); + FullTextQuery query =3D fullTextSession.createFullTextQuery( ftQuery, Bu= sLine.class ); + query.setProjection( "busLineName" ); + assertEquals( 1, query.list().size() ); + List results =3D query.list(); + String resultName =3D (String) ((Object[])results.get(0))[0]; + assertEquals( "Linea 64", resultName ); + tx.commit(); + fullTextSession.close(); + } + = + @Override + public void setUp() throws Exception { + super.setUp(); + openSession(); + Transaction tx =3D null; + try { + tx =3D session.beginTransaction(); + BusLine bus =3D new BusLine(); + bus.setBusLineName( "Linea 64" ); + addBusStop( bus, "Stazione Termini" ); + addBusStop( bus, "via Gregorio VII" ); + addBusStop( bus, "via Alessandro III" ); + addBusStop( bus, "via M.Buonarroti" ); + session.persist( bus ); + tx.commit(); + } catch (Throwable t) { + if ( tx !=3D null ) + tx.rollback(); + } finally { + session.close(); + } + } + = + private void addBusStop(BusLine bus, String roadName) { + BusStop stop =3D new BusStop(); + stop.setRoadName( roadName ); + bus.getStops().add( stop ); + stop.getBusses().add( bus ); + } + + // Test setup options - Entities + @Override + protected Class[] getMappings() { + return new Class[] { BusLine.class, BusStop.class }; + } + = + // Test setup options - SessionFactory Properties + @Override + protected void configure(org.hibernate.cfg.Configuration configuration) { + super.configure( configuration ); + cfg.setProperty( "hibernate.search.default.directory_provider", RAMDirec= toryProvider.class.getName() ); + cfg.setProperty( Environment.ANALYZER_CLASS, SimpleAnalyzer.class.getNam= e() ); + } + +} Property changes on: search/branches/Branch_3_1/src/test/org/hibernate/sear= ch/test/engine/LazyCollectionsUpdatingTest.java ___________________________________________________________________ Name: svn:keywords + Id Added: search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine= /RollbackTransactionTest.java =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/Ro= llbackTransactionTest.java (rev 0) +++ search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/Ro= llbackTransactionTest.java 2009-04-07 20:49:32 UTC (rev 16272) @@ -0,0 +1,90 @@ +// $Id$ +package org.hibernate.search.test.engine; + +import org.apache.lucene.analysis.SimpleAnalyzer; +import org.apache.lucene.search.MatchAllDocsQuery; +import org.hibernate.Transaction; +import org.hibernate.search.Environment; +import org.hibernate.search.FullTextQuery; +import org.hibernate.search.FullTextSession; +import org.hibernate.search.Search; +import org.hibernate.search.store.RAMDirectoryProvider; +import org.hibernate.search.test.SearchTestCase; + +/** + * Verify index changes queued during a transaction are canceled + * when the transaction is rolled back. + * = + * @author Sanne Grinovero + */ +public class RollbackTransactionTest extends SearchTestCase { + = + public void testTransactionBehaviour() { + assertEquals( 0, countBusLinesByFullText() ); + assertEquals( 0, countBusLineByDatabaseCount() ); + createBusLines( 5, true ); + assertEquals( 0, countBusLinesByFullText() ); + assertEquals( 0, countBusLineByDatabaseCount() ); + createBusLines( 5, false ); + assertEquals( 5, countBusLinesByFullText() ); + assertEquals( 5, countBusLineByDatabaseCount() ); + createBusLines( 7, true ); + assertEquals( 5, countBusLinesByFullText() ); + assertEquals( 5, countBusLineByDatabaseCount() ); + createBusLines( 7, false ); + assertEquals( 12, countBusLinesByFullText() ); + assertEquals( 12, countBusLineByDatabaseCount() ); + } + = + private void createBusLines(int number, boolean rollback) { + FullTextSession fullTextSession =3D Search.getFullTextSession( sessions.= openSession() ); + Transaction tx =3D fullTextSession.beginTransaction(); + for (int i=3D0; i<number; i++ ) { + BusLine line =3D new BusLine(); + line.setBusLineName( "line " + i ); + fullTextSession.persist( line ); + } + if ( rollback ) { + tx.rollback(); + } + else { + tx.commit(); + } + fullTextSession.close(); + } + + public int countBusLinesByFullText() { + FullTextSession fullTextSession =3D Search.getFullTextSession( sessions.= openSession() ); + Transaction tx =3D fullTextSession.beginTransaction(); + org.apache.lucene.search.Query ftQuery =3D new MatchAllDocsQuery(); + FullTextQuery query =3D fullTextSession.createFullTextQuery( ftQuery, Bu= sLine.class ); + int count =3D query.list().size(); + tx.commit(); + fullTextSession.close(); + return count; + } + = + public int countBusLineByDatabaseCount() { + FullTextSession fullTextSession =3D Search.getFullTextSession( sessions.= openSession() ); + Transaction tx =3D fullTextSession.beginTransaction(); + int count =3D fullTextSession.createCriteria( BusLine.class ).list().siz= e(); + tx.commit(); + fullTextSession.close(); + return count; + } + = + // Test setup options - Entities + @Override + protected Class[] getMappings() { + return new Class[] { BusLine.class, BusStop.class }; + } + = + // Test setup options - SessionFactory Properties + @Override + protected void configure(org.hibernate.cfg.Configuration configuration) { + super.configure( configuration ); + cfg.setProperty( "hibernate.search.default.directory_provider", RAMDirec= toryProvider.class.getName() ); + cfg.setProperty( Environment.ANALYZER_CLASS, SimpleAnalyzer.class.getNam= e() ); + } + +} Property changes on: search/branches/Branch_3_1/src/test/org/hibernate/sear= ch/test/engine/RollbackTransactionTest.java ___________________________________________________________________ Name: svn:keywords + Id --===============8059118933101091560==--