/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * 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.engine.spi;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.hibernate.action.spi.Executable;
import org.hibernate.internal.util.collections.CollectionHelper;

Specialized encapsulating of the state pertaining to each Executable list.

Manages sorting the executables (lazily)

Manages the querySpaces affected by the executables in the list, and caches this too.
Author:Steve Ebersole, Anton Marsden
Type parameters:
  • <E> – Intersection type describing Executable implementations
/** * Specialized encapsulating of the state pertaining to each Executable list. * <p/> * Manages sorting the executables (lazily) * <p/> * Manages the querySpaces affected by the executables in the list, and caches this too. * * @author Steve Ebersole * @author Anton Marsden * * @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.
Type parameters:
  • <E> –
/** * Provides a sorting interface for ExecutableList. * * @param <E> */
public static interface Sorter<E extends Executable> {
Sorts the list.
/** * Sorts the list. */
void sort(List<E> l); } private final ArrayList<E> executables; private final Sorter<E> sorter; private final boolean requiresSorting; private boolean sorted;
Used to hold the query spaces (table names, roughly) that all the Executable instances contained in this list define. This information is ultimately used to invalidate cache regions as it is exposed from getQuerySpaces. This value being null indicates that the query spaces should be calculated.
/** * Used to hold the query spaces (table names, roughly) that all the Executable instances contained * in this list define. This information is ultimately used to invalidate cache regions as it is * exposed from {@link #getQuerySpaces}. This value being {@code null} indicates that the * query spaces should be calculated. */
private transient Set<Serializable> querySpaces;
Creates a new ExecutableList with the default settings.
/** * Creates a new ExecutableList with the default settings. */
public ExecutableList() { this( INIT_QUEUE_LIST_SIZE ); }
Creates a new ExecutableList with the specified initialCapacity.
Params:
  • initialCapacity – The initial capacity for instantiating the internal List
/** * Creates a new ExecutableList with the specified initialCapacity. * * @param initialCapacity The initial capacity for instantiating the internal List */
public ExecutableList(int initialCapacity) { // pass true for requiresSorting argument to maintain original behavior this( initialCapacity, true ); } public ExecutableList(boolean requiresSorting) { this( INIT_QUEUE_LIST_SIZE, requiresSorting ); } public ExecutableList(int initialCapacity, boolean requiresSorting) { this.sorter = null; this.executables = new ArrayList<E>( initialCapacity ); this.querySpaces = null; this.requiresSorting = requiresSorting; this.sorted = requiresSorting; }
Creates a new ExecutableList using the specified Sorter.
Params:
  • sorter – The Sorter to use; may be null
/** * Creates a new ExecutableList using the specified Sorter. * * @param sorter The Sorter to use; may be {@code null} */
public ExecutableList(ExecutableList.Sorter<E> sorter) { this( INIT_QUEUE_LIST_SIZE, sorter ); }
Creates a new ExecutableList with the specified initialCapacity and Sorter.
Params:
  • initialCapacity – The initial capacity for instantiating the internal List
  • sorter – The Sorter to use; may be null
/** * Creates a new ExecutableList with the specified initialCapacity and Sorter. * * @param initialCapacity The initial capacity for instantiating the internal List * @param sorter The Sorter to use; may be {@code null} */
public ExecutableList(int initialCapacity, ExecutableList.Sorter<E> sorter) { this.sorter = sorter; this.executables = new ArrayList<E>( initialCapacity ); this.querySpaces = null; // require sorting by default, even if sorter is null to maintain original behavior this.requiresSorting = true; this.sorted = true; }
Lazily constructs the querySpaces affected by the actions in the list.
Returns:the querySpaces affected by the actions in this list
/** * Lazily constructs the querySpaces affected by the actions in the list. * * @return the querySpaces affected by the actions in this list */
public Set<Serializable> getQuerySpaces() { if ( querySpaces == null ) { for ( E e : executables ) { Serializable[] propertySpaces = e.getPropertySpaces(); if ( propertySpaces != null && propertySpaces.length > 0 ) { if( querySpaces == null ) { querySpaces = new HashSet<Serializable>(); } Collections.addAll( querySpaces, propertySpaces ); } } if( querySpaces == null ) { return Collections.emptySet(); } } return querySpaces; }
Returns:true if the list is empty.
/** * @return true if the list is empty. */
public boolean isEmpty() { return executables.isEmpty(); }
Removes the entry at position index in the list.
Params:
  • index – The index of the element to remove
Returns:the entry that was removed
/** * Removes the entry at position index in the list. * * @param index The index of the element to remove * * @return the entry that was removed */
public E remove(int index) { // removals are generally safe in regards to sorting... final E e = executables.remove( index ); // If the executable being removed defined query spaces we need to recalculate the overall query spaces for // this list. The problem is that we don't know how many other executable instances in the list also // contributed those query spaces as well. // // An alternative here is to use a "multiset" which is a specialized set that keeps a reference count // associated to each entry. But that is likely overkill here. if ( e.getPropertySpaces() != null && e.getPropertySpaces().length > 0 ) { querySpaces = null; } return e; }
Clears the list of executions.
/** * Clears the list of executions. */
public void clear() { executables.clear(); querySpaces = null; sorted = requiresSorting; }
Removes the last n entries from the list.
Params:
  • n – The number of elements to remove.
/** * Removes the last n entries from the list. * * @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 ) { // querySpaces could now be incorrect querySpaces = null; break; } } executables.subList( size - n, size ).clear(); } }
Add an Executable to this list.
Params:
  • executable – the executable to add to the list
Returns:true if the object was added to the list
/** * Add an Executable to this list. * * @param executable the executable to add to the list * * @return true if the object was added to the list */
public boolean add(E executable) { final E previousLast = sorter != null || executables.isEmpty() ? null : executables.get( executables.size() - 1 ); boolean added = executables.add( executable ); if ( !added ) { return false; } // if it was sorted before the addition, then check if the addition invalidated the sorting if ( sorted ) { if ( sorter != null ) { // we don't have intrinsic insight into the sorter's algorithm, so invalidate sorting sorted = false; } else { // otherwise, we added to the end of the list. So check the comparison between the incoming // executable and the one previously at the end of the list using the Comparable contract if ( previousLast != null && previousLast.compareTo( executable ) > 0 ) { sorted = false; } } } Serializable[] querySpaces = executable.getPropertySpaces(); if ( this.querySpaces != null && querySpaces != null ) { Collections.addAll( this.querySpaces, querySpaces ); } return added; }
Sorts the list using the natural ordering or using the Sorter if it's not null.
/** * Sorts the list using the natural ordering or using the Sorter if it's not null. */
@SuppressWarnings("unchecked") public void sort() { if ( sorted || !requiresSorting ) { // nothing to do return; } if ( sorter != null ) { sorter.sort( executables ); } else { Collections.sort( executables ); } sorted = true; }
Returns:the current size of the list
/** * @return the current size of the list */
public int size() { return executables.size(); }
Params:
  • index – The index of the element to retrieve
Returns:The element at specified index
/** * @param index The index of the element to retrieve * * @return The element at specified index */
public E get(int index) { return executables.get( index ); }
Returns an iterator for the list. Wraps the list just in case something tries to modify it.
Returns:an unmodifiable iterator
/** * Returns an iterator for the list. Wraps the list just in case something tries to modify it. * * @return an unmodifiable iterator */
@Override public Iterator<E> iterator() { return Collections.unmodifiableList( executables ).iterator(); }
Write this list out to the given stream as part of serialization
Params:
  • oos – The stream to which to serialize our state
/** * Write this list out to the given stream as part of serialization * * @param oos The stream to which to serialize our state */
@Override public void writeExternal(ObjectOutput oos) throws IOException { oos.writeBoolean( sorted ); oos.writeInt( executables.size() ); for ( E e : executables ) { oos.writeObject( e ); } // if the spaces are initialized, write them out for usage after deserialization if ( querySpaces == null ) { oos.writeInt( -1 ); } else { oos.writeInt( querySpaces.size() ); // these are always String, why we treat them as Serializable instead is beyond me... for ( Serializable querySpace : querySpaces ) { oos.writeUTF( querySpace.toString() ); } } }
Read this object state back in from the given stream as part of de-serialization
Params:
  • in – The stream from which to read our serial state
/** * Read this object state back in from the given stream as part of de-serialization * * @param in The stream from which to read our serial state */
@Override @SuppressWarnings("unchecked") public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { sorted = in.readBoolean(); final int numberOfExecutables = in.readInt(); executables.ensureCapacity( numberOfExecutables ); if ( numberOfExecutables > 0 ) { for ( int i = 0; i < numberOfExecutables; i++ ) { E e = (E) in.readObject(); executables.add( e ); } } final int numberOfQuerySpaces = in.readInt(); if ( numberOfQuerySpaces < 0 ) { this.querySpaces = null; } else { querySpaces = new HashSet<Serializable>( CollectionHelper.determineProperSizing( numberOfQuerySpaces ) ); for ( int i = 0; i < numberOfQuerySpaces; i++ ) { querySpaces.add( in.readUTF() ); } } }
Allow the Executables to re-associate themselves with the Session after deserialization.
Params:
  • session – The session to which to associate the Executables
/** * Allow the Executables to re-associate themselves with the Session after deserialization. * * @param session The session to which to associate the Executables */
public void afterDeserialize(SessionImplementor session) { for ( E e : executables ) { e.afterDeserialize( session ); } } public String toString() { return "ExecutableList{size=" + executables.size() + "}"; } }