/** * 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.camel.component.hystrix.processor; import java.io.IOException; import org.apache.camel.CamelExecutionException; import org.apache.camel.Exchange; import org.apache.camel.Processor; import org.apache.camel.RoutesBuilder; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.test.junit4.CamelTestSupport; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import static org.apache.camel.component.hystrix.processor.HystrixConstants.HYSTRIX_RESPONSE_SHORT_CIRCUITED; import static org.apache.camel.component.hystrix.processor.HystrixConstants.HYSTRIX_RESPONSE_SUCCESSFUL_EXECUTION; public class HystrixCircuitOpenTest extends CamelTestSupport { public static final Integer REQUEST_VOLUME_THRESHOLD = 4; private static final Logger LOG = LoggerFactory.getLogger(HystrixCircuitOpenTest.class); private HystrixExceptionRoute route = new HystrixExceptionRoute(); @Test public void testCircuitOpen() throws Exception { LOG.info("testCircuitOpen start"); // failing requests route.throwException = true; for (int i = 0; i < 2 * REQUEST_VOLUME_THRESHOLD; i++) { try { template.asyncRequestBody("direct:start", "Request Body"); } catch (CamelExecutionException e) { LOG.info(e.toString()); } } Thread.sleep(1500); resetMocks(); // notice this can be flaky due timing when using thread sleeps in unit tests getMockEndpoint("mock:result").expectedPropertyReceived(HYSTRIX_RESPONSE_SHORT_CIRCUITED, true); route.throwException = false; try { template.requestBody("direct:start", "Request Body"); LOG.info("Instead circuit open expected"); } catch (CamelExecutionException e) { LOG.info("Circuit open expected ", e); } assertMockEndpointsSatisfied(); // wait for the circuit to try an other request Thread.sleep(500); for (int i = 0; i < 2 * REQUEST_VOLUME_THRESHOLD; i++) { try { template.requestBody("direct:start", "Request Body"); LOG.info("Circuit has closed"); } catch (CamelExecutionException e) { Thread.sleep(i * 100); LOG.info("Circuit will be closed soon " + e.toString()); } } resetMocks(); getMockEndpoint("mock:result").expectedPropertyReceived(HYSTRIX_RESPONSE_SHORT_CIRCUITED, false); getMockEndpoint("mock:result").expectedPropertyReceived(HYSTRIX_RESPONSE_SUCCESSFUL_EXECUTION, true); template.requestBody("direct:start", "Request Body"); assertMockEndpointsSatisfied(); } @Override protected RoutesBuilder createRouteBuilder() throws Exception { return route; } class HystrixExceptionRoute extends RouteBuilder { volatile boolean throwException = true; @Override public void configure() throws Exception { from("direct:start") .hystrix() .hystrixConfiguration() .executionTimeoutInMilliseconds(100) .circuitBreakerRequestVolumeThreshold(REQUEST_VOLUME_THRESHOLD) .metricsRollingStatisticalWindowInMilliseconds(1000) .circuitBreakerSleepWindowInMilliseconds(2000) .end() .log("Hystrix processing start: ${threadName}") .process(new Processor() { @Override public void process(Exchange exchange) throws Exception { if (throwException) { LOG.info("Will throw exception"); throw new IOException("Route has failed"); } else { LOG.info("Will NOT throw exception"); } } }) .log("Hystrix processing end: ${threadName}") .end() .log(HYSTRIX_RESPONSE_SHORT_CIRCUITED + " = ${exchangeProperty." + HYSTRIX_RESPONSE_SHORT_CIRCUITED + "}") .to("mock:result"); } } }