/** * Copyright 2012 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.examples.basic; import static org.junit.Assert.*; import org.junit.Test; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandKey; import com.netflix.hystrix.HystrixEventType; import com.netflix.hystrix.HystrixInvokableInfo; import com.netflix.hystrix.HystrixRequestLog; import com.netflix.hystrix.HystrixThreadPoolKey; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; /** * Sample {@link HystrixCommand} that implements fallback logic that requires * network traffic and thus executes another {@link HystrixCommand} from the {@link #getFallback()} method. * <p> * Note also that the fallback command uses a separate thread-pool as well even though * it's in the same command group. * <p> * It needs to be on a separate thread-pool otherwise the first command could saturate it * and the fallback command never have a chance to execute. */ public class CommandWithFallbackViaNetwork extends HystrixCommand<String> { private final int id; protected CommandWithFallbackViaNetwork(int id) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceX")) .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueCommand"))); this.id = id; } @Override protected String run() { // RemoteServiceXClient.getValue(id); throw new RuntimeException("force failure for example"); } @Override protected String getFallback() { return new FallbackViaNetwork(id).execute(); } private static class FallbackViaNetwork extends HystrixCommand<String> { private final int id; public FallbackViaNetwork(int id) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceX")) .andCommandKey(HystrixCommandKey.Factory.asKey("GetValueFallbackCommand")) // use a different threadpool for the fallback command // so saturating the RemoteServiceX pool won't prevent // fallbacks from executing .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("RemoteServiceXFallback"))); this.id = id; } @Override protected String run() { // MemCacheClient.getValue(id); throw new RuntimeException("the fallback also failed"); } @Override protected String getFallback() { // the fallback also failed // so this fallback-of-a-fallback will // fail silently and return null return null; } } public static class UnitTest { @Test public void test() { HystrixRequestContext context = HystrixRequestContext.initializeContext(); try { assertEquals(null, new CommandWithFallbackViaNetwork(1).execute()); HystrixInvokableInfo<?> command1 = HystrixRequestLog.getCurrentRequest().getAllExecutedCommands().toArray(new HystrixInvokableInfo<?>[2])[0]; assertEquals("GetValueCommand", command1.getCommandKey().name()); assertTrue(command1.getExecutionEvents().contains(HystrixEventType.FAILURE)); HystrixInvokableInfo<?> command2 = HystrixRequestLog.getCurrentRequest().getAllExecutedCommands().toArray(new HystrixInvokableInfo<?>[2])[1]; assertEquals("GetValueFallbackCommand", command2.getCommandKey().name()); assertTrue(command2.getExecutionEvents().contains(HystrixEventType.FAILURE)); } finally { context.shutdown(); } } } }