6 changed files with 264 additions and 105 deletions
@ -0,0 +1,86 @@
@@ -0,0 +1,86 @@
|
||||
/* |
||||
* Copyright 2016-2018 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.client.loadbalancer; |
||||
|
||||
import java.io.ByteArrayInputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.client.AbstractClientHttpResponse; |
||||
import org.springframework.http.client.ClientHttpResponse; |
||||
import org.springframework.util.StreamUtils; |
||||
|
||||
/** |
||||
* {@link RetryableStatusCodeException} that captures a {@link ClientHttpResponse} |
||||
* @author Ryan Baxter |
||||
*/ |
||||
public class ClientHttpResponseStatusCodeException extends RetryableStatusCodeException { |
||||
|
||||
private ClientHttpResponseWrapper response; |
||||
|
||||
/** |
||||
* Constructor |
||||
* @param serviceId The service id |
||||
* @param response The response object |
||||
* @throws IOException Thrown if the {@link ClientHttpResponse} body cannot be retrieved |
||||
*/ |
||||
public ClientHttpResponseStatusCodeException(String serviceId, ClientHttpResponse response) throws IOException { |
||||
super(serviceId, response.getRawStatusCode(), response, null); |
||||
this.response = new ClientHttpResponseWrapper(response); |
||||
response.close(); |
||||
} |
||||
|
||||
@Override |
||||
public ClientHttpResponse getResponse() { |
||||
return response; |
||||
} |
||||
|
||||
static class ClientHttpResponseWrapper extends AbstractClientHttpResponse { |
||||
|
||||
private ClientHttpResponse response; |
||||
private byte[] body; |
||||
|
||||
public ClientHttpResponseWrapper(ClientHttpResponse response) throws IOException { |
||||
this.response = response; |
||||
this.body = StreamUtils.copyToByteArray(response.getBody()); |
||||
} |
||||
|
||||
@Override |
||||
public int getRawStatusCode() throws IOException { |
||||
return response.getRawStatusCode(); |
||||
} |
||||
|
||||
@Override |
||||
public String getStatusText() throws IOException { |
||||
return response.getStatusText(); |
||||
} |
||||
|
||||
@Override |
||||
public void close() { |
||||
response.close(); |
||||
} |
||||
|
||||
@Override |
||||
public InputStream getBody() throws IOException { |
||||
return new ByteArrayInputStream(body); |
||||
} |
||||
|
||||
@Override |
||||
public HttpHeaders getHeaders() { |
||||
return response.getHeaders(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,53 @@
@@ -0,0 +1,53 @@
|
||||
/* |
||||
* Copyright 2013-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.client.loadbalancer; |
||||
|
||||
import org.springframework.retry.RecoveryCallback; |
||||
import org.springframework.retry.RetryContext; |
||||
import org.springframework.retry.RetryException; |
||||
|
||||
import java.net.URI; |
||||
|
||||
/** |
||||
* An implementation of {@link RecoveryCallback} which relies on an implemtation |
||||
* of {@link RetryableStatusCodeException} to contain the last response object from |
||||
* the request |
||||
* @author LiYuan Lee |
||||
*/ |
||||
public abstract class RibbonRecoveryCallback<T, R> implements RecoveryCallback<T> { |
||||
|
||||
/** |
||||
* Create the response returned in the {@link RecoveryCallback} |
||||
* @param response The response from the HTTP client |
||||
* @param uri The URI the response is from |
||||
* @return The response to be returned |
||||
*/ |
||||
protected abstract T createResponse(R response, URI uri); |
||||
|
||||
@Override |
||||
public T recover(RetryContext context) throws Exception { |
||||
Throwable lastThrowable = context.getLastThrowable(); |
||||
if (lastThrowable != null) { |
||||
if (lastThrowable instanceof RetryableStatusCodeException) { |
||||
RetryableStatusCodeException ex = (RetryableStatusCodeException) lastThrowable; |
||||
return createResponse((R) ex.getResponse(), ex.getUri()); |
||||
} else if (lastThrowable instanceof Exception){ |
||||
throw (Exception)lastThrowable; |
||||
} |
||||
} |
||||
throw new RetryException("Could not recover", lastThrowable); |
||||
} |
||||
} |
@ -0,0 +1,72 @@
@@ -0,0 +1,72 @@
|
||||
package org.springframework.cloud.client.loadbalancer; |
||||
|
||||
import java.io.ByteArrayInputStream; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.runners.MockitoJUnitRunner; |
||||
import org.springframework.http.HttpHeaders; |
||||
import org.springframework.http.client.AbstractClientHttpResponse; |
||||
import org.springframework.http.client.ClientHttpResponse; |
||||
import org.springframework.util.StreamUtils; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertTrue; |
||||
import static org.junit.Assert.assertFalse; |
||||
|
||||
/** |
||||
* @author Ryan Baxter |
||||
*/ |
||||
@RunWith(MockitoJUnitRunner.class) |
||||
public class ClientHttpResponseStatusCodeExceptionTest { |
||||
|
||||
@Test |
||||
public void testCreation() throws Exception { |
||||
MyClientHttpResponse response = new MyClientHttpResponse(); |
||||
assertFalse(response.isClosed()); |
||||
ClientHttpResponseStatusCodeException exp = new ClientHttpResponseStatusCodeException("service", response); |
||||
assertTrue(response.isClosed()); |
||||
ClientHttpResponse expResponse = exp.getResponse(); |
||||
assertEquals(response.getRawStatusCode(), expResponse.getRawStatusCode()); |
||||
assertEquals(response.getStatusText(), expResponse.getStatusText()); |
||||
assertEquals(response.getHeaders(), expResponse.getHeaders()); |
||||
assertEquals(response.getStatusText(), new String(StreamUtils.copyToByteArray(expResponse.getBody()))); |
||||
} |
||||
|
||||
class MyClientHttpResponse extends AbstractClientHttpResponse { |
||||
|
||||
private boolean closed = false; |
||||
|
||||
@Override |
||||
public int getRawStatusCode() throws IOException { |
||||
return 200; |
||||
} |
||||
|
||||
@Override |
||||
public String getStatusText() throws IOException { |
||||
return "foo"; |
||||
} |
||||
|
||||
@Override |
||||
public void close() { |
||||
this.closed = true; |
||||
} |
||||
|
||||
public boolean isClosed() { |
||||
return closed; |
||||
} |
||||
|
||||
@Override |
||||
public InputStream getBody() throws IOException { |
||||
return new ByteArrayInputStream(getStatusText().getBytes()); |
||||
} |
||||
|
||||
@Override |
||||
public HttpHeaders getHeaders() { |
||||
HttpHeaders headers = new HttpHeaders(); |
||||
headers.add("foo", "bar"); |
||||
return headers; |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue