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

org.hibernate.envers.internal.synchronization.AuditProcess Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha3
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * Copyright (c) 2013, Red Hat Inc. 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.envers.internal.synchronization;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;

import org.hibernate.ConnectionReleaseMode;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.envers.internal.revisioninfo.RevisionInfoGenerator;
import org.hibernate.envers.internal.synchronization.work.AuditWorkUnit;
import org.hibernate.envers.tools.Pair;

/**
 * @author Adam Warski (adam at warski dot org)
 */
public class AuditProcess implements BeforeTransactionCompletionProcess {
	private final RevisionInfoGenerator revisionInfoGenerator;
	private final SessionImplementor session;

	private final LinkedList workUnits;
	private final Queue undoQueue;
	private final Map, AuditWorkUnit> usedIds;
	private final EntityChangeNotifier entityChangeNotifier;

	private Object revisionData;

	public AuditProcess(RevisionInfoGenerator revisionInfoGenerator, SessionImplementor session) {
		this.revisionInfoGenerator = revisionInfoGenerator;
		this.session = session;

		workUnits = new LinkedList();
		undoQueue = new LinkedList();
		usedIds = new HashMap, AuditWorkUnit>();
		entityChangeNotifier = new EntityChangeNotifier( revisionInfoGenerator, session );
	}

	private void removeWorkUnit(AuditWorkUnit vwu) {
		workUnits.remove( vwu );
		if ( vwu.isPerformed() ) {
			// If this work unit has already been performed, it must be deleted (undone) first.
			undoQueue.offer( vwu );
		}
	}

	public void addWorkUnit(AuditWorkUnit vwu) {
		if ( vwu.containsWork() ) {
			final Object entityId = vwu.getEntityId();

			if ( entityId == null ) {
				// Just adding the work unit - it's not associated with any persistent entity.
				workUnits.offer( vwu );
			}
			else {
				final String entityName = vwu.getEntityName();
				final Pair usedIdsKey = Pair.make( entityName, entityId );

				if ( usedIds.containsKey( usedIdsKey ) ) {
					final AuditWorkUnit other = usedIds.get( usedIdsKey );
					final AuditWorkUnit result = vwu.dispatch( other );

					if ( result != other ) {
						removeWorkUnit( other );

						if ( result != null ) {
							usedIds.put( usedIdsKey, result );
							workUnits.offer( result );
						}
						// else: a null result means that no work unit should be kept
					}
					// else: the result is the same as the work unit already added. No need to do anything.
				}
				else {
					usedIds.put( usedIdsKey, vwu );
					workUnits.offer( vwu );
				}
			}
		}
	}

	private void executeInSession(Session session) {
		// Making sure the revision data is persisted.
		final Object currentRevisionData = getCurrentRevisionData( session, true );

		AuditWorkUnit vwu;

		// First undoing any performed work units
		while ( (vwu = undoQueue.poll()) != null ) {
			vwu.undo( session );
		}

		while ( (vwu = workUnits.poll()) != null ) {
			vwu.perform( session, revisionData );
			entityChangeNotifier.entityChanged( session, currentRevisionData, vwu );
		}
	}

	public Object getCurrentRevisionData(Session session, boolean persist) {
		// Generating the revision data if not yet generated
		if ( revisionData == null ) {
			revisionData = revisionInfoGenerator.generate();
		}

		// Saving the revision data, if not yet saved and persist is true
		if ( !session.contains( revisionData ) && persist ) {
			revisionInfoGenerator.saveRevisionData( session, revisionData );
		}

		return revisionData;
	}

	@Override
	public void doBeforeTransactionCompletion(SessionImplementor session) {
		if ( workUnits.size() == 0 && undoQueue.size() == 0 ) {
			return;
		}

		// see: http://www.jboss.com/index.html?module=bb&op=viewtopic&p=4178431
		if ( FlushMode.isManualFlushMode( session.getFlushMode() ) ) {
			Session temporarySession = null;
			try {
				temporarySession = ((Session) session).sessionWithOptions().transactionContext().autoClose( false )
						.connectionReleaseMode( ConnectionReleaseMode.AFTER_TRANSACTION )
						.openSession();
				executeInSession( temporarySession );
				temporarySession.flush();
			}
			finally {
				if ( temporarySession != null ) {
					temporarySession.close();
				}
			}
		}
		else {
			executeInSession( (Session) session );

			// Explicitly flushing the session, as the auto-flush may have already happened.
			session.flush();
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy