The change
HSEARCH-1618 Closed forces the subclasser to jump through hoops in order to perform proper hibernate session cleanup. A typical subclass using Plain Hibernate ORM would look similar to something like this
public class AMQJmsSearchController extends AbstractJMSHibernateSearchController
{
private static final Logger logger = LoggerFactory.getLogger(AMQJmsSearchController.class);
private SessionFactory sessionFactory;
private volatile Session session;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
@Required
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Override
protected void afterMessage() {
Session tempSession = session;
session = null;
if ( tempSession != null && tempSession.isOpen()) {
tempSession.close();
}
}
@Override
protected SearchIntegrator getSearchIntegrator() {
session = sessionFactory.openSession();
final FullTextSession fullTextSession = Search.getFullTextSession(session);
SearchIntegrator searchIntegrator = fullTextSession.getSearchFactory().unwrap(SearchIntegrator.class);
return searchIntegrator;
}
}
However this implementation has multiple problems. 1. AbstractJMSHibernateSearchController.onMessage calls getSearchIntegrator() 2x (needlessly)
public void onMessage(Message message) {
if ( !( message instanceof ObjectMessage ) ) {
log.incorrectMessageType( message.getClass() );
return;
}
final ObjectMessage objectMessage = (ObjectMessage) message;
final String indexName;
final List<LuceneWork> queue;
final IndexManager indexManager;
SearchIntegrator integrator = getSearchIntegrator();
try {
indexName = extractIndexName( objectMessage );
if ( log.isDebugEnabled() ) {
logMessageDetails( objectMessage, indexName );
}
indexManager = integrator.getIndexManager( indexName );
if ( indexManager == null ) {
log.messageReceivedForUndefinedIndex( indexName );
return;
}
queue = getSearchIntegrator().getWorkSerializer().toLuceneWorks( (byte[]) objectMessage.getObject() );
indexManager.performOperations( queue, null );
}
catch (JMSException e) {
log.unableToRetrieveObjectFromMessage( message.getClass(), e );
return;
}
catch (ClassCastException e) {
log.illegalObjectRetrievedFromMessage( e );
return;
}
finally {
afterMessage();
}
}
This will cause the current subclass to leak 1 Hibernate session per message. 2. Since AbstractJMSHibernateSearchController#afterMessage does not contain a reference or context object, the subclass needs to store the HibernateSession locally making it vulnerable to threading issues. The only solution is to basically ditch AbstractJMSHibernateSearchController, copy it and make sure that the session is only stored as a local variable. |