/* * 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 ); } }