/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.usergrid.chop.stack;
import java.util.Collections;
import java.util.Formatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.junit.Assert;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
/**
* Represents the setup state of a stack
*/
public enum SetupStackState {
// JarNotFound ==> (setup signal) ==> SettingUp
// JarNotFound ==> (deploy signal) ==> NotSetUp
JarNotFound( 5, new SetupStackSignal[] { SetupStackSignal.DEPLOY, SetupStackSignal.SETUP },
new Integer[] { 3, 1 },
"No runner jars found with given parameters, deploy first.",
"%s signal rejected. When JarNotFound only DEPLOY and SETUP signal(s) which cause to transition into " +
"NotSetUp and SettingUp state(s) respectively" ),
// Destroying ==> (Complete signal) ==> NotSetUp
Destroying( 4, new SetupStackSignal[] { SetupStackSignal.COMPLETE },
new Integer[] { 3 },
"Currently being destroyed. Wait until it is finished to set up again.",
"%s signal rejected. When Destroying only COMPLETE signal(s) can be sent which cause to " +
"transition into NotSetUp state(s) respectively" ),
// NotSetUp ==> (deploy signal) ==> NotSetUp
// NotSetUp ==> (setup signal) ==> SettingUp
NotSetUp( 3, new SetupStackSignal[] { SetupStackSignal.SETUP, SetupStackSignal.DEPLOY },
new Integer[] { 1, 3 },
"Jar is deployed but no stack set up with it.",
"%s signal rejected. When NotSetUp only SETUP and DEPLOY signal(s) which cause to transition into " +
"SettingUp and NotSetUp state(s) respectively" ),
// SetupFailed ==> (setup deploy) ==> NotSetUp
// SetupFailed ==> (setup signal) ==> SettingUp
SetupFailed( 2, new SetupStackSignal[] { SetupStackSignal.SETUP, SetupStackSignal.DEPLOY },
new Integer[] { 1, 3 },
"Stack was registered, however its setup failed. Call setup again to restart.",
"%s signal rejected. When SetupFailed only SETUP and DEPLOY signal(s) which cause to transition into " +
"SettingUp and NotSetUp state(s) respectively" ),
// SettingUp ==> (Fail signal) ==> SetupFailed
// SettingUp ==> (Complete signal) ==> SetUp
SettingUp( 1, new SetupStackSignal[] { SetupStackSignal.COMPLETE, SetupStackSignal.FAIL},
new Integer[] { 0, 2 },
"Setting up the stack right now.",
"%s signal rejected. When SettingUp only COMPLETE and FAIL signal(s) can be sent which " +
"cause to transition into SetUp and SetupFailed state(s) respectively" ),
// SetUp ==> (destroy signal) ==> NotSetUp
SetUp( 0, new SetupStackSignal[] { SetupStackSignal.DESTROY },
new Integer[] { 4 },
"Stack is set up and ready to start the tests.",
"%s signal rejected. When SetUp only DESTROY signal(s) which cause to transition into " +
"NotSetUp state(s) respectively" );
private static final Logger LOG = LoggerFactory.getLogger( SetupStackState.class );
private static final String SUCCESS_MSG = "%s signal accepted, transitioning from %s state to %s";
private final String stackStateMessage;
private final int stateID;
private final Map<SetupStackSignal, Integer> signalsCorrespondingStateIDs;
private final Set<SetupStackSignal> acceptedStates;
private final String rejectedMessage;
private SetupStackState( int stateID, SetupStackSignal[] signals, Integer[] states, String stackStateMessage,
String rejectedMessage ) {
Assert.assertTrue( states.length == signals.length );
this.stackStateMessage = stackStateMessage;
this.stateID = stateID;
this.rejectedMessage = rejectedMessage;
signalsCorrespondingStateIDs = getCorrespondingStateIDs( signals, states );
acceptedStates = getAcceptedStates( signals );
}
public String getStackStateMessage() {
return stackStateMessage;
}
public String getMessage( SetupStackSignal signal ) {
Formatter formatter = new Formatter();
if ( accepts( signal ) ) {
return formatter.format( SUCCESS_MSG, new Object[] { signal, this, next( signal ) } ).toString();
}
return formatter.format( rejectedMessage, signal ).toString();
}
public int getStateID() {
return stateID;
}
/**
* Check to see if the state accepts a signal: meaning is the signal a
* valid signal to produce a state transition.
*
* @param signal the signal to check
* @return true if the signal will be accepted, false otherwise
*/
public boolean accepts( SetupStackSignal signal ) {
if ( signal == null || acceptedStates == null)
return false;
return acceptedStates.contains( signal );
}
/**
* Check to see if the state accepts a signal: in other words is the signal a
* valid signal to produce a state transition and does that transition lead
* to the supplied 'next' state parameter.
*
* @param signal the signal to check
* @param next the next state to transit to
* @return true if the signal will be accepted and the next state will be the
* supplied state, false otherwise
*/
public boolean accepts( SetupStackSignal signal, SetupStackState next ) {
if ( signal == null || next == null ) {
return false;
}
if ( acceptedStates == null ) {
return false;
}
if ( ! acceptedStates.contains( signal ) ) {
return false;
}
Integer stateID = signalsCorrespondingStateIDs.get( signal );
if ( stateID == null ) {
return false;
}
SetupStackState realNext = get( stateID );
if ( realNext == null ) {
return false;
}
return realNext.equals( next );
}
public SetupStackState get( Integer stateID ) {
Preconditions.checkNotNull( stateID, "The stateID cannot be null: state = {}", toString() );
switch ( stateID ) {
case 0:
return SetUp;
case 1:
return SettingUp;
case 2:
return SetupFailed;
case 3:
return NotSetUp;
case 4:
return Destroying;
case 5:
return JarNotFound;
}
throw new RuntimeException( "Should never get here!" );
}
public SetupStackState next( SetupStackSignal signal ) {
if ( signal == null ) {
return null;
}
Integer stateID = signalsCorrespondingStateIDs.get( signal );
if ( stateID == null ) {
return null;
}
LOG.debug( "Got signal {} in {} state: stateID = " + stateID, signal, toString() );
return get( stateID );
}
private static Map<SetupStackSignal, Integer> getCorrespondingStateIDs( SetupStackSignal[] signals,
Integer[] states ) {
Assert.assertTrue( signals.length == states.length );
if ( signals.length == 0 ) {
return null;
}
Map<SetupStackSignal, Integer> signalsCorrespondingStateIDs = new HashMap<SetupStackSignal, Integer>( signals.length );
for ( int ii = 0; ii < signals.length; ii++ ) {
signalsCorrespondingStateIDs.put( signals[ii], states[ii] );
}
return Collections.unmodifiableMap( signalsCorrespondingStateIDs );
}
public Set<SetupStackSignal> getAcceptedStates( SetupStackSignal[] signals ) {
if ( signals.length == 0 ) {
return null;
}
Set<SetupStackSignal> acceptedStates = new HashSet<SetupStackSignal>( signals.length );
Collections.addAll( acceptedStates, signals );
return acceptedStates;
}
}