Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
spring-cloud-netflix
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
openSource
spring-cloud-netflix
Commits
24d0bedf
Commit
24d0bedf
authored
Sep 08, 2017
by
Ryan Baxter
Committed by
GitHub
Sep 08, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2255 from DominikMostek/fallback_cause
Fallback cause is propagated to fallback provider
parents
38b8786e
4dc5088f
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
386 additions
and
2 deletions
+386
-2
spring-cloud-netflix.adoc
docs/src/main/asciidoc/spring-cloud-netflix.adoc
+62
-0
FallbackProvider.java
...rk/cloud/netflix/zuul/filters/route/FallbackProvider.java
+36
-0
ZuulFallbackProvider.java
...loud/netflix/zuul/filters/route/ZuulFallbackProvider.java
+2
-0
AbstractRibbonCommand.java
...lix/zuul/filters/route/support/AbstractRibbonCommand.java
+22
-2
RibbonCommandCauseFallbackPropagationTest.java
...te/support/RibbonCommandCauseFallbackPropagationTest.java
+264
-0
No files found.
docs/src/main/asciidoc/spring-cloud-netflix.adoc
View file @
24d0bedf
...
...
@@ -1978,6 +1978,68 @@ class MyFallbackProvider implements ZuulFallbackProvider {
}
----
If
you
would
like
to
choose
the
response
based
on
the
cause
of
the
failure
use
`
FallbackProvider
`
which
will
replace
`
ZuulFallbackProvder
`
in
future
versions
.
[
source
,
java
]
----
class
MyFallbackProvider
implements
FallbackProvider
{
@
Override
public
String
getRoute
()
{
return
"*"
;
}
@
Override
public
ClientHttpResponse
fallbackResponse
(
final
Throwable
cause
)
{
if
(
cause
instanceof
HystrixTimeoutException
)
{
return
response
(
HttpStatus
.
GATEWAY_TIMEOUT
);
}
else
{
return
fallbackResponse
();
}
}
@
Override
public
ClientHttpResponse
fallbackResponse
()
{
return
response
(
HttpStatus
.
INTERNAL_SERVER_ERROR
);
}
private
ClientHttpResponse
response
(
final
HttpStatus
status
)
{
return
new
ClientHttpResponse
()
{
@
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
(
"fallback"
.
getBytes
());
}
@
Override
public
HttpHeaders
getHeaders
()
{
HttpHeaders
headers
=
new
HttpHeaders
();
headers
.
setContentType
(
MediaType
.
APPLICATION_JSON
);
return
headers
;
}
};
}
}
----
[[
zuul
-
redirect
-
location
-
rewrite
]]
===
Rewriting
`
Location
`
header
...
...
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/FallbackProvider.java
0 → 100644
View file @
24d0bedf
/*
* Copyright 2017 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
*
* 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
.
springframework
.
cloud
.
netflix
.
zuul
.
filters
.
route
;
import
org.springframework.http.client.ClientHttpResponse
;
/**
* Extension of {@link ZuulFallbackProvider} which adds possibility to choose proper response
* based on the exception which caused the main method to fail.
*
* @author Dominik Mostek
*/
public
interface
FallbackProvider
extends
ZuulFallbackProvider
{
/**
* Provides a fallback response based on the cause of the failed execution.
*
* @param cause cause of the main method failure
* @return the fallback response
*/
ClientHttpResponse
fallbackResponse
(
Throwable
cause
);
}
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/ZuulFallbackProvider.java
View file @
24d0bedf
...
...
@@ -23,7 +23,9 @@ import org.springframework.http.client.ClientHttpResponse;
/**
* Provides fallback when a failure occurs on a route.
* @author Ryan Baxter
* @deprecated Use {@link FallbackProvider}
*/
@Deprecated
public
interface
ZuulFallbackProvider
{
/**
...
...
spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/zuul/filters/route/support/AbstractRibbonCommand.java
View file @
24d0bedf
...
...
@@ -23,6 +23,7 @@ import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
import
org.springframework.cloud.netflix.zuul.filters.route.RibbonCommand
;
import
org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext
;
import
org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider
;
import
org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider
;
import
org.springframework.http.client.ClientHttpResponse
;
import
com.netflix.client.AbstractLoadBalancerAwareClient
;
import
com.netflix.client.ClientRequest
;
...
...
@@ -69,7 +70,13 @@ public abstract class AbstractRibbonCommand<LBC extends AbstractLoadBalancerAwar
public
AbstractRibbonCommand
(
String
commandKey
,
LBC
client
,
RibbonCommandContext
context
,
ZuulProperties
zuulProperties
,
ZuulFallbackProvider
fallbackProvider
,
IClientConfig
config
)
{
super
(
getSetter
(
commandKey
,
zuulProperties
));
this
(
getSetter
(
commandKey
,
zuulProperties
),
client
,
context
,
fallbackProvider
,
config
);
}
protected
AbstractRibbonCommand
(
Setter
setter
,
LBC
client
,
RibbonCommandContext
context
,
ZuulFallbackProvider
fallbackProvider
,
IClientConfig
config
)
{
super
(
setter
);
this
.
client
=
client
;
this
.
context
=
context
;
this
.
zuulFallbackProvider
=
fallbackProvider
;
...
...
@@ -126,11 +133,24 @@ public abstract class AbstractRibbonCommand<LBC extends AbstractLoadBalancerAwar
@Override
protected
ClientHttpResponse
getFallback
()
{
if
(
zuulFallbackProvider
!=
null
)
{
return
zuulFallbackProvider
.
f
allbackResponse
();
return
getF
allbackResponse
();
}
return
super
.
getFallback
();
}
protected
ClientHttpResponse
getFallbackResponse
()
{
if
(
zuulFallbackProvider
instanceof
FallbackProvider
)
{
Throwable
cause
=
getFailedExecutionException
();
cause
=
cause
==
null
?
getExecutionException
()
:
cause
;
if
(
cause
==
null
)
{
zuulFallbackProvider
.
fallbackResponse
();
}
else
{
return
((
FallbackProvider
)
zuulFallbackProvider
).
fallbackResponse
(
cause
);
}
}
return
zuulFallbackProvider
.
fallbackResponse
();
}
public
LBC
getClient
()
{
return
client
;
}
...
...
spring-cloud-netflix-core/src/test/java/org/springframework/cloud/netflix/zuul/filters/route/support/RibbonCommandCauseFallbackPropagationTest.java
0 → 100644
View file @
24d0bedf
/*
* Copyright 2017 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
*
* 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
.
springframework
.
cloud
.
netflix
.
zuul
.
filters
.
route
.
support
;
import
com.netflix.client.AbstractLoadBalancerAwareClient
;
import
com.netflix.client.ClientException
;
import
com.netflix.client.ClientRequest
;
import
com.netflix.client.IResponse
;
import
com.netflix.client.RequestSpecificRetryHandler
;
import
com.netflix.client.config.IClientConfig
;
import
com.netflix.client.http.HttpResponse
;
import
com.netflix.hystrix.HystrixCommandProperties
;
import
com.netflix.hystrix.exception.HystrixTimeoutException
;
import
org.junit.Test
;
import
org.springframework.cloud.netflix.zuul.filters.ZuulProperties
;
import
org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider
;
import
org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider
;
import
org.springframework.http.HttpHeaders
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.http.client.ClientHttpResponse
;
import
java.io.ByteArrayInputStream
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
static
org
.
assertj
.
core
.
api
.
Assertions
.
assertThat
;
/**
* @author Dominik Mostek
*/
public
class
RibbonCommandCauseFallbackPropagationTest
{
@Test
public
void
providerIsCalledInCaseOfException
()
throws
Exception
{
TestZuulFallbackProviderWithoutCause
provider
=
new
TestZuulFallbackProviderWithoutCause
(
HttpStatus
.
INTERNAL_SERVER_ERROR
);
RuntimeException
exception
=
new
RuntimeException
(
"Failed!"
);
TestRibbonCommand
testCommand
=
new
TestRibbonCommand
(
new
TestClient
(
exception
),
provider
);
ClientHttpResponse
response
=
testCommand
.
execute
();
assertThat
(
response
).
isNotNull
();
assertThat
(
response
.
getStatusCode
()).
isEqualTo
(
HttpStatus
.
INTERNAL_SERVER_ERROR
);
}
@Test
public
void
causeIsProvidedForNewInterface
()
throws
Exception
{
TestFallbackProvider
provider
=
TestFallbackProvider
.
withResponse
(
HttpStatus
.
NOT_FOUND
);
RuntimeException
exception
=
new
RuntimeException
(
"Failed!"
);
TestRibbonCommand
testCommand
=
new
TestRibbonCommand
(
new
TestClient
(
exception
),
provider
);
ClientHttpResponse
response
=
testCommand
.
execute
();
assertThat
(
response
).
isNotNull
();
assertThat
(
response
.
getStatusCode
()).
isEqualTo
(
HttpStatus
.
NOT_FOUND
);
Throwable
cause
=
provider
.
getCause
();
assertThat
(
cause
.
getClass
()).
isEqualTo
(
exception
.
getClass
());
assertThat
(
cause
.
getMessage
()).
isEqualTo
(
exception
.
getMessage
());
}
@Test
public
void
executionExceptionIsUsedInsteadWhenFailedExceptionIsNull
()
throws
Exception
{
TestFallbackProvider
provider
=
TestFallbackProvider
.
withResponse
(
HttpStatus
.
BAD_GATEWAY
);
final
RuntimeException
exception
=
new
RuntimeException
(
"Failed!"
);
TestRibbonCommand
testCommand
=
new
TestRibbonCommand
(
new
TestClient
(
exception
),
provider
)
{
@Override
public
Throwable
getFailedExecutionException
()
{
return
null
;
}
@Override
public
Throwable
getExecutionException
()
{
return
exception
;
}
};
ClientHttpResponse
response
=
testCommand
.
execute
();
assertThat
(
response
).
isNotNull
();
assertThat
(
response
.
getStatusCode
()).
isEqualTo
(
HttpStatus
.
BAD_GATEWAY
);
}
@Test
public
void
timeoutExceptionIsPropagated
()
throws
Exception
{
TestFallbackProvider
provider
=
TestFallbackProvider
.
withResponse
(
HttpStatus
.
CONFLICT
);
RuntimeException
exception
=
new
RuntimeException
(
"Failed!"
);
TestRibbonCommand
testCommand
=
new
TestRibbonCommand
(
new
TestClient
(
exception
),
provider
,
1
)
{
@Override
protected
ClientRequest
createRequest
()
throws
Exception
{
Thread
.
sleep
(
5
);
return
super
.
createRequest
();
}
};
ClientHttpResponse
response
=
testCommand
.
execute
();
assertThat
(
response
).
isNotNull
();
assertThat
(
response
.
getStatusCode
()).
isEqualTo
(
HttpStatus
.
CONFLICT
);
assertThat
(
provider
.
getCause
()).
isNotNull
();
assertThat
(
provider
.
getCause
().
getClass
()).
isEqualTo
(
HystrixTimeoutException
.
class
);
}
public
static
class
TestRibbonCommand
extends
AbstractRibbonCommand
<
AbstractLoadBalancerAwareClient
<
ClientRequest
,
HttpResponse
>,
ClientRequest
,
HttpResponse
>
{
public
TestRibbonCommand
(
AbstractLoadBalancerAwareClient
<
ClientRequest
,
HttpResponse
>
client
,
ZuulFallbackProvider
fallbackProvider
)
{
this
(
client
,
new
ZuulProperties
(),
fallbackProvider
);
}
public
TestRibbonCommand
(
AbstractLoadBalancerAwareClient
<
ClientRequest
,
HttpResponse
>
client
,
ZuulProperties
zuulProperties
,
ZuulFallbackProvider
fallbackProvider
)
{
super
(
"testCommand"
,
client
,
null
,
zuulProperties
,
fallbackProvider
);
}
public
TestRibbonCommand
(
AbstractLoadBalancerAwareClient
<
ClientRequest
,
HttpResponse
>
client
,
ZuulFallbackProvider
fallbackProvider
,
int
timeout
)
{
// different name is used because of properties caching
super
(
getSetter
(
"testCommand2"
,
new
ZuulProperties
()).
andCommandPropertiesDefaults
(
defauts
(
timeout
)),
client
,
null
,
fallbackProvider
,
null
);
}
private
static
HystrixCommandProperties
.
Setter
defauts
(
final
int
timeout
)
{
return
HystrixCommandProperties
.
Setter
().
withExecutionTimeoutEnabled
(
true
)
.
withExecutionIsolationStrategy
(
HystrixCommandProperties
.
ExecutionIsolationStrategy
.
THREAD
)
.
withExecutionTimeoutInMilliseconds
(
timeout
);
}
@Override
protected
ClientRequest
createRequest
()
throws
Exception
{
return
null
;
}
}
public
static
class
TestClient
extends
AbstractLoadBalancerAwareClient
{
private
final
RuntimeException
exception
;
public
TestClient
(
RuntimeException
exception
)
{
super
(
null
);
this
.
exception
=
exception
;
}
@Override
public
IResponse
executeWithLoadBalancer
(
final
ClientRequest
request
,
final
IClientConfig
requestConfig
)
throws
ClientException
{
throw
exception
;
}
@Override
public
RequestSpecificRetryHandler
getRequestSpecificRetryHandler
(
final
ClientRequest
clientRequest
,
final
IClientConfig
iClientConfig
)
{
return
null
;
}
@Override
public
IResponse
execute
(
final
ClientRequest
clientRequest
,
final
IClientConfig
iClientConfig
)
throws
Exception
{
return
null
;
}
}
public
static
class
TestFallbackProvider
implements
FallbackProvider
{
private
final
ClientHttpResponse
response
;
private
Throwable
cause
;
public
TestFallbackProvider
(
final
ClientHttpResponse
response
)
{
this
.
response
=
response
;
}
@Override
public
ClientHttpResponse
fallbackResponse
(
final
Throwable
cause
)
{
this
.
cause
=
cause
;
return
response
;
}
@Override
public
String
getRoute
()
{
return
"test-route"
;
}
@Override
public
ClientHttpResponse
fallbackResponse
()
{
throw
new
UnsupportedOperationException
(
"fallback without cause is not supported"
);
}
public
Throwable
getCause
()
{
return
cause
;
}
public
static
TestFallbackProvider
withResponse
(
final
HttpStatus
status
)
{
return
new
TestFallbackProvider
(
getClientHttpResponse
(
status
));
}
}
public
static
class
TestZuulFallbackProviderWithoutCause
implements
ZuulFallbackProvider
{
private
final
ClientHttpResponse
response
;
public
TestZuulFallbackProviderWithoutCause
(
final
ClientHttpResponse
response
)
{
this
.
response
=
response
;
}
public
TestZuulFallbackProviderWithoutCause
(
final
HttpStatus
status
)
{
this
(
getClientHttpResponse
(
status
));
}
@Override
public
String
getRoute
()
{
return
"test-route"
;
}
@Override
public
ClientHttpResponse
fallbackResponse
()
{
return
response
;
}
}
private
static
ClientHttpResponse
getClientHttpResponse
(
final
HttpStatus
status
)
{
return
new
ClientHttpResponse
()
{
@Override
public
HttpStatus
getStatusCode
()
throws
IOException
{
return
status
;
}
@Override
public
int
getRawStatusCode
()
throws
IOException
{
return
getStatusCode
().
value
();
}
@Override
public
String
getStatusText
()
throws
IOException
{
return
getStatusCode
().
getReasonPhrase
();
}
@Override
public
void
close
()
{
}
@Override
public
InputStream
getBody
()
throws
IOException
{
return
new
ByteArrayInputStream
(
"test"
.
getBytes
());
}
@Override
public
HttpHeaders
getHeaders
()
{
return
new
HttpHeaders
();
}
};
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment