/*
* Hibernate OGM, Domain model persistence for NoSQL datastores
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.ogm.dialect.batch.spi;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.hibernate.ogm.model.key.spi.EntityKey;
import org.hibernate.ogm.util.impl.Log;
import org.hibernate.ogm.util.impl.LoggerFactory;
/**
* A queue for {@link Operation}s.
* <p>
* It keeps track of the elements that are going to be affected by an {@link InsertOrUpdateTupleOperation}.
* The queue can be closed, in that case it will throw an exception when trying to add or poll an operation.
*
* @author Guillaume Scheibel <guillaume.scheibel@gmail.com>
* @author Davide D'Alto <davide@hibernate.org>
* @author Guillaume Smet
*/
public class OperationsQueue {
/**
* A queue that is always closed
*/
public static final OperationsQueue CLOSED_QUEUE = new OperationsQueue() {
@Override
public boolean isClosed() {
return true;
}
};
private static final Log log = LoggerFactory.make();
private final Queue<Operation> operations = new LinkedList<Operation>();
private final Map<EntityKey, GroupedChangesToEntityOperation> groupedOperations = new HashMap<>();
private final Set<EntityKey> insertionQueue = new HashSet<>();
private boolean closed = false;
public OperationsQueue() {
}
public void add(Operation operation) {
validate();
log.debugf( "Add batched operation %1$s", operation );
if ( operation instanceof InsertOrUpdateTupleOperation ) {
addInsertOrUpdateTupleOperation( (InsertOrUpdateTupleOperation) operation );
}
else if ( operation instanceof GroupableEntityOperation ) {
addGroupableEntityOperation( (GroupableEntityOperation) operation );
}
else {
addOperation( operation );
}
}
private void addInsertOrUpdateTupleOperation(InsertOrUpdateTupleOperation operation) {
addGroupableEntityOperation( operation );
insertionQueue.add( operation.getEntityKey() );
}
private void addGroupableEntityOperation(GroupableEntityOperation operation) {
validate();
GroupedChangesToEntityOperation groupedOperation = getOrCreateGroupedChangesOnEntityOperation( operation.getEntityKey() );
groupedOperation.addOperation( operation );
}
private void addOperation(Operation operation) {
operations.add( operation );
}
private GroupedChangesToEntityOperation getOrCreateGroupedChangesOnEntityOperation(EntityKey entityKey) {
GroupedChangesToEntityOperation groupedOperation = groupedOperations.get( entityKey );
if ( groupedOperation == null ) {
groupedOperation = new GroupedChangesToEntityOperation( entityKey );
groupedOperations.put( entityKey, groupedOperation );
addOperation( groupedOperation );
}
return groupedOperations.get( entityKey );
}
private void validate() {
if ( isClosed() ) {
throw log.closedOperationQueue();
}
}
public Operation poll() {
validate();
return operations.poll();
}
public void clear() {
groupedOperations.clear();
operations.clear();
insertionQueue.clear();
}
public void close() {
clear();
closed = true;
}
public boolean isClosed() {
return closed;
}
/**
* @param key the {@link EntityKey} that identify the element
* @return true if an {@link InsertOrUpdateTupleOperation} is bound to the key, false otherwise
*/
public boolean isInTheInsertionQueue(EntityKey key) {
return insertionQueue.contains( key );
}
/**
* @return the length of the queue
*/
public int size() {
return operations.size();
}
}