package com.eucalyptus.util; import java.util.List; import org.apache.log4j.Logger; import com.eucalyptus.records.EventType; import com.eucalyptus.records.EventRecord; /** * @author decker */ public abstract class Transition<O, T extends Comparable> implements Comparable<Transition> { private static Logger LOG = Logger.getLogger( Transition.class ); private final T oldState; private final T newState; private final String name; /** * @param oldState * @param newState */ public Transition( String name, T oldState, T newState ) { this.name = name; this.oldState = oldState; this.newState = newState; } public static <A, B extends Comparable> Transition<A, B> anonymous( Class<? extends Transition<A, B>> r ) throws Exception { return ( Transition<A, B> ) new Transition.anonymously<A, B>( r ); } public static <A, B extends Comparable, L extends Iterable<A>> Transition<A, B> anonymous( L i, Class<? extends Transition<A, B>> r ) throws Throwable { Transition anon = anonymous( r ); for ( A a : i ) { anon.transition( a ); } return ( Transition<A, B> ) anon; } public static <A, B extends Comparable> Transition<A, B> anonymous( B from, B to, Committor<A> r ) throws Exception { return ( Transition<A, B> ) new Transition.anonymously<A, B>( from, to, r ); } public static <A, B extends Comparable, L extends Iterable<A>> Transition<A, B> anonymous( B from, B to, L i, Committor<A> r ) throws Throwable { Transition<A, B> anon = anonymous( from, to, r ); for ( A a : i ) { anon.transition( a ); } return ( Transition<A, B> ) anon; } private static class anonymously<O, T extends Comparable> extends Transition<O, T> { private Committor<O> committor; public anonymously( T from, T to, Committor<O> committor ) { super( "anonymous", from, to ); this.committor = committor; } public anonymously( final Class<? extends Transition<O, T>> c ) throws Exception { this( c.newInstance( ) ); } public anonymously( final Transition<O, T> transition ) { super( transition.getName( ), transition.getOldState( ), transition.getNewState( ) ); this.committor = new Committor<O>( ) { @Override public void commit( O object ) throws Exception { transition.commit( object ); } }; } @Override protected void commit( O component ) throws Exception { this.committor.commit( component ); } @Override protected void post( O component ) throws Exception {} @Override protected void prepare( O component ) throws Exception {} @Override protected void rollback( O component ) {} } /** * Check preconditions and prepare for the transition * * @throws Exception */ protected abstract void prepare( O component ) throws Exception; /** * Perform component specific operations needed to accomplish the state transition. Note that the * component's lifecycle state will only be updated when and if * this operations completes. * * @throws Exception */ protected abstract void commit( O component ) throws Exception; /** * Check postconditions to ensure that the new component state has been entered cleanly. * * @throws Exception */ protected abstract void post( O component ) throws Exception; /** * In the case any operation throws an exception this method is invoked. */ protected abstract void rollback( O component ); /** * @see com.eucalyptus.util.Transition#transition(java.lang.Object) * @param object * @throws Throwable */ public final Transition<O, T> transition( O object ) throws Throwable { if ( object instanceof Iterable ) { for ( O o : ( Iterable<O> ) object ) this.doTransition( o ); } else { this.doTransition( object ); } return this; } private void doTransition( O object ) throws Exception { try { EventRecord.caller( this.getClass( ), EventType.TRANSITION_PREPARE, this.toString( ), object.getClass( ).getCanonicalName( ) ).info( ); this.prepare( object ); EventRecord.caller( this.getClass( ), EventType.TRANSITION_COMMIT, this.toString( ), object.getClass( ).getCanonicalName( ) ).info( ); this.commit( object ); EventRecord.caller( this.getClass( ), EventType.TRANSITION_POST, this.toString( ), object.getClass( ).getCanonicalName( ) ).info( ); this.post( object ); } catch ( Exception e ) { EventRecord.caller( this.getClass( ), EventType.TRANSITION_ROLLBACK, this.toString( ), object.getClass( ).getCanonicalName( ) ).error( ); this.rollback( object ); throw e; } EventRecord.caller( this.getClass( ), EventType.TRANSITION_FINISHED, this.toString( ) ); } public final Transition<O, T> transition( Iterable<O> list ) throws Throwable { for ( O o : list ) this.doTransition( o ); return this; } /** * @see java.lang.Comparable#compareTo(java.lang.Object) * @param that * @return */ @Override public int compareTo( Transition that ) { int ret = 0; if ( ( ret = this.oldState.compareTo( that.oldState ) ) == 0 ) { return this.newState.compareTo( that.newState ); } else { return ret; } } /** * @see java.lang.Object#hashCode() * @return */ @Override public int hashCode( ) { final int prime = 31; int result = 1; result = prime * result + ( ( this.newState == null ) ? 0 : this.newState.hashCode( ) ); result = prime * result + ( ( this.oldState == null ) ? 0 : this.oldState.hashCode( ) ); return result; } /** * @see java.lang.Object#equals(java.lang.Object) * @param obj * @return */ @Override public boolean equals( Object obj ) { if ( this == obj ) return true; if ( obj == null ) return false; if ( getClass( ) != obj.getClass( ) ) return false; Transition other = ( Transition ) obj; if ( this.newState == null ) { if ( other.newState != null ) return false; } else if ( !this.newState.equals( other.newState ) ) return false; if ( this.oldState == null ) { if ( other.oldState != null ) return false; } else if ( !this.oldState.equals( other.oldState ) ) return false; return true; } /** * @return the oldState */ public final T getOldState( ) { return this.oldState; } /** * @return the newState */ public final T getNewState( ) { return this.newState; } /** * @see java.lang.Object#toString() * @return */ @Override public String toString( ) { return String.format( "Transition:from:%s:to:%s", this.oldState, this.newState ); } /** * @return the name */ protected final String getName( ) { return this.name; } protected Transition<O, T> newInstance( ) throws Exception { return this.getClass( ).newInstance( ); } }