/**
* Copyright 2015 Netflix, Inc.
*
* Licensed 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 com.netflix.hystrix;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import com.netflix.hystrix.exception.HystrixRuntimeException.FailureType;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import rx.Notification;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
class TestableExecutionHook extends HystrixCommandExecutionHook {
private static void recordHookCall(StringBuilder sequenceRecorder, String methodName) {
sequenceRecorder.append(methodName).append(" - ");
}
StringBuilder executionSequence = new StringBuilder();
List<Notification<?>> commandEmissions = new ArrayList<Notification<?>>();
List<Notification<?>> executionEmissions = new ArrayList<Notification<?>>();
List<Notification<?>> fallbackEmissions = new ArrayList<Notification<?>>();
public boolean commandEmissionsMatch(int numOnNext, int numOnError, int numOnCompleted) {
return eventsMatch(commandEmissions, numOnNext, numOnError, numOnCompleted);
}
public boolean executionEventsMatch(int numOnNext, int numOnError, int numOnCompleted) {
return eventsMatch(executionEmissions, numOnNext, numOnError, numOnCompleted);
}
public boolean fallbackEventsMatch(int numOnNext, int numOnError, int numOnCompleted) {
return eventsMatch(fallbackEmissions, numOnNext, numOnError, numOnCompleted);
}
private boolean eventsMatch(List<Notification<?>> l, int numOnNext, int numOnError, int numOnCompleted) {
boolean matchFailed = false;
int actualOnNext = 0;
int actualOnError = 0;
int actualOnCompleted = 0;
if (l.size() != numOnNext + numOnError + numOnCompleted) {
System.out.println("Actual : " + l + ", Expected : " + numOnNext + " OnNexts, " + numOnError + " OnErrors, " + numOnCompleted + " OnCompleted");
return false;
}
for (int n = 0; n < numOnNext; n++) {
Notification<?> current = l.get(n);
if (!current.isOnNext()) {
matchFailed = true;
} else {
actualOnNext++;
}
}
for (int e = numOnNext; e < numOnNext + numOnError; e++) {
Notification<?> current = l.get(e);
if (!current.isOnError()) {
matchFailed = true;
} else {
actualOnError++;
}
}
for (int c = numOnNext + numOnError; c < numOnNext + numOnError + numOnCompleted; c++) {
Notification<?> current = l.get(c);
if (!current.isOnCompleted()) {
matchFailed = true;
} else {
actualOnCompleted++;
}
}
if (matchFailed) {
System.out.println("Expected : " + numOnNext + " OnNexts, " + numOnError + " OnErrors, and " + numOnCompleted);
System.out.println("Actual : " + actualOnNext + " OnNexts, " + actualOnError + " OnErrors, and " + actualOnCompleted);
}
return !matchFailed;
}
public Throwable getCommandException() {
return getException(commandEmissions);
}
public Throwable getExecutionException() {
return getException(executionEmissions);
}
public Throwable getFallbackException() {
return getException(fallbackEmissions);
}
private Throwable getException(List<Notification<?>> l) {
for (Notification<?> n: l) {
if (n.isOnError()) {
n.getThrowable().printStackTrace();
return n.getThrowable();
}
}
return null;
}
@Override
public <T> void onStart(HystrixInvokable<T> commandInstance) {
super.onStart(commandInstance);
recordHookCall(executionSequence, "onStart");
}
@Override
public <T> T onEmit(HystrixInvokable<T> commandInstance, T value) {
commandEmissions.add(Notification.createOnNext(value));
recordHookCall(executionSequence, "onEmit");
return super.onEmit(commandInstance, value);
}
@Override
public <T> Exception onError(HystrixInvokable<T> commandInstance, FailureType failureType, Exception e) {
commandEmissions.add(Notification.createOnError(e));
recordHookCall(executionSequence, "onError");
return super.onError(commandInstance, failureType, e);
}
@Override
public <T> void onSuccess(HystrixInvokable<T> commandInstance) {
commandEmissions.add(Notification.createOnCompleted());
recordHookCall(executionSequence, "onSuccess");
super.onSuccess(commandInstance);
}
@Override
public <T> void onThreadStart(HystrixInvokable<T> commandInstance) {
super.onThreadStart(commandInstance);
recordHookCall(executionSequence, "onThreadStart");
}
@Override
public <T> void onThreadComplete(HystrixInvokable<T> commandInstance) {
super.onThreadComplete(commandInstance);
recordHookCall(executionSequence, "onThreadComplete");
}
@Override
public <T> void onExecutionStart(HystrixInvokable<T> commandInstance) {
recordHookCall(executionSequence, "onExecutionStart");
super.onExecutionStart(commandInstance);
}
@Override
public <T> T onExecutionEmit(HystrixInvokable<T> commandInstance, T value) {
executionEmissions.add(Notification.createOnNext(value));
recordHookCall(executionSequence, "onExecutionEmit");
return super.onExecutionEmit(commandInstance, value);
}
@Override
public <T> Exception onExecutionError(HystrixInvokable<T> commandInstance, Exception e) {
executionEmissions.add(Notification.createOnError(e));
recordHookCall(executionSequence, "onExecutionError");
return super.onExecutionError(commandInstance, e);
}
@Override
public <T> void onExecutionSuccess(HystrixInvokable<T> commandInstance) {
executionEmissions.add(Notification.createOnCompleted());
recordHookCall(executionSequence, "onExecutionSuccess");
super.onExecutionSuccess(commandInstance);
}
@Override
public <T> void onFallbackStart(HystrixInvokable<T> commandInstance) {
super.onFallbackStart(commandInstance);
recordHookCall(executionSequence, "onFallbackStart");
}
@Override
public <T> T onFallbackEmit(HystrixInvokable<T> commandInstance, T value) {
fallbackEmissions.add(Notification.createOnNext(value));
recordHookCall(executionSequence, "onFallbackEmit");
return super.onFallbackEmit(commandInstance, value);
}
@Override
public <T> Exception onFallbackError(HystrixInvokable<T> commandInstance, Exception e) {
fallbackEmissions.add(Notification.createOnError(e));
recordHookCall(executionSequence, "onFallbackError");
return super.onFallbackError(commandInstance, e);
}
@Override
public <T> void onFallbackSuccess(HystrixInvokable<T> commandInstance) {
fallbackEmissions.add(Notification.createOnCompleted());
recordHookCall(executionSequence, "onFallbackSuccess");
super.onFallbackSuccess(commandInstance);
}
@Override
public <T> void onCacheHit(HystrixInvokable<T> commandInstance) {
super.onCacheHit(commandInstance);
recordHookCall(executionSequence, "onCacheHit");
}
@Override
public <T> void onUnsubscribe(HystrixInvokable<T> commandInstance) {
super.onUnsubscribe(commandInstance);
recordHookCall(executionSequence, "onUnsubscribe");
}
/**
* DEPRECATED METHODS FOLLOW. The string representation starts with `!` to distinguish
*/
AtomicInteger startExecute = new AtomicInteger();
Object endExecuteSuccessResponse = null;
Exception endExecuteFailureException = null;
HystrixRuntimeException.FailureType endExecuteFailureType = null;
AtomicInteger startRun = new AtomicInteger();
Object runSuccessResponse = null;
Exception runFailureException = null;
AtomicInteger startFallback = new AtomicInteger();
Object fallbackSuccessResponse = null;
Exception fallbackFailureException = null;
AtomicInteger threadStart = new AtomicInteger();
AtomicInteger threadComplete = new AtomicInteger();
AtomicInteger cacheHit = new AtomicInteger();
@Override
public <T> T onFallbackSuccess(HystrixInvokable<T> commandInstance, T response) {
recordHookCall(executionSequence, "!onFallbackSuccess");
return super.onFallbackSuccess(commandInstance, response);
}
@Override
public <T> T onComplete(HystrixInvokable<T> commandInstance, T response) {
recordHookCall(executionSequence, "!onComplete");
return super.onComplete(commandInstance, response);
}
@Override
public <T> void onRunStart(HystrixInvokable<T> commandInstance) {
super.onRunStart(commandInstance);
recordHookCall(executionSequence, "!onRunStart");
}
@Override
public <T> T onRunSuccess(HystrixInvokable<T> commandInstance, T response) {
recordHookCall(executionSequence, "!onRunSuccess");
return super.onRunSuccess(commandInstance, response);
}
@Override
public <T> Exception onRunError(HystrixInvokable<T> commandInstance, Exception e) {
recordHookCall(executionSequence, "!onRunError");
return super.onRunError(commandInstance, e);
}
}