diff --git a/spring-cloud/pom.xml b/spring-cloud/pom.xml
index baf86a4386..7138efc128 100644
--- a/spring-cloud/pom.xml
+++ b/spring-cloud/pom.xml
@@ -25,7 +25,7 @@
spring-cloud-zookeeper
spring-cloud-gateway
spring-cloud-stream
- spring-cloud-stream-starters/twitterhdfs
+ spring-cloud-stream-starters/twitterhdfs
spring-cloud-connectors-heroku
spring-cloud-aws
spring-cloud-consul
@@ -35,9 +35,10 @@
spring-cloud-archaius
spring-cloud-functions
spring-cloud-vault
-
- spring-cloud-task
+
+ spring-cloud-task
spring-cloud-zuul
+ spring-cloud-zuul-fallback
diff --git a/spring-cloud/spring-cloud-zuul-fallback/README.md b/spring-cloud/spring-cloud-zuul-fallback/README.md
new file mode 100644
index 0000000000..2f13c1923b
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/README.md
@@ -0,0 +1,2 @@
+### Relevant Articles:
+- [Fallback for Zuul Route](TODO)
diff --git a/spring-cloud/spring-cloud-zuul-fallback/api-gateway/pom.xml b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/pom.xml
new file mode 100644
index 0000000000..ee0f607d8a
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/pom.xml
@@ -0,0 +1,43 @@
+
+ 4.0.0
+ api-gateway
+ api-gateway
+ API Gateway using Zuul
+ jar
+
+
+ com.baeldung.spring.cloud
+ spring-cloud-zuul-fallback
+ 1.0.0-SNAPSHOT
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-zuul
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-parent
+ ${spring-cloud-dependencies.version}
+ pom
+ import
+
+
+
+
diff --git a/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/java/com/baeldung/spring/cloud/apigateway/ApiGatewayApplication.java b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/java/com/baeldung/spring/cloud/apigateway/ApiGatewayApplication.java
new file mode 100644
index 0000000000..78f489f2bb
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/java/com/baeldung/spring/cloud/apigateway/ApiGatewayApplication.java
@@ -0,0 +1,15 @@
+package com.baeldung.spring.cloud.apigateway;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
+
+@SpringBootApplication
+@EnableZuulProxy
+public class ApiGatewayApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ApiGatewayApplication.class, args);
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/java/com/baeldung/spring/cloud/apigateway/fallback/GatewayClientResponse.java b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/java/com/baeldung/spring/cloud/apigateway/fallback/GatewayClientResponse.java
new file mode 100644
index 0000000000..ce0c7819f1
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/java/com/baeldung/spring/cloud/apigateway/fallback/GatewayClientResponse.java
@@ -0,0 +1,69 @@
+package com.baeldung.spring.cloud.apigateway.fallback;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.client.ClientHttpResponse;
+
+public class GatewayClientResponse implements ClientHttpResponse {
+
+ private HttpStatus status;
+ private String message;
+
+ public GatewayClientResponse(HttpStatus status, String message) {
+ this.status = status;
+ this.message = message;
+ }
+
+ @Override
+ public HttpStatus getStatusCode() throws IOException {
+ return status;
+ }
+
+ @Override
+ public int getRawStatusCode() throws IOException {
+ return status.value();
+ }
+
+ @Override
+ public String getStatusText() throws IOException {
+ return status.getReasonPhrase();
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public InputStream getBody() throws IOException {
+ return new ByteArrayInputStream(message.getBytes());
+ }
+
+ @Override
+ public HttpHeaders getHeaders() {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ return headers;
+ }
+
+ public HttpStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(HttpStatus status) {
+ this.status = status;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/java/com/baeldung/spring/cloud/apigateway/fallback/GatewayServiceFallback.java b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/java/com/baeldung/spring/cloud/apigateway/fallback/GatewayServiceFallback.java
new file mode 100644
index 0000000000..73f72492c9
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/java/com/baeldung/spring/cloud/apigateway/fallback/GatewayServiceFallback.java
@@ -0,0 +1,29 @@
+package com.baeldung.spring.cloud.apigateway.fallback;
+
+import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.stereotype.Component;
+
+import com.netflix.hystrix.exception.HystrixTimeoutException;
+
+@Component
+class GatewayServiceFallback implements FallbackProvider {
+
+ private static final String DEFAULT_MESSAGE = "Service not available.";
+
+ @Override
+ public String getRoute() {
+ return "*"; // or return null;
+ }
+
+ @Override
+ public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
+ if (cause instanceof HystrixTimeoutException) {
+ return new GatewayClientResponse(HttpStatus.GATEWAY_TIMEOUT, DEFAULT_MESSAGE);
+ } else {
+ return new GatewayClientResponse(HttpStatus.INTERNAL_SERVER_ERROR, DEFAULT_MESSAGE);
+ }
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/java/com/baeldung/spring/cloud/apigateway/fallback/WeatherServiceFallback.java b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/java/com/baeldung/spring/cloud/apigateway/fallback/WeatherServiceFallback.java
new file mode 100644
index 0000000000..28fb6fb6e5
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/java/com/baeldung/spring/cloud/apigateway/fallback/WeatherServiceFallback.java
@@ -0,0 +1,29 @@
+package com.baeldung.spring.cloud.apigateway.fallback;
+
+import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.stereotype.Component;
+
+import com.netflix.hystrix.exception.HystrixTimeoutException;
+
+@Component
+class WeatherServiceFallback implements FallbackProvider {
+
+ private static final String DEFAULT_MESSAGE = "Weather information is not available.";
+
+ @Override
+ public String getRoute() {
+ return "weather-service";
+ }
+
+ @Override
+ public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
+ if (cause instanceof HystrixTimeoutException) {
+ return new GatewayClientResponse(HttpStatus.GATEWAY_TIMEOUT, DEFAULT_MESSAGE);
+ } else {
+ return new GatewayClientResponse(HttpStatus.INTERNAL_SERVER_ERROR, DEFAULT_MESSAGE);
+ }
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/resources/application.yml b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/resources/application.yml
new file mode 100644
index 0000000000..4a0662a960
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/main/resources/application.yml
@@ -0,0 +1,22 @@
+spring:
+ application:
+ name: api-gateway
+server:
+ port: 7070
+
+zuul:
+ igoredServices: '*'
+ routes:
+ weather-service:
+ path: /weather/**
+ serviceId: weather-service
+ strip-prefix: false
+
+ribbon:
+ eureka:
+ enabled: false
+
+weather-service:
+ ribbon:
+ listOfServers: localhost:8080
+
diff --git a/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/test/java/com/baeldung/spring/cloud/apigateway/ApiGatewayApplicationIntegrationTest.java b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/test/java/com/baeldung/spring/cloud/apigateway/ApiGatewayApplicationIntegrationTest.java
new file mode 100644
index 0000000000..4052edc1f3
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/test/java/com/baeldung/spring/cloud/apigateway/ApiGatewayApplicationIntegrationTest.java
@@ -0,0 +1,16 @@
+package com.baeldung.spring.cloud.apigateway;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class ApiGatewayApplicationIntegrationTest {
+
+ @Test
+ public void whenSpringContextIsBootstrapped_thenNoExceptions() {
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/test/java/com/baeldung/spring/cloud/apigateway/fallback/GatewayServiceFallbackUnitTest.java b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/test/java/com/baeldung/spring/cloud/apigateway/fallback/GatewayServiceFallbackUnitTest.java
new file mode 100644
index 0000000000..db2f703084
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/test/java/com/baeldung/spring/cloud/apigateway/fallback/GatewayServiceFallbackUnitTest.java
@@ -0,0 +1,46 @@
+package com.baeldung.spring.cloud.apigateway.fallback;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.netflix.hystrix.exception.HystrixTimeoutException;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class GatewayServiceFallbackUnitTest {
+
+ private static final String ROUTE = "*";
+
+ @Autowired
+ private GatewayServiceFallback fallback;
+
+ @Test
+ public void testWhenGetRouteThenReturnWeatherServiceRoute() {
+ assertEquals(ROUTE, fallback.getRoute());
+
+ }
+
+ @Test
+ public void testFallbackResponse_whenHystrixException_thenGatewayTimeout() throws Exception {
+ HystrixTimeoutException exception = new HystrixTimeoutException();
+ ClientHttpResponse response = fallback.fallbackResponse(ROUTE, exception);
+
+ assertEquals(HttpStatus.GATEWAY_TIMEOUT, response.getStatusCode());
+ }
+
+ @Test
+ public void testFallbackResponse_whenNonHystrixException_thenInternalServerError() throws Exception {
+ RuntimeException exception = new RuntimeException("Test exception");
+ ClientHttpResponse response = fallback.fallbackResponse(ROUTE, exception);
+
+ assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/test/java/com/baeldung/spring/cloud/apigateway/fallback/WeatherServiceFallbackUnitTest.java b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/test/java/com/baeldung/spring/cloud/apigateway/fallback/WeatherServiceFallbackUnitTest.java
new file mode 100644
index 0000000000..ccd59531b5
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/api-gateway/src/test/java/com/baeldung/spring/cloud/apigateway/fallback/WeatherServiceFallbackUnitTest.java
@@ -0,0 +1,46 @@
+package com.baeldung.spring.cloud.apigateway.fallback;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.test.context.junit4.SpringRunner;
+
+import com.netflix.hystrix.exception.HystrixTimeoutException;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class WeatherServiceFallbackUnitTest {
+
+ private static final String ROUTE = "weather-service";
+
+ @Autowired
+ private WeatherServiceFallback fallback;
+
+ @Test
+ public void testWhenGetRouteThenReturnWeatherServiceRoute() {
+ assertEquals(ROUTE, fallback.getRoute());
+
+ }
+
+ @Test
+ public void testFallbackResponse_whenHystrixException_thenGatewayTimeout() throws Exception {
+ HystrixTimeoutException exception = new HystrixTimeoutException();
+ ClientHttpResponse response = fallback.fallbackResponse(ROUTE, exception);
+
+ assertEquals(HttpStatus.GATEWAY_TIMEOUT, response.getStatusCode());
+ }
+
+ @Test
+ public void testFallbackResponse_whenNonHystrixException_thenInternalServerError() throws Exception {
+ RuntimeException exception = new RuntimeException("Test exception");
+ ClientHttpResponse response = fallback.fallbackResponse(ROUTE, exception);
+
+ assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-zuul-fallback/pom.xml b/spring-cloud/spring-cloud-zuul-fallback/pom.xml
new file mode 100644
index 0000000000..5bdd1d49ca
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/pom.xml
@@ -0,0 +1,28 @@
+
+
+ 4.0.0
+ spring-cloud-zuul-fallback
+ pom
+ spring-cloud-zuul-fallback
+ Spring Cloud Zuul Fallback
+
+
+ com.baeldung.spring.cloud
+ spring-cloud
+ 1.0.0-SNAPSHOT
+ ..
+
+
+
+ api-gateway
+ weather-service
+
+
+
+ 1.8
+ Finchley.SR2
+ 3.1.1
+
+
diff --git a/spring-cloud/spring-cloud-zuul-fallback/weather-service/pom.xml b/spring-cloud/spring-cloud-zuul-fallback/weather-service/pom.xml
new file mode 100644
index 0000000000..611f1dcd4f
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/weather-service/pom.xml
@@ -0,0 +1,39 @@
+
+ 4.0.0
+ weather-service
+ Weather Service
+ Weather Service for Zuul Fallback Test
+
+
+ com.baeldung.spring.cloud
+ spring-cloud-zuul-fallback
+ 1.0.0-SNAPSHOT
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-parent
+ ${spring-cloud-dependencies.version}
+ pom
+ import
+
+
+
+
diff --git a/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/main/java/com/baeldung/spring/cloud/weatherservice/WeatherController.java b/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/main/java/com/baeldung/spring/cloud/weatherservice/WeatherController.java
new file mode 100644
index 0000000000..3cf451dcf7
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/main/java/com/baeldung/spring/cloud/weatherservice/WeatherController.java
@@ -0,0 +1,16 @@
+package com.baeldung.spring.cloud.weatherservice;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/weather")
+public class WeatherController {
+
+ @GetMapping("/today")
+ public String getMessage() {
+ return "It's a bright sunny day today!";
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/main/java/com/baeldung/spring/cloud/weatherservice/WeatherServiceApplication.java b/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/main/java/com/baeldung/spring/cloud/weatherservice/WeatherServiceApplication.java
new file mode 100644
index 0000000000..3a6d85e8d1
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/main/java/com/baeldung/spring/cloud/weatherservice/WeatherServiceApplication.java
@@ -0,0 +1,13 @@
+package com.baeldung.spring.cloud.weatherservice;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class WeatherServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(WeatherServiceApplication.class, args);
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/main/resources/application.yml b/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/main/resources/application.yml
new file mode 100644
index 0000000000..242f842f44
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/main/resources/application.yml
@@ -0,0 +1,4 @@
+spring:
+ application:
+ name: weather-service
+
diff --git a/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/test/java/com/baeldung/spring/cloud/weatherservice/WeatherControllerIntegrationTest.java b/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/test/java/com/baeldung/spring/cloud/weatherservice/WeatherControllerIntegrationTest.java
new file mode 100644
index 0000000000..3ffd2b0e7a
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/test/java/com/baeldung/spring/cloud/weatherservice/WeatherControllerIntegrationTest.java
@@ -0,0 +1,31 @@
+package com.baeldung.spring.cloud.weatherservice;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+
+@RunWith(SpringRunner.class)
+@WebMvcTest(WeatherController.class)
+public class WeatherControllerIntegrationTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Test
+ public void whenWeatherControllerInvoked_thenReturnWeatherInformation() throws Exception {
+ this.mockMvc.perform(get("/weather/today"))
+ .andDo(print())
+ .andExpect(status().isOk())
+ .andExpect(content().string(containsString("bright sunny day")));
+ }
+
+}
diff --git a/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/test/java/com/baeldung/spring/cloud/weatherservice/WeatherServiceApplicationIntegrationTest.java b/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/test/java/com/baeldung/spring/cloud/weatherservice/WeatherServiceApplicationIntegrationTest.java
new file mode 100644
index 0000000000..fec7ec1560
--- /dev/null
+++ b/spring-cloud/spring-cloud-zuul-fallback/weather-service/src/test/java/com/baeldung/spring/cloud/weatherservice/WeatherServiceApplicationIntegrationTest.java
@@ -0,0 +1,16 @@
+package com.baeldung.spring.cloud.weatherservice;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+public class WeatherServiceApplicationIntegrationTest {
+
+ @Test
+ public void whenSpringContextIsBootstrapped_thenNoExceptions() {
+ }
+
+}