HHH-4577 - 2L query cache: Low performance of flush and commit due many unnecessary (pre)invalidate calls on UpdateTimestampsCache
This commit is contained in:
parent
7bca11a504
commit
8dae133bba
|
@ -177,6 +177,7 @@ public abstract class EntityAction
|
|||
*
|
||||
* @param session The session being deserialized
|
||||
*/
|
||||
@Override
|
||||
public void afterDeserialize(SessionImplementor session) {
|
||||
if ( this.session != null || this.persister != null ) {
|
||||
throw new IllegalStateException( "already attached to a session." );
|
||||
|
|
|
@ -48,27 +48,16 @@ public final class QueuedOperationCollectionAction extends CollectionAction {
|
|||
* @param session The session
|
||||
*/
|
||||
public QueuedOperationCollectionAction(
|
||||
final PersistentCollection collection,
|
||||
final CollectionPersister persister,
|
||||
final Serializable id,
|
||||
final SessionImplementor session) {
|
||||
final PersistentCollection collection,
|
||||
final CollectionPersister persister,
|
||||
final Serializable id,
|
||||
final SessionImplementor session) {
|
||||
super( persister, collection, id, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws HibernateException {
|
||||
final Serializable id = getKey();
|
||||
final SessionImplementor session = getSession();
|
||||
final CollectionPersister persister = getPersister();
|
||||
final PersistentCollection collection = getCollection();
|
||||
|
||||
persister.processQueuedOps( collection, id, session );
|
||||
getPersister().processQueuedOps( getCollection(), getKey(), getSession() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -38,8 +38,6 @@ import java.util.Queue;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.PropertyValueException;
|
||||
|
@ -59,30 +57,30 @@ import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
|
|||
import org.hibernate.action.spi.Executable;
|
||||
import org.hibernate.cache.CacheException;
|
||||
import org.hibernate.engine.internal.NonNullableTransientDependencies;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* Responsible for maintaining the queue of actions related to events. The ActionQueue holds the DML operations queued
|
||||
* as part of a session's transactional-write-behind semantics. DML operations are queued here until a flush forces them
|
||||
* to be executed against the database.
|
||||
* Responsible for maintaining the queue of actions related to events.
|
||||
*
|
||||
* The ActionQueue holds the DML operations queued as part of a session's transactional-write-behind semantics. The
|
||||
* DML operations are queued here until a flush forces them to be executed against the database.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
* @author Gail Badner
|
||||
* @author Anton Marsden
|
||||
*/
|
||||
public class ActionQueue {
|
||||
|
||||
static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, ActionQueue.class.getName() );
|
||||
private static final int INIT_QUEUE_LIST_SIZE = 5;
|
||||
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( ActionQueue.class );
|
||||
|
||||
private SessionImplementor session;
|
||||
|
||||
private UnresolvedEntityInsertActions unresolvedInsertions;
|
||||
|
||||
// Object insertions, updates, and deletions have list semantics because
|
||||
// they must happen in the right order so as to respect referential
|
||||
// integrity
|
||||
private UnresolvedEntityInsertActions unresolvedInsertions;
|
||||
|
||||
private final ExecutableList<AbstractEntityInsertAction> insertions;
|
||||
private final ExecutableList<EntityDeleteAction> deletions;
|
||||
private final ExecutableList<EntityUpdateAction> updates;
|
||||
|
@ -111,20 +109,21 @@ public class ActionQueue {
|
|||
this.session = session;
|
||||
|
||||
unresolvedInsertions = new UnresolvedEntityInsertActions();
|
||||
insertions = new ExecutableList<AbstractEntityInsertAction>( INIT_QUEUE_LIST_SIZE, new InsertActionSorter() );
|
||||
deletions = new ExecutableList<EntityDeleteAction>( INIT_QUEUE_LIST_SIZE );
|
||||
updates = new ExecutableList<EntityUpdateAction>( INIT_QUEUE_LIST_SIZE );
|
||||
insertions = new ExecutableList<AbstractEntityInsertAction>( ExecutableList.INIT_QUEUE_LIST_SIZE, new InsertActionSorter() );
|
||||
deletions = new ExecutableList<EntityDeleteAction>( ExecutableList.INIT_QUEUE_LIST_SIZE );
|
||||
updates = new ExecutableList<EntityUpdateAction>( ExecutableList.INIT_QUEUE_LIST_SIZE );
|
||||
|
||||
collectionCreations = new ExecutableList<CollectionRecreateAction>( INIT_QUEUE_LIST_SIZE );
|
||||
collectionRemovals = new ExecutableList<CollectionRemoveAction>( INIT_QUEUE_LIST_SIZE );
|
||||
collectionUpdates = new ExecutableList<CollectionUpdateAction>( INIT_QUEUE_LIST_SIZE );
|
||||
collectionQueuedOps = new ExecutableList<QueuedOperationCollectionAction>( INIT_QUEUE_LIST_SIZE );
|
||||
collectionCreations = new ExecutableList<CollectionRecreateAction>( ExecutableList.INIT_QUEUE_LIST_SIZE );
|
||||
collectionRemovals = new ExecutableList<CollectionRemoveAction>( ExecutableList.INIT_QUEUE_LIST_SIZE );
|
||||
collectionUpdates = new ExecutableList<CollectionUpdateAction>( ExecutableList.INIT_QUEUE_LIST_SIZE );
|
||||
collectionQueuedOps = new ExecutableList<QueuedOperationCollectionAction>( ExecutableList.INIT_QUEUE_LIST_SIZE );
|
||||
|
||||
// Important: these lists are in execution order
|
||||
List<ExecutableList<?>> tmp = new ArrayList<ExecutableList<?>>( 7 );
|
||||
tmp.add( insertions );
|
||||
tmp.add( updates );
|
||||
tmp.add( collectionQueuedOps ); // do before actions are handled in the other collection queues
|
||||
// do before actions are handled in the other collection queues
|
||||
tmp.add( collectionQueuedOps );
|
||||
tmp.add( collectionRemovals );
|
||||
tmp.add( collectionUpdates );
|
||||
tmp.add( collectionCreations );
|
||||
|
@ -138,48 +137,22 @@ public class ActionQueue {
|
|||
}
|
||||
|
||||
public void clear() {
|
||||
|
||||
for ( ExecutableList<?> l : executableLists ) {
|
||||
l.clear();
|
||||
}
|
||||
|
||||
unresolvedInsertions.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entity insert action
|
||||
*
|
||||
* @param action The action representing the entity insertion
|
||||
*/
|
||||
public void addAction(EntityInsertAction action) {
|
||||
LOG.tracev( "Adding an EntityInsertAction for [{0}] object", action.getEntityName() );
|
||||
addInsertAction( action );
|
||||
}
|
||||
|
||||
public void addAction(EntityDeleteAction action) {
|
||||
deletions.add( action );
|
||||
}
|
||||
|
||||
public void addAction(EntityUpdateAction action) {
|
||||
updates.add( action );
|
||||
}
|
||||
|
||||
public void addAction(CollectionRecreateAction action) {
|
||||
collectionCreations.add( action );
|
||||
}
|
||||
|
||||
public void addAction(CollectionRemoveAction action) {
|
||||
collectionRemovals.add( action );
|
||||
}
|
||||
|
||||
public void addAction(CollectionUpdateAction action) {
|
||||
collectionUpdates.add( action );
|
||||
}
|
||||
|
||||
public void addAction(QueuedOperationCollectionAction action) {
|
||||
collectionQueuedOps.add( action );
|
||||
}
|
||||
|
||||
public void addAction(EntityIdentityInsertAction insert) {
|
||||
LOG.tracev( "Adding an EntityIdentityInsertAction for [{0}] object", insert.getEntityName() );
|
||||
addInsertAction( insert );
|
||||
}
|
||||
|
||||
private void addInsertAction(AbstractEntityInsertAction insert) {
|
||||
if ( insert.isEarlyInsert() ) {
|
||||
// For early inserts, must execute inserts before finding non-nullable transient entities.
|
||||
|
@ -195,7 +168,7 @@ public class ActionQueue {
|
|||
else {
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracev( "Adding insert with non-nullable, transient entities; insert=[{0}], dependencies=[{1}]", insert,
|
||||
nonNullableTransientDependencies.toLoggableString( insert.getSession() ) );
|
||||
nonNullableTransientDependencies.toLoggableString( insert.getSession() ) );
|
||||
}
|
||||
unresolvedInsertions.addUnresolvedEntityInsertAction( insert, nonNullableTransientDependencies );
|
||||
}
|
||||
|
@ -218,6 +191,87 @@ public class ActionQueue {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entity (IDENTITY) insert action
|
||||
*
|
||||
* @param action The action representing the entity insertion
|
||||
*/
|
||||
public void addAction(EntityIdentityInsertAction action) {
|
||||
LOG.tracev( "Adding an EntityIdentityInsertAction for [{0}] object", action.getEntityName() );
|
||||
addInsertAction( action );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entity delete action
|
||||
*
|
||||
* @param action The action representing the entity deletion
|
||||
*/
|
||||
public void addAction(EntityDeleteAction action) {
|
||||
deletions.add( action );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entity update action
|
||||
*
|
||||
* @param action The action representing the entity update
|
||||
*/
|
||||
public void addAction(EntityUpdateAction action) {
|
||||
updates.add( action );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a collection (re)create action
|
||||
*
|
||||
* @param action The action representing the (re)creation of a collection
|
||||
*/
|
||||
public void addAction(CollectionRecreateAction action) {
|
||||
collectionCreations.add( action );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a collection remove action
|
||||
*
|
||||
* @param action The action representing the removal of a collection
|
||||
*/
|
||||
public void addAction(CollectionRemoveAction action) {
|
||||
collectionRemovals.add( action );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a collection update action
|
||||
*
|
||||
* @param action The action representing the update of a collection
|
||||
*/
|
||||
public void addAction(CollectionUpdateAction action) {
|
||||
collectionUpdates.add( action );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an action relating to a collection queued operation (extra lazy).
|
||||
*
|
||||
* @param action The action representing the queued operation
|
||||
*/
|
||||
public void addAction(QueuedOperationCollectionAction action) {
|
||||
collectionQueuedOps.add( action );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an action defining a cleanup relating to a bulk operation (HQL/JPQL or Criteria based update/delete)
|
||||
*
|
||||
* @param action The action representing the queued operation
|
||||
*/
|
||||
public void addAction(BulkOperationCleanupAction action) {
|
||||
registerCleanupActions( action );
|
||||
}
|
||||
|
||||
private void registerCleanupActions(Executable executable) {
|
||||
beforeTransactionProcesses.register( executable.getBeforeTransactionCompletionProcess() );
|
||||
if ( session.getFactory().getSettings().isQueryCacheEnabled() ) {
|
||||
invalidateSpaces( executable.getPropertySpaces() );
|
||||
}
|
||||
afterTransactionProcesses.register( executable.getAfterTransactionCompletionProcess() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Are there unresolved entity insert actions that depend on non-nullable associations with a transient entity?
|
||||
*
|
||||
|
@ -242,10 +296,6 @@ public class ActionQueue {
|
|||
unresolvedInsertions.checkNoUnresolvedActionsAfterOperation();
|
||||
}
|
||||
|
||||
public void addAction(BulkOperationCleanupAction cleanupAction) {
|
||||
registerCleanupActions( cleanupAction );
|
||||
}
|
||||
|
||||
public void registerProcess(AfterTransactionCompletionProcess process) {
|
||||
afterTransactionProcesses.register( process );
|
||||
}
|
||||
|
@ -272,6 +322,7 @@ public class ActionQueue {
|
|||
if ( !unresolvedInsertions.isEmpty() ) {
|
||||
throw new IllegalStateException( "About to execute actions, but there are unresolved entity insert actions." );
|
||||
}
|
||||
|
||||
for ( ExecutableList<?> l : executableLists ) {
|
||||
executeActions( l );
|
||||
}
|
||||
|
@ -289,6 +340,12 @@ public class ActionQueue {
|
|||
prepareActions( collectionQueuedOps );
|
||||
}
|
||||
|
||||
private void prepareActions(ExecutableList<?> queue) throws HibernateException {
|
||||
for ( Executable executable : queue ) {
|
||||
executable.beforeExecutions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs cleanup of any held cache softlocks.
|
||||
*
|
||||
|
@ -305,11 +362,21 @@ public class ActionQueue {
|
|||
beforeTransactionProcesses.beforeTransactionCompletion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether any insertion or deletion actions are currently queued.
|
||||
*
|
||||
* @return {@code true} if insertions or deletions are currently queued; {@code false} otherwise.
|
||||
*/
|
||||
public boolean areInsertionsOrDeletionsQueued() {
|
||||
return !insertions.isEmpty() || !unresolvedInsertions.isEmpty() || !deletions.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given tables/query-spaces are to be executed against given the currently queued actions.
|
||||
*
|
||||
* @param tables The table/query-spaces to check.
|
||||
* @return True if we contain pending actions against any of the given tables; false otherwise.
|
||||
*
|
||||
* @return {@code true} if we contain pending actions against any of the given tables; {@code false} otherwise.
|
||||
*/
|
||||
public boolean areTablesToBeUpdated(@SuppressWarnings("rawtypes") Set tables) {
|
||||
if ( tables.isEmpty() ) {
|
||||
|
@ -323,22 +390,12 @@ public class ActionQueue {
|
|||
return areTablesToBeUpdated( unresolvedInsertions, tables );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether any insertion or deletion actions are currently queued.
|
||||
*
|
||||
* @return True if insertions or deletions are currently queued; false otherwise.
|
||||
*/
|
||||
public boolean areInsertionsOrDeletionsQueued() {
|
||||
return !insertions.isEmpty() || !unresolvedInsertions.isEmpty() || !deletions.isEmpty();
|
||||
}
|
||||
|
||||
private static boolean areTablesToBeUpdated(ExecutableList<?> actions, @SuppressWarnings("rawtypes") Set tableSpaces) {
|
||||
|
||||
if ( actions.isEmpty() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for ( Serializable actionSpace : actions.getPropertySpaces() ) {
|
||||
for ( Serializable actionSpace : actions.getQuerySpaces() ) {
|
||||
if ( tableSpaces.contains( actionSpace ) ) {
|
||||
LOG.debugf( "Changes must be flushed to space: %s", actionSpace );
|
||||
return true;
|
||||
|
@ -362,18 +419,18 @@ public class ActionQueue {
|
|||
}
|
||||
|
||||
/**
|
||||
* Executes a list of Executables
|
||||
* Perform {@link org.hibernate.action.spi.Executable#execute()} on each element of the list
|
||||
*
|
||||
* @param list
|
||||
* @param list The list of Executable elements to be performed
|
||||
*
|
||||
* @throws HibernateException
|
||||
*/
|
||||
private <E extends Executable & Comparable<?> & Serializable> void executeActions(ExecutableList<E> list) throws HibernateException {
|
||||
|
||||
// todo : consider ways to improve the double iteration of Executables here:
|
||||
// 1) we explicitly iterate list here to perform Executable#execute()
|
||||
// 2) ExecutableList#getQuerySpaces also iterates the Executables to collect query spaces.
|
||||
try {
|
||||
|
||||
for ( E e : list ) {
|
||||
// Preserves the try/finally behavior of an individual execution as per execute(Executable), for what
|
||||
// it's worth
|
||||
try {
|
||||
e.execute();
|
||||
}
|
||||
|
@ -388,7 +445,7 @@ public class ActionQueue {
|
|||
// Strictly speaking, only a subset of the list may have been processed if a RuntimeException occurs.
|
||||
// We still invalidate all spaces. I don't see this as a big deal - after all, RuntimeExceptions are
|
||||
// unexpected.
|
||||
Set<Serializable> propertySpaces = list.getPropertySpaces();
|
||||
Set<Serializable> propertySpaces = list.getQuerySpaces();
|
||||
invalidateSpaces( propertySpaces.toArray( new Serializable[propertySpaces.size()] ) );
|
||||
}
|
||||
}
|
||||
|
@ -398,7 +455,7 @@ public class ActionQueue {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param executable
|
||||
* @param executable The action to execute
|
||||
*/
|
||||
public <E extends Executable & Comparable<?>> void execute(E executable) {
|
||||
try {
|
||||
|
@ -409,21 +466,13 @@ public class ActionQueue {
|
|||
}
|
||||
}
|
||||
|
||||
private void registerCleanupActions(Executable executable) {
|
||||
beforeTransactionProcesses.register( executable.getBeforeTransactionCompletionProcess() );
|
||||
if ( session.getFactory().getSettings().isQueryCacheEnabled() ) {
|
||||
invalidateSpaces( executable.getPropertySpaces() );
|
||||
}
|
||||
afterTransactionProcesses.register( executable.getAfterTransactionCompletionProcess() );
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is now called once per execution of an ExecutableList or once for execution of an Execution.
|
||||
*
|
||||
* @param spaces
|
||||
* @param spaces The spaces to invalidate
|
||||
*/
|
||||
private void invalidateSpaces(Serializable... spaces) {
|
||||
if ( spaces != null && spaces.length > 0 ) { // HHH-6286
|
||||
if ( spaces != null && spaces.length > 0 ) {
|
||||
for ( Serializable s : spaces ) {
|
||||
afterTransactionProcesses.addSpaceToInvalidate( (String) s );
|
||||
}
|
||||
|
@ -432,12 +481,6 @@ public class ActionQueue {
|
|||
}
|
||||
}
|
||||
|
||||
private void prepareActions(ExecutableList<?> queue) throws HibernateException {
|
||||
for ( Executable executable : queue ) {
|
||||
executable.beforeExecutions();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the object.
|
||||
*
|
||||
|
@ -445,10 +488,15 @@ public class ActionQueue {
|
|||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return new StringBuilder().append( "ActionQueue[insertions=" ).append( insertions ).append( " updates=" ).append( updates ).append( " deletions=" )
|
||||
.append( deletions ).append( " collectionCreations=" ).append( collectionCreations ).append( " collectionRemovals=" )
|
||||
.append( collectionRemovals ).append( " collectionUpdates=" ).append( collectionUpdates ).append( " collectionQueuedOps=" )
|
||||
.append( collectionQueuedOps ).append( " unresolvedInsertDependencies=" ).append( unresolvedInsertions ).append( "]" ).toString();
|
||||
return "ActionQueue[insertions=" + insertions
|
||||
+ " updates=" + updates
|
||||
+ " deletions=" + deletions
|
||||
+ " collectionCreations=" + collectionCreations
|
||||
+ " collectionRemovals=" + collectionRemovals
|
||||
+ " collectionUpdates=" + collectionUpdates
|
||||
+ " collectionQueuedOps=" + collectionQueuedOps
|
||||
+ " unresolvedInsertDependencies=" + unresolvedInsertions
|
||||
+ "]";
|
||||
}
|
||||
|
||||
public int numberOfCollectionRemovals() {
|
||||
|
@ -571,8 +619,10 @@ public class ActionQueue {
|
|||
return rtn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates behavior needed for after transaction processing
|
||||
*/
|
||||
private static class BeforeTransactionCompletionProcessQueue {
|
||||
|
||||
private SessionImplementor session;
|
||||
// Concurrency handling required when transaction completion process is dynamically registered
|
||||
// inside event listener (HHH-7478).
|
||||
|
@ -605,8 +655,10 @@ public class ActionQueue {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates behavior needed for after transaction processing
|
||||
*/
|
||||
private static class AfterTransactionCompletionProcessQueue {
|
||||
|
||||
private SessionImplementor session;
|
||||
private Set<String> querySpacesToInvalidate = new HashSet<String>();
|
||||
// Concurrency handling required when transaction completion process is dynamically registered
|
||||
|
|
|
@ -38,22 +38,28 @@ import java.util.Set;
|
|||
import org.hibernate.action.spi.Executable;
|
||||
|
||||
/**
|
||||
* Encapsulates state relating to each executable list. Lazily sorts the list and caches the sorted state. Lazily
|
||||
* calculates the spaces affected by the actions in the list, and caches this too.
|
||||
*
|
||||
* Specialized encapsulating of the state pertaining to each Executable list.
|
||||
* <p/>
|
||||
* Lazily sorts the list and caches the sorted state.
|
||||
* <p/>
|
||||
* Lazily calculates the querySpaces affected by the actions in the list, and caches this too.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
* @author Anton Marsden
|
||||
* @param <E>
|
||||
*
|
||||
* @param <E> Intersection type describing Executable implementations
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class ExecutableList<E extends Executable & Comparable & Serializable> implements Serializable, Iterable<E>, Externalizable {
|
||||
|
||||
public static final int INIT_QUEUE_LIST_SIZE = 5;
|
||||
|
||||
/**
|
||||
* Provides a sorting interface for ExecutableList.
|
||||
*
|
||||
* @author Anton Marsden
|
||||
* @param <E>
|
||||
*/
|
||||
public interface Sorter<E extends Executable> {
|
||||
public static interface Sorter<E extends Executable> {
|
||||
|
||||
/**
|
||||
* Sorts the list.
|
||||
|
@ -61,13 +67,12 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
|
|||
void sort(List<E> l);
|
||||
}
|
||||
|
||||
private final ExecutableList.Sorter<E> sorter;
|
||||
|
||||
private final ArrayList<E> executables;
|
||||
|
||||
private final Sorter<E> sorter;
|
||||
private boolean sorted;
|
||||
|
||||
private transient Set<Serializable> spaces;
|
||||
private transient Set<Serializable> querySpaces;
|
||||
|
||||
/**
|
||||
* Creates a new ExecutableList.
|
||||
|
@ -79,16 +84,16 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
|
|||
/**
|
||||
* Creates a new ExecutableList using the specified Sorter.
|
||||
*
|
||||
* @param sorter
|
||||
* @param sorter The Sorter to use; may be {@code null}
|
||||
*/
|
||||
public ExecutableList(ExecutableList.Sorter<E> sorter) {
|
||||
this( 10, sorter ); // use the standard ArrayList initialCapacity
|
||||
this( INIT_QUEUE_LIST_SIZE, sorter );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ExecutableList with the specified initialCapacity.
|
||||
*
|
||||
* @param initialCapacity
|
||||
* @param initialCapacity The initial capacity for instantiating the internal List
|
||||
*/
|
||||
ExecutableList(int initialCapacity) {
|
||||
this( initialCapacity, null );
|
||||
|
@ -96,16 +101,16 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
|
|||
|
||||
/**
|
||||
* Creates a new ExecutableList with the specified initialCapacity and Sorter.
|
||||
*
|
||||
* @param initialCapacity
|
||||
* @param sorter
|
||||
*
|
||||
* @param initialCapacity The initial capacity for instantiating the internal List
|
||||
* @param sorter The Sorter to use; may be {@code null}
|
||||
*/
|
||||
ExecutableList(int initialCapacity, ExecutableList.Sorter<E> sorter) {
|
||||
this.sorter = sorter;
|
||||
this.executables = new ArrayList<E>( initialCapacity );
|
||||
// a non-null spaces value would add to the spaces as the list is added to,
|
||||
// a non-null querySpaces value would add to the querySpaces as the list is added to,
|
||||
// but we would like this data to be lazily initialized.
|
||||
this.spaces = null;
|
||||
this.querySpaces = null;
|
||||
this.sorted = true;
|
||||
}
|
||||
|
||||
|
@ -117,20 +122,22 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
|
|||
}
|
||||
|
||||
/**
|
||||
* Removes the entry at position idx in the list.
|
||||
* Removes the entry at position index in the list.
|
||||
*
|
||||
* @param idx
|
||||
* @param index The index of the element to remove
|
||||
*
|
||||
* @return the entry that was removed
|
||||
*/
|
||||
public E remove(int idx) {
|
||||
|
||||
if ( idx < executables.size() - 1 ) {
|
||||
public E remove(int index) {
|
||||
if ( index < executables.size() - 1 ) {
|
||||
sorted = false;
|
||||
}
|
||||
E e = executables.remove( idx );
|
||||
// clear the spaces cache if the removed Executable had property spaces
|
||||
|
||||
final E e = executables.remove( index );
|
||||
|
||||
// clear the querySpaces cache if the removed Executable had property querySpaces
|
||||
if ( e.getPropertySpaces() != null && e.getPropertySpaces().length > 0 ) {
|
||||
spaces = null;
|
||||
querySpaces = null;
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
@ -139,24 +146,23 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
|
|||
* Clears the list of executions.
|
||||
*/
|
||||
public void clear() {
|
||||
// Note: another option here is to replace the list with a new one
|
||||
executables.clear();
|
||||
spaces = null;
|
||||
querySpaces = null;
|
||||
sorted = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the last n entries from the list.
|
||||
*
|
||||
* @param n
|
||||
* @param n The number of elements to remove.
|
||||
*/
|
||||
public void removeLastN(int n) {
|
||||
if ( n > 0 ) {
|
||||
int size = executables.size();
|
||||
for ( Executable e : executables.subList( size - n, size ) ) {
|
||||
if ( e.getPropertySpaces() != null && e.getPropertySpaces().length > 0 ) {
|
||||
// spaces could now be incorrect
|
||||
spaces = null;
|
||||
// querySpaces could now be incorrect
|
||||
querySpaces = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -165,23 +171,21 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
|
|||
}
|
||||
|
||||
/**
|
||||
* Lazily constructs the spaces affected by the actions in the list.
|
||||
* Lazily constructs the querySpaces affected by the actions in the list.
|
||||
*
|
||||
* @return the spaces affected by the actions in this list
|
||||
* @return the querySpaces affected by the actions in this list
|
||||
*/
|
||||
public Set<Serializable> getPropertySpaces() {
|
||||
if ( spaces == null ) {
|
||||
spaces = new HashSet<Serializable>();
|
||||
public Set<Serializable> getQuerySpaces() {
|
||||
if ( querySpaces == null ) {
|
||||
querySpaces = new HashSet<Serializable>();
|
||||
for ( E e : executables ) {
|
||||
Serializable[] propertySpaces = e.getPropertySpaces();
|
||||
if ( spaces != null && propertySpaces != null ) {
|
||||
for ( Serializable s : propertySpaces ) {
|
||||
spaces.add( s );
|
||||
}
|
||||
if ( querySpaces != null && propertySpaces != null ) {
|
||||
Collections.addAll( querySpaces, propertySpaces );
|
||||
}
|
||||
}
|
||||
}
|
||||
return spaces;
|
||||
return querySpaces;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,11 +200,9 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
|
|||
// no longer sorted
|
||||
sorted = false;
|
||||
Serializable[] propertySpaces = o.getPropertySpaces();
|
||||
// we can cheaply keep spaces in sync once they are cached
|
||||
if ( spaces != null && propertySpaces != null ) {
|
||||
for ( Serializable s : propertySpaces ) {
|
||||
spaces.add( s );
|
||||
}
|
||||
// we can cheaply keep querySpaces in sync once they are cached
|
||||
if ( querySpaces != null && propertySpaces != null ) {
|
||||
Collections.addAll( querySpaces, propertySpaces );
|
||||
}
|
||||
}
|
||||
return added;
|
||||
|
@ -214,6 +216,7 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
|
|||
if ( sorted ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( sorter != null ) {
|
||||
sorter.sort( executables );
|
||||
}
|
||||
|
@ -231,11 +234,12 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
|
|||
}
|
||||
|
||||
/**
|
||||
* @param idx
|
||||
* @return the element at index idx
|
||||
* @param index The index of the element to retrieve
|
||||
*
|
||||
* @return The element at specified index
|
||||
*/
|
||||
public E get(int idx) {
|
||||
return executables.get( idx );
|
||||
public E get(int index) {
|
||||
return executables.get( index );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -251,7 +255,7 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
|
|||
/**
|
||||
* Serializes the list out to oos.
|
||||
*
|
||||
* @param oos
|
||||
* @param oos The stream to which to serialize our state
|
||||
*/
|
||||
@Override
|
||||
public void writeExternal(ObjectOutput oos) throws IOException {
|
||||
|
@ -262,15 +266,15 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
|
|||
}
|
||||
|
||||
/**
|
||||
* Deserializes the list into this object from in.
|
||||
* De-serializes the list into this object from in.
|
||||
*
|
||||
* @param in
|
||||
* @param in The stream from which to read our serial state
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
|
||||
sorted = false;
|
||||
spaces = null;
|
||||
querySpaces = null;
|
||||
int size = in.readInt();
|
||||
executables.ensureCapacity( size );
|
||||
if ( size > 0 ) {
|
||||
|
@ -282,9 +286,9 @@ public class ExecutableList<E extends Executable & Comparable & Serializable> im
|
|||
}
|
||||
|
||||
/**
|
||||
* Re-attaches the executables to the session after deserialization.
|
||||
* Re-attaches the Executable elements to the session after deserialization.
|
||||
*
|
||||
* @param session
|
||||
* @param session The session to which to attach the Executable elements
|
||||
*/
|
||||
public void afterDeserialize(SessionImplementor session) {
|
||||
for ( E e : executables ) {
|
||||
|
|
|
@ -181,13 +181,13 @@ public class ExecutableListTest extends BaseUnitTestCase {
|
|||
@Test
|
||||
public void testGetSpaces() {
|
||||
l.add( action1 );
|
||||
Set<Serializable> ss = l.getPropertySpaces();
|
||||
Set<Serializable> ss = l.getQuerySpaces();
|
||||
Assert.assertEquals( 1, ss.size() );
|
||||
Assert.assertTrue( ss.contains( "a" ) );
|
||||
l.add( action2 );
|
||||
l.add( action3 );
|
||||
l.add( action4 );
|
||||
Set<Serializable> ss2 = l.getPropertySpaces();
|
||||
Set<Serializable> ss2 = l.getQuerySpaces();
|
||||
Assert.assertEquals( 4, ss2.size() );
|
||||
Assert.assertTrue( ss2.contains( "a" ) );
|
||||
Assert.assertTrue( ss2.contains( "b" ) );
|
||||
|
@ -196,11 +196,11 @@ public class ExecutableListTest extends BaseUnitTestCase {
|
|||
Assert.assertTrue( ss == ss2 ); // same Set (cached)
|
||||
// now remove action4
|
||||
l.remove( 3 );
|
||||
ss2 = l.getPropertySpaces();
|
||||
ss2 = l.getQuerySpaces();
|
||||
Assert.assertTrue( ss == ss2 ); // same Set (action4 has no spaces)
|
||||
Assert.assertEquals( 4, ss2.size() );
|
||||
l.remove( 2 );
|
||||
ss2 = l.getPropertySpaces();
|
||||
ss2 = l.getQuerySpaces();
|
||||
Assert.assertTrue( ss != ss2 ); // Different Set because it has been rebuilt. This would be incorrect if
|
||||
// Set.clear() was used
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue