[hibernate-commits] Hibernate SVN: r16392 - in search/branches/Branch_3_1: src/java/org/hibernate/search/backend/impl and 3 other directories.
hibernate-commits at lists.jboss.org
hibernate-commits at lists.jboss.org
Wed Apr 22 02:52:05 EDT 2009
Author: sannegrinovero
Date: 2009-04-22 02:52:05 -0400 (Wed, 22 Apr 2009)
New Revision: 16392
Added:
search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/EventListenerSerializationTest.java
Removed:
search/branches/Branch_3_1/src/java/org/hibernate/search/event/IndexWorkFlushEventListener.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/EventSourceTransactionContext.java
search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/PostTransactionWorkQueueSynchronization.java
search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/TransactionalWorker.java
search/branches/Branch_3_1/src/java/org/hibernate/search/event/EventListenerRegister.java
search/branches/Branch_3_1/src/java/org/hibernate/search/event/FullTextIndexEventListener.java
search/branches/Branch_3_1/src/test/org/hibernate/search/test/TestCase.java
Log:
merging changes for HSEARCH-178 from trunk to branch 3.1
Modified: search/branches/Branch_3_1/doc/reference/en/modules/configuration.xml
===================================================================
--- search/branches/Branch_3_1/doc/reference/en/modules/configuration.xml 2009-04-21 20:24:58 UTC (rev 16391)
+++ search/branches/Branch_3_1/doc/reference/en/modules/configuration.xml 2009-04-22 06:52:05 UTC (rev 16392)
@@ -600,8 +600,8 @@
<para>To enable Hibernate Search in Hibernate Core (ie. if you don't use
Hibernate Annotations), add the
<literal>FullTextIndexEventListener</literal> for the following six
- Hibernate events and add the <literal>IndexWorkFlushEventListener</literal> after
- the default <literal>DefaultFlushEventListener</literal>, as in the following example.</para>
+ Hibernate events and also add it after the default
+ <literal>DefaultFlushEventListener</literal>, as in the following example.</para>
<example>
<title>Explicitly enabling Hibernate Search by configuring the
@@ -630,7 +630,7 @@
</event>
<event type="flush">
<listener class="org.hibernate.event.def.DefaultFlushEventListener"/>
- <listener class="org.hibernate.search.event.IndexWorkFlushEventListener"/>
+ <listener class="org.hibernate.search.event.FullTextIndexEventListener"/>
</event>
</session-factory>
</hibernate-configuration></programlisting>
Modified: search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/EventSourceTransactionContext.java
===================================================================
--- search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/EventSourceTransactionContext.java 2009-04-21 20:24:58 UTC (rev 16391)
+++ search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/EventSourceTransactionContext.java 2009-04-22 06:52:05 UTC (rev 16392)
@@ -5,12 +5,12 @@
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.SearchException;
import org.hibernate.search.backend.TransactionContext;
-import org.hibernate.search.event.IndexWorkFlushEventListener;
+import org.hibernate.search.event.FullTextIndexEventListener;
import org.hibernate.search.util.LoggerFactory;
import org.slf4j.Logger;
@@ -26,7 +26,9 @@
private static final Logger log = LoggerFactory.make();
private final EventSource eventSource;
- private final IndexWorkFlushEventListener flushListener;
+
+ //this transient is required to break recursive serialization
+ private transient FullTextIndexEventListener flushListener;
//constructor time is too early to define the value of realTxInProgress,
//postpone it, otherwise doing
@@ -36,7 +38,7 @@
public EventSourceTransactionContext(EventSource eventSource) {
this.eventSource = eventSource;
- this.flushListener = findIndexWorkFlushEventListener();
+ this.flushListener = getIndexWorkFlushEventListener();
}
public Object getTransactionIdentifier() {
@@ -54,25 +56,32 @@
transaction.registerSynchronization( synchronization );
}
else {
+ //registerSynchronization is only called if isRealTransactionInProgress or if
+ // a flushListener was found; still we might need to find the listener again
+ // as it might have been cleared by serialization (is transient).
+ flushListener = getIndexWorkFlushEventListener();
if ( flushListener != null ) {
flushListener.addSynchronization( eventSource, synchronization );
}
else {
- //It appears we are flushing out of transaction and have no way to perform the index update
- //Not expected: see check in isTransactionInProgress()
- throw new AssertionFailure( "On flush out of transaction: IndexWorkFlushEventListener not registered" );
+ //shouldn't happen if the code about serialization is fine:
+ throw new SearchException( "AssertionFailure: flushListener not registered any more.");
}
}
}
- private IndexWorkFlushEventListener findIndexWorkFlushEventListener() {
+ private FullTextIndexEventListener getIndexWorkFlushEventListener() {
+ if ( this.flushListener != null) {
+ //for the "transient" case: might have been nullified.
+ return flushListener;
+ }
FlushEventListener[] flushEventListeners = eventSource.getListeners().getFlushEventListeners();
for (FlushEventListener listener : flushEventListeners) {
- if ( listener.getClass().equals( IndexWorkFlushEventListener.class ) ) {
- return (IndexWorkFlushEventListener) listener;
+ if ( listener.getClass().equals( FullTextIndexEventListener.class ) ) {
+ return (FullTextIndexEventListener) listener;
}
}
- log.debug( "No IndexWorkFlushEventListener was registered" );
+ log.debug( "No FullTextIndexEventListener was registered" );
return null;
}
@@ -81,7 +90,7 @@
//This is because we want to behave as "inTransaction" if the flushListener is registered.
public boolean isTransactionInProgress() {
// either it is a real transaction, or if we are capable to manage this in the IndexWorkFlushEventListener
- return isRealTransactionInProgress() || flushListener != null;
+ return getIndexWorkFlushEventListener() != null || isRealTransactionInProgress();
}
private boolean isRealTransactionInProgress() {
Modified: search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/PostTransactionWorkQueueSynchronization.java
===================================================================
--- search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/PostTransactionWorkQueueSynchronization.java 2009-04-21 20:24:58 UTC (rev 16391)
+++ search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/PostTransactionWorkQueueSynchronization.java 2009-04-22 06:52:05 UTC (rev 16392)
@@ -15,6 +15,13 @@
* @author Emmanuel Bernard
*/
public class PostTransactionWorkQueueSynchronization implements Synchronization {
+
+ /**
+ * FullTextIndexEventListener is using a WeakIdentityHashMap<Session,Synchronization>
+ * So make sure all Synchronization implementations don't have any
+ * (direct or indirect) reference to the Session.
+ */
+
private final QueueingProcessor queueingProcessor;
private boolean consumed;
private final WeakIdentityHashMap queuePerTransaction;
Modified: search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/TransactionalWorker.java
===================================================================
--- search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/TransactionalWorker.java 2009-04-21 20:24:58 UTC (rev 16391)
+++ search/branches/Branch_3_1/src/java/org/hibernate/search/backend/impl/TransactionalWorker.java 2009-04-22 06:52:05 UTC (rev 16392)
@@ -30,8 +30,8 @@
private static final Logger log = LoggerFactory.make();
- //FIXME: discuss the next line! it looks like there actually is concurrent access
- //not a synchronized map since for a given transaction, we have not concurrent access
+ //this is being used from different threads, but doesn't need a
+ //synchronized map since for a given transaction, we have not concurrent access
protected final WeakIdentityHashMap<Object, Synchronization> synchronizationPerTransaction = new WeakIdentityHashMap<Object, Synchronization>();
private QueueingProcessor queueingProcessor;
Modified: search/branches/Branch_3_1/src/java/org/hibernate/search/event/EventListenerRegister.java
===================================================================
--- search/branches/Branch_3_1/src/java/org/hibernate/search/event/EventListenerRegister.java 2009-04-21 20:24:58 UTC (rev 16391)
+++ search/branches/Branch_3_1/src/java/org/hibernate/search/event/EventListenerRegister.java 2009-04-22 06:52:05 UTC (rev 16392)
@@ -6,6 +6,7 @@
import org.slf4j.Logger;
import org.hibernate.event.EventListeners;
+import org.hibernate.event.FlushEventListener;
import org.hibernate.event.PostCollectionRecreateEventListener;
import org.hibernate.event.PostCollectionRemoveEventListener;
import org.hibernate.event.PostCollectionUpdateEventListener;
@@ -15,7 +16,6 @@
import org.hibernate.search.Environment;
import org.hibernate.search.util.LoggerFactory;
-
/**
* Helper methods initializing Hibernate Search event listeners.
*
@@ -94,13 +94,14 @@
new PostCollectionUpdateEventListener[] { searchListener }
)
);
- // Adding IndexWorkFlushEventListener to manage events out-of-transaction
- if ( ! isFlushEventListenerRegistered( listeners.getFlushEventListeners() ) ) {
- listeners.setFlushEventListeners( appendToArray(
- listeners.getFlushEventListeners(),
- new IndexWorkFlushEventListener()
- ) );
- }
+ // Adding also as FlushEventListener to manage events out-of-transaction
+ listeners.setFlushEventListeners(
+ addIfNeeded(
+ listeners.getFlushEventListeners(),
+ searchListener,
+ new FlushEventListener[] { searchListener }
+ )
+ );
}
/**
@@ -166,18 +167,4 @@
return false;
}
- /**
- * Verifies if an IndexWorkFlushEventListener is contained in the array of 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 == eventListener.getClass() ) {
- return true;
- }
- }
- return false;
- }
-
}
Modified: search/branches/Branch_3_1/src/java/org/hibernate/search/event/FullTextIndexEventListener.java
===================================================================
--- search/branches/Branch_3_1/src/java/org/hibernate/search/event/FullTextIndexEventListener.java 2009-04-21 20:24:58 UTC (rev 16391)
+++ search/branches/Branch_3_1/src/java/org/hibernate/search/event/FullTextIndexEventListener.java 2009-04-22 06:52:05 UTC (rev 16392)
@@ -1,15 +1,27 @@
//$Id$
package org.hibernate.search.event;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.Map;
+import javax.transaction.Status;
+import javax.transaction.Synchronization;
+
import org.slf4j.Logger;
+import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.engine.EntityEntry;
import org.hibernate.event.AbstractCollectionEvent;
import org.hibernate.event.AbstractEvent;
import org.hibernate.event.Destructible;
+import org.hibernate.event.EventSource;
+import org.hibernate.event.FlushEvent;
+import org.hibernate.event.FlushEventListener;
import org.hibernate.event.Initializable;
import org.hibernate.event.PostCollectionRecreateEvent;
import org.hibernate.event.PostCollectionRecreateEventListener;
@@ -28,6 +40,7 @@
import org.hibernate.search.backend.impl.EventSourceTransactionContext;
import org.hibernate.search.engine.SearchFactoryImplementor;
import org.hibernate.search.util.LoggerFactory;
+import org.hibernate.search.util.WeakIdentityHashMap;
/**
* This listener supports setting a parent directory for all generated index files.
@@ -36,20 +49,27 @@
* @author Gavin King
* @author Emmanuel Bernard
* @author Mattias Arbin
+ * @author Sanne Grinovero
*/
-//TODO work on sharing the same indexWriters and readers across a single post operation...
//TODO implement and use a LockableDirectoryProvider that wraps a DP to handle the lock inside the LDP
//TODO make this class final as soon as FullTextIndexCollectionEventListener is removed.
@SuppressWarnings( "serial" )
public class FullTextIndexEventListener implements PostDeleteEventListener,
PostInsertEventListener, PostUpdateEventListener,
PostCollectionRecreateEventListener, PostCollectionRemoveEventListener,
- PostCollectionUpdateEventListener, Initializable, Destructible {
+ PostCollectionUpdateEventListener, FlushEventListener, Initializable, Destructible {
private static final Logger log = LoggerFactory.make();
protected boolean used;
protected SearchFactoryImplementor searchFactoryImplementor;
+
+ //only used by the FullTextIndexEventListener instance playing in the FlushEventListener role.
+ // transient because it's not serializable (and state doesn't need to live longer than a flush).
+ // final because it's initialization should be published to other threads.
+ // ! update the readObject() method in case of name changes !
+ // make sure the Synchronization doesn't contain references to Session, otherwise we'll leak memory.
+ private transient final Map<Session,Synchronization> flushSynch = new WeakIdentityHashMap<Session,Synchronization>(0);
/**
* Initialize method called by Hibernate Core when the SessionFactory starts
@@ -157,4 +177,59 @@
}
return id;
}
+
+ /**
+ * Make sure the indexes are updated right after the hibernate flush,
+ * avoiding object loading during a flush. Not needed during transactions.
+ */
+ public void onFlush(FlushEvent event) {
+ if ( used ) {
+ Session session = event.getSession();
+ Synchronization synchronization = flushSynch.get( session );
+ if ( synchronization != null ) {
+ //first cleanup
+ flushSynch.remove( session );
+ log.debug( "flush event causing index update out of transaction" );
+ synchronization.beforeCompletion();
+ synchronization.afterCompletion( Status.STATUS_COMMITTED );
+ }
+ }
+ }
+
+ /**
+ * Adds a synchronization to be performed in the onFlush method;
+ * should only be used as workaround for the case a flush is happening
+ * out of transaction.
+ * Warning: if the synchronization contains a hard reference
+ * to the Session proper cleanup is not guaranteed and memory leaks
+ * will happen.
+ * @param eventSource should be the Session doing the flush
+ * @param synchronization
+ */
+ public void addSynchronization(EventSource eventSource, Synchronization synchronization) {
+ this.flushSynch.put( eventSource, synchronization );
+ }
+
+ /* 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.
+ }
+ */
+
+ private void writeObject(ObjectOutputStream os) throws IOException {
+ os.defaultWriteObject();
+ }
+
+ //needs to implement custom readObject to restore the transient fields
+ private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException, SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
+ is.defaultReadObject();
+ Class<FullTextIndexEventListener> cl = FullTextIndexEventListener.class;
+ Field f = cl.getDeclaredField("flushSynch");
+ f.setAccessible( true );
+ Map<Session,Synchronization> flushSynch = new WeakIdentityHashMap<Session,Synchronization>(0);
+ // setting a final field by reflection during a readObject is considered as safe as in a constructor:
+ f.set( this, flushSynch );
+ }
+
}
Deleted: search/branches/Branch_3_1/src/java/org/hibernate/search/event/IndexWorkFlushEventListener.java
===================================================================
--- search/branches/Branch_3_1/src/java/org/hibernate/search/event/IndexWorkFlushEventListener.java 2009-04-21 20:24:58 UTC (rev 16391)
+++ search/branches/Branch_3_1/src/java/org/hibernate/search/event/IndexWorkFlushEventListener.java 2009-04-22 06:52:05 UTC (rev 16392)
@@ -1,61 +0,0 @@
-// $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 FlushEventListener, Serializable {
-
- private static final Logger log = LoggerFactory.make();
-
- private final ConcurrentHashMap<Session, Synchronization> synchronizationPerTransaction
- = new ConcurrentHashMap<Session, Synchronization>();
-
- public IndexWorkFlushEventListener() {
- }
-
- public void onFlush(FlushEvent event) throws HibernateException {
- Session session = event.getSession();
- Synchronization synchronization = synchronizationPerTransaction.get( session );
- if ( synchronization != 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 synchronization) {
- Synchronization previousSync = synchronizationPerTransaction.put( eventSource, synchronization );
- if ( previousSync != 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.
- }
- */
-
-}
Modified: search/branches/Branch_3_1/src/test/org/hibernate/search/test/TestCase.java
===================================================================
--- search/branches/Branch_3_1/src/test/org/hibernate/search/test/TestCase.java 2009-04-21 20:24:58 UTC (rev 16391)
+++ search/branches/Branch_3_1/src/test/org/hibernate/search/test/TestCase.java 2009-04-22 06:52:05 UTC (rev 16392)
@@ -15,7 +15,7 @@
import org.hibernate.dialect.Dialect;
import org.hibernate.event.FlushEventListener;
import org.hibernate.event.def.DefaultFlushEventListener;
-import org.hibernate.search.event.IndexWorkFlushEventListener;
+import org.hibernate.search.event.FullTextIndexEventListener;
/**
* A modified base class for tests without annotations.
@@ -154,7 +154,7 @@
cfg.setListener( "post-collection-remove", "org.hibernate.search.event.FullTextIndexEventListener" );
cfg.setListener( "post-collection-update", "org.hibernate.search.event.FullTextIndexEventListener" );
- cfg.setListeners( "flush", new FlushEventListener[]{new DefaultFlushEventListener(), new IndexWorkFlushEventListener()} );
+ cfg.setListeners( "flush", new FlushEventListener[]{new DefaultFlushEventListener(), new FullTextIndexEventListener()} );
cfg.setProperty( "hibernate.search.default.directory_provider", RAMDirectoryProvider.class.getName() );
cfg.setProperty( org.hibernate.search.Environment.ANALYZER_CLASS, StopAnalyzer.class.getName() );
Copied: search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/EventListenerSerializationTest.java (from rev 16306, search/trunk/src/test/java/org/hibernate/search/test/engine/EventListenerSerializationTest.java)
===================================================================
--- search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/EventListenerSerializationTest.java (rev 0)
+++ search/branches/Branch_3_1/src/test/org/hibernate/search/test/engine/EventListenerSerializationTest.java 2009-04-22 06:52:05 UTC (rev 16392)
@@ -0,0 +1,26 @@
+package org.hibernate.search.test.engine;
+
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.hibernate.search.event.FullTextIndexEventListener;
+import org.hibernate.search.test.SerializationTestHelper;
+
+/**
+ * Tests that the FullTextIndexEventListener is Serializable
+ *
+ * @author Sanne Grinovero
+ */
+public class EventListenerSerializationTest extends TestCase {
+
+ public void testEventListenerSerializable() throws IOException, ClassNotFoundException {
+ FullTextIndexEventListener eventListener = new FullTextIndexEventListener();
+ eventListener.addSynchronization( null, null );
+ Object secondListener = SerializationTestHelper
+ .duplicateBySerialization(eventListener);
+ assertNotNull(secondListener);
+ assertFalse(secondListener == eventListener);
+ }
+
+}
More information about the hibernate-commits
mailing list