Olga Maciaszek-Sharma
4 years ago
committed by
GitHub
6 changed files with 304 additions and 94 deletions
@ -0,0 +1,159 @@
@@ -0,0 +1,159 @@
|
||||
/* |
||||
* Copyright 2013-2021 the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.cloud.openfeign.circuitbreaker; |
||||
|
||||
import java.util.function.Function; |
||||
|
||||
import org.apache.commons.logging.Log; |
||||
import org.apache.commons.logging.LogFactory; |
||||
import org.junit.jupiter.api.AfterAll; |
||||
import org.junit.jupiter.api.BeforeAll; |
||||
import org.junit.jupiter.api.BeforeEach; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; |
||||
import org.springframework.boot.test.context.SpringBootTest; |
||||
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; |
||||
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; |
||||
import org.springframework.cloud.client.circuitbreaker.ConfigBuilder; |
||||
import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException; |
||||
import org.springframework.cloud.openfeign.EnableFeignClients; |
||||
import org.springframework.cloud.openfeign.FeignClient; |
||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.Import; |
||||
import org.springframework.test.annotation.DirtiesContext; |
||||
import org.springframework.util.SocketUtils; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RequestMethod; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
|
||||
/** |
||||
* Tests for Feign calls with CircuitBreaker, without fallbacks. |
||||
* |
||||
* @author Olga Maciaszek-Sharma |
||||
*/ |
||||
@SpringBootTest(classes = CircuitBreakerWithNoFallbackTests.Application.class, |
||||
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT, |
||||
value = { "spring.application.name=springcircuittest", "spring.jmx.enabled=false", |
||||
"feign.circuitbreaker.enabled=true" }) |
||||
@DirtiesContext |
||||
public class CircuitBreakerWithNoFallbackTests { |
||||
|
||||
@Autowired |
||||
MyCircuitBreaker myCircuitBreaker; |
||||
|
||||
@Autowired |
||||
CircuitBreakerTestClient testClient; |
||||
|
||||
@BeforeAll |
||||
public static void beforeClass() { |
||||
System.setProperty("server.port", |
||||
String.valueOf(SocketUtils.findAvailableTcpPort())); |
||||
} |
||||
|
||||
@AfterAll |
||||
public static void afterClass() { |
||||
System.clearProperty("server.port"); |
||||
} |
||||
|
||||
@BeforeEach |
||||
public void setup() { |
||||
this.myCircuitBreaker.clear(); |
||||
} |
||||
|
||||
@Test |
||||
public void testSimpleTypeWithFallback() { |
||||
Hello hello = testClient.getHello(); |
||||
|
||||
assertThat(hello).as("hello was null").isNotNull(); |
||||
assertThat(hello).as("first hello didn't match") |
||||
.isEqualTo(new Hello("hello world 1")); |
||||
assertThat(myCircuitBreaker.runWasCalled).as("Circuit Breaker was called") |
||||
.isTrue(); |
||||
} |
||||
|
||||
@Test |
||||
public void test404WithoutFallback() { |
||||
assertThatThrownBy(() -> testClient.getException()) |
||||
.isInstanceOf(NoFallbackAvailableException.class); |
||||
} |
||||
|
||||
@FeignClient(name = "test", url = "http://localhost:${server.port}/") |
||||
protected interface CircuitBreakerTestClient { |
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hello") |
||||
Hello getHello(); |
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/hellonotfound") |
||||
String getException(); |
||||
|
||||
} |
||||
|
||||
@Configuration(proxyBeanMethods = false) |
||||
@EnableAutoConfiguration |
||||
@RestController |
||||
@EnableFeignClients(clients = { CircuitBreakerTestClient.class }) |
||||
@Import(NoSecurityConfiguration.class) |
||||
protected static class Application implements CircuitBreakerTestClient { |
||||
|
||||
static final Log log = LogFactory.getLog(Application.class); |
||||
|
||||
@Bean |
||||
MyCircuitBreaker myCircuitBreaker() { |
||||
return new MyCircuitBreaker(); |
||||
} |
||||
|
||||
@Bean |
||||
CircuitBreakerFactory circuitBreakerFactory(MyCircuitBreaker myCircuitBreaker) { |
||||
return new CircuitBreakerFactory() { |
||||
@Override |
||||
public CircuitBreaker create(String id) { |
||||
log.info("Creating a circuit breaker with id [" + id + "]"); |
||||
return myCircuitBreaker; |
||||
} |
||||
|
||||
@Override |
||||
protected ConfigBuilder configBuilder(String id) { |
||||
return Object::new; |
||||
} |
||||
|
||||
@Override |
||||
public void configureDefault(Function defaultConfiguration) { |
||||
|
||||
} |
||||
}; |
||||
} |
||||
|
||||
@Override |
||||
public Hello getHello() { |
||||
return new Hello("hello world 1"); |
||||
} |
||||
|
||||
@Override |
||||
public String getException() { |
||||
throw new IllegalStateException("BOOM!"); |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
@ -0,0 +1,60 @@
@@ -0,0 +1,60 @@
|
||||
/* |
||||
* Copyright 2013-2021 the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.cloud.openfeign.circuitbreaker; |
||||
|
||||
import java.util.Objects; |
||||
|
||||
/** |
||||
* @author Marcin Grzejszczak |
||||
*/ |
||||
class Hello { |
||||
|
||||
private String message; |
||||
|
||||
public Hello() { |
||||
} |
||||
|
||||
public Hello(String message) { |
||||
this.message = message; |
||||
} |
||||
|
||||
public String getMessage() { |
||||
return this.message; |
||||
} |
||||
|
||||
public void setMessage(String message) { |
||||
this.message = message; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object o) { |
||||
if (this == o) { |
||||
return true; |
||||
} |
||||
if (o == null || getClass() != o.getClass()) { |
||||
return false; |
||||
} |
||||
Hello that = (Hello) o; |
||||
return Objects.equals(this.message, that.message); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Objects.hash(this.message); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
/* |
||||
* Copyright 2013-2021 the original author or authors. |
||||
* |
||||
* 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 |
||||
* |
||||
* https://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.springframework.cloud.openfeign.circuitbreaker; |
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean; |
||||
import java.util.function.Function; |
||||
import java.util.function.Supplier; |
||||
|
||||
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; |
||||
import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException; |
||||
|
||||
/** |
||||
* @author Marcin Grzejszczak |
||||
* @author Olga Maciaszek-Sharma |
||||
*/ |
||||
class MyCircuitBreaker implements CircuitBreaker { |
||||
|
||||
AtomicBoolean runWasCalled = new AtomicBoolean(); |
||||
|
||||
@Override |
||||
public <T> T run(Supplier<T> toRun) { |
||||
try { |
||||
this.runWasCalled.set(true); |
||||
return toRun.get(); |
||||
} |
||||
catch (Throwable throwable) { |
||||
throw new NoFallbackAvailableException("No fallback available.", throwable); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) { |
||||
try { |
||||
return run(toRun); |
||||
} |
||||
catch (Throwable throwable) { |
||||
return fallback.apply(throwable); |
||||
} |
||||
} |
||||
|
||||
public void clear() { |
||||
this.runWasCalled.set(false); |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue