All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.search.backend.impl.PerTransactionWorker Maven / Gradle / Ivy

There is a newer version: 5.11.12.Final
Show newest version
/*
 * Hibernate Search, full-text search for your domain model
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.search.backend.impl;

import java.util.Properties;
import java.util.concurrent.ConcurrentMap;

import org.hibernate.search.cfg.Environment;
import org.hibernate.search.exception.AssertionFailure;
import org.hibernate.search.backend.spi.Work;
import org.hibernate.search.backend.spi.WorkType;
import org.hibernate.search.backend.spi.Worker;
import org.hibernate.search.engine.integration.impl.ExtendedSearchIntegrator;
import org.hibernate.search.engine.spi.EntityIndexBinding;
import org.hibernate.search.indexes.interceptor.EntityIndexingInterceptor;
import org.hibernate.search.indexes.interceptor.IndexingOverride;
import org.hibernate.search.util.configuration.impl.ConfigurationParseHelper;
import org.hibernate.search.util.impl.Maps;
import org.hibernate.search.util.logging.impl.Log;
import org.hibernate.search.exception.SearchException;
import org.hibernate.search.backend.TransactionContext;
import org.hibernate.search.spi.IndexedTypeIdentifier;
import org.hibernate.search.spi.InstanceInitializer;
import org.hibernate.search.spi.WorkerBuildContext;
import org.hibernate.search.util.logging.impl.LoggerFactory;
import java.lang.invoke.MethodHandles;

/**
 * Queue works per transaction.
 * If out of transaction, the work is executed right away
 * 

* When hibernate.search.worker.type is set to async * the work is done in a separate thread (threads are pooled) * * @author Emmanuel Bernard */ public class PerTransactionWorker implements Worker { //note: there is only one Worker instance, reused concurrently for all sessions. private static final Log log = LoggerFactory.make( MethodHandles.lookup() ); //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 ConcurrentMap synchronizationPerTransaction = Maps.createIdentityWeakKeyConcurrentMap( 64, 32 ); private QueueingProcessor queueingProcessor; private ExtendedSearchIntegrator factory; private InstanceInitializer instanceInitializer; private boolean transactionExpected; private boolean enlistInTransaction; public ConcurrentMap synchronizationPerTransactionForTests() { return synchronizationPerTransaction; } @Override public void performWork(Work work, TransactionContext transactionContext) { final IndexedTypeIdentifier entityType = instanceInitializer.getIndexedTypeIdFromWork( work ); EntityIndexBinding indexBindingForEntity = factory.getIndexBinding( entityType ); if ( indexBindingForEntity == null && factory.getDocumentBuilderContainedEntity( entityType ) == null ) { throw new SearchException( "Unable to perform work. Entity Class is not @Indexed nor hosts @ContainedIn: " + entityType ); } work = interceptWork( indexBindingForEntity, work ); if ( work == null ) { //nothing to do return; } if ( transactionContext.isTransactionInProgress() ) { final Object transactionIdentifier = transactionContext.getTransactionIdentifier(); WorkQueueSynchronization txSync = synchronizationPerTransaction.get( transactionIdentifier ); if ( txSync == null || txSync.isConsumed() ) { txSync = createTransactionWorkQueueSynchronization( transactionIdentifier ); transactionContext.registerSynchronization( txSync ); synchronizationPerTransaction.put( transactionIdentifier, txSync ); } txSync.add( work ); } else { if ( transactionExpected ) { // this is a workaround: isTransactionInProgress should return "true" // for correct configurations. log.pushedChangesOutOfTransaction(); } WorkQueue queue = new WorkQueue( factory ); queueingProcessor.add( work, queue ); queueingProcessor.prepareWorks( queue ); queueingProcessor.performWorks( queue ); } } private WorkQueueSynchronization createTransactionWorkQueueSynchronization(Object transactionIdentifier) { if ( enlistInTransaction ) { return new InTransactionWorkQueueSynchronization( transactionIdentifier, queueingProcessor, synchronizationPerTransaction, factory ); } else { return new PostTransactionWorkQueueSynchronization( transactionIdentifier, queueingProcessor, synchronizationPerTransaction, factory ); } } private Work interceptWork(EntityIndexBinding indexBindingForEntity, Work work) { if ( indexBindingForEntity == null ) { return work; } EntityIndexingInterceptor interceptor = indexBindingForEntity.getEntityIndexingInterceptor(); if ( interceptor == null ) { return work; } IndexingOverride operation; switch ( work.getType() ) { case ADD: operation = interceptor.onAdd( work.getEntity() ); break; case UPDATE: operation = interceptor.onUpdate( work.getEntity() ); break; case DELETE: operation = interceptor.onDelete( work.getEntity() ); break; case COLLECTION: operation = interceptor.onCollectionUpdate( work.getEntity() ); break; case PURGE: case PURGE_ALL: case INDEX: case DELETE_BY_QUERY: operation = IndexingOverride.APPLY_DEFAULT; break; default: throw new AssertionFailure( "Unknown work type: " + work.getType() ); } Work result = work; IndexedTypeIdentifier entityType = work.getTypeIdentifier(); switch ( operation ) { case APPLY_DEFAULT: break; case SKIP: result = null; log.forceSkipIndexOperationViaInterception( entityType, work.getType() ); break; case UPDATE: result = new Work( work.getTenantIdentifier(), work.getEntity(), work.getId(), WorkType.UPDATE ); log.forceUpdateOnIndexOperationViaInterception( entityType, work.getType() ); break; case REMOVE: //This works because other Work constructors are never used from WorkType ADD, UPDATE, REMOVE, COLLECTION //TODO should we force isIdentifierRollback to false if the operation is not a delete? result = new Work( work.getTenantIdentifier(), work.getEntity(), work.getId(), WorkType.DELETE, work.isIdentifierWasRolledBack() ); log.forceRemoveOnIndexOperationViaInterception( entityType, work.getType() ); break; default: throw new AssertionFailure( "Unknown action type: " + operation ); } return result; } @Override public void initialize(Properties props, WorkerBuildContext context, QueueingProcessor queueingProcessor) { this.queueingProcessor = queueingProcessor; this.factory = context.getUninitializedSearchIntegrator(); this.transactionExpected = context.isTransactionManagerExpected(); this.instanceInitializer = context.getInstanceInitializer(); this.enlistInTransaction = ConfigurationParseHelper.getBooleanValue( props, Environment.WORKER_ENLIST_IN_TRANSACTION, false ); } @Override public void close() { } @Override public void flushWorks(TransactionContext transactionContext) { if ( transactionContext.isTransactionInProgress() ) { Object transaction = transactionContext.getTransactionIdentifier(); WorkQueueSynchronization txSync = synchronizationPerTransaction.get( transaction ); if ( txSync != null && !txSync.isConsumed() ) { txSync.flushWorks(); } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy