/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2010, Red Hat, Inc. and/or its affiliates or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat, Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.search.batchindexing.impl;
import java.io.Serializable;
import java.util.List;
import org.hibernate.CacheMode;
import org.hibernate.Criteria;
import org.hibernate.FlushMode;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Restrictions;
import org.hibernate.search.batchindexing.MassIndexerProgressMonitor;
import org.hibernate.search.exception.ErrorHandler;
import org.hibernate.search.util.logging.impl.Log;
import org.hibernate.search.util.logging.impl.LoggerFactory;
/**
* This {@code Runnable} is consuming entity identifiers and
* producing loaded detached entities for the next queue.
* It will finish when the queue it is consuming from will
* signal there are no more identifiers.
*
* @author Sanne Grinovero
*/
public class IdentifierConsumerEntityProducer implements SessionAwareRunnable {
private static final Log log = LoggerFactory.make();
private final ProducerConsumerQueue<List<Serializable>> source;
private final ProducerConsumerQueue<List<?>> destination;
private final SessionFactory sessionFactory;
private final CacheMode cacheMode;
private final Class<?> type;
private final MassIndexerProgressMonitor monitor;
private final String idName;
private final ErrorHandler errorHandler;
public IdentifierConsumerEntityProducer(
ProducerConsumerQueue<List<Serializable>> fromIdentifierListToEntities,
ProducerConsumerQueue<List<?>> fromEntityToAddWork,
MassIndexerProgressMonitor monitor,
SessionFactory sessionFactory,
CacheMode cacheMode, Class<?> type,
String idName, ErrorHandler errorHandler) {
this.source = fromIdentifierListToEntities;
this.destination = fromEntityToAddWork;
this.monitor = monitor;
this.sessionFactory = sessionFactory;
this.cacheMode = cacheMode;
this.type = type;
this.idName = idName;
this.errorHandler = errorHandler;
log.trace( "created" );
}
public void run(Session upperSession) {
log.trace( "started" );
Session session = upperSession;
if ( upperSession == null ) {
session = sessionFactory.openSession();
}
session.setFlushMode( FlushMode.MANUAL );
session.setCacheMode( cacheMode );
session.setDefaultReadOnly( true );
try {
Transaction transaction = Helper.getTransactionAndMarkForJoin( session );
transaction.begin();
loadAllFromQueue( session );
transaction.commit();
}
catch ( Throwable e ) {
errorHandler.handleException( log.massIndexerUnexpectedErrorMessage() , e );
}
finally {
if ( upperSession == null ) {
session.close();
}
}
log.trace( "finished" );
}
private void loadAllFromQueue(Session session) {
try {
Object take;
do {
take = source.take();
if ( take != null ) {
@SuppressWarnings("unchecked")
List<Serializable> listIds = (List<Serializable>) take;
log.tracef( "received list of ids %s", listIds );
loadList( listIds, session );
}
}
while ( take != null );
}
catch ( InterruptedException e ) {
// just quit
Thread.currentThread().interrupt();
}
finally {
destination.producerStopping();
}
}
/**
* Loads a list of entities of defined type using their identifiers.
* The loaded objects are then pushed to the next queue one by one.
*
* @param listIds the list of entity identifiers (of type
* @param session the session to be used
*
* @throws InterruptedException
*/
private void loadList(List<Serializable> listIds, Session session) throws InterruptedException {
//TODO investigate if I should use ObjectLoaderHelper.initializeObjects instead
Criteria criteria = session
.createCriteria( type )
.setCacheMode( cacheMode )
.setLockMode( LockMode.NONE )
.setCacheable( false )
.setFlushMode( FlushMode.MANUAL )
.setFetchSize( listIds.size() )
.setResultTransformer( CriteriaSpecification.DISTINCT_ROOT_ENTITY )
.add( Restrictions.in( idName, listIds ) );
List<?> list = criteria.list();
monitor.entitiesLoaded( list.size() );
session.clear();
destination.put( list );
}
}