diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/ClientHttpResponseStatusCodeException.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/ClientHttpResponseStatusCodeException.java index 0d7dd299..eaf74882 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/ClientHttpResponseStatusCodeException.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/ClientHttpResponseStatusCodeException.java @@ -21,7 +21,6 @@ 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} @@ -35,12 +34,11 @@ public class ClientHttpResponseStatusCodeException extends RetryableStatusCodeEx * Constructor * @param serviceId The service id * @param response The response object - * @throws IOException Thrown if the {@link ClientHttpResponse} body cannot be retrieved + * @throws IOException Thrown if the {@link ClientHttpResponse} response code cant be retrieved */ - public ClientHttpResponseStatusCodeException(String serviceId, ClientHttpResponse response) throws IOException { + public ClientHttpResponseStatusCodeException(String serviceId, ClientHttpResponse response, byte[] body) throws IOException { super(serviceId, response.getRawStatusCode(), response, null); - this.response = new ClientHttpResponseWrapper(response); - response.close(); + this.response = new ClientHttpResponseWrapper(response, body); } @Override @@ -53,9 +51,9 @@ public class ClientHttpResponseStatusCodeException extends RetryableStatusCodeEx private ClientHttpResponse response; private byte[] body; - public ClientHttpResponseWrapper(ClientHttpResponse response) throws IOException { + public ClientHttpResponseWrapper(ClientHttpResponse response, byte[] body) { this.response = response; - this.body = StreamUtils.copyToByteArray(response.getBody()); + this.body = body; } @Override diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptor.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptor.java index c2616653..409e94a6 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptor.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptor.java @@ -32,6 +32,7 @@ import org.springframework.retry.backoff.NoBackOffPolicy; import org.springframework.retry.policy.NeverRetryPolicy; import org.springframework.retry.support.RetryTemplate; import org.springframework.util.Assert; +import org.springframework.util.StreamUtils; /** * @author Ryan Baxter @@ -131,7 +132,9 @@ public class RetryLoadBalancerInterceptor implements ClientHttpRequestIntercepto requestFactory.createRequest(request, body, execution)); int statusCode = response.getRawStatusCode(); if (retryPolicy != null && retryPolicy.retryableStatusCode(statusCode)) { - throw new ClientHttpResponseStatusCodeException(serviceName, response); + byte[] body = StreamUtils.copyToByteArray(response.getBody()); + response.close(); + throw new ClientHttpResponseStatusCodeException(serviceName, response, body); } return response; } diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/ClientHttpResponseStatusCodeExceptionTest.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/ClientHttpResponseStatusCodeExceptionTest.java index 9358e7ea..61155168 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/ClientHttpResponseStatusCodeExceptionTest.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/ClientHttpResponseStatusCodeExceptionTest.java @@ -25,8 +25,8 @@ public class ClientHttpResponseStatusCodeExceptionTest { public void testCreation() throws Exception { MyClientHttpResponse response = new MyClientHttpResponse(); assertFalse(response.isClosed()); - ClientHttpResponseStatusCodeException exp = new ClientHttpResponseStatusCodeException("service", response); - assertTrue(response.isClosed()); + ClientHttpResponseStatusCodeException exp = new ClientHttpResponseStatusCodeException("service", + response, response.getStatusText().getBytes()); ClientHttpResponse expResponse = exp.getResponse(); assertEquals(response.getRawStatusCode(), expResponse.getRawStatusCode()); assertEquals(response.getStatusText(), expResponse.getStatusText()); diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/restart/RestartEndpoint.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/restart/RestartEndpoint.java index 0d0613d7..ca6a060a 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/restart/RestartEndpoint.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/restart/RestartEndpoint.java @@ -16,13 +16,18 @@ package org.springframework.cloud.context.restart; +import java.io.Closeable; +import java.io.IOException; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.boot.SpringApplication; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.springframework.context.ConfigurableApplicationContext; @@ -39,8 +44,7 @@ import org.springframework.util.ClassUtils; * */ @Endpoint(id = "restart", enableByDefault = false) -public class RestartEndpoint - implements ApplicationListener { +public class RestartEndpoint implements ApplicationListener { private static Log logger = LogFactory.getLog(RestartEndpoint.class); @@ -82,7 +86,7 @@ public class RestartEndpoint } @WriteOperation - //FIXME: map with "message: Restarting" or couldn't restart + // FIXME: map with "message: Restarting" or couldn't restart public Boolean restart() { try { doRestart(); @@ -142,7 +146,7 @@ public class RestartEndpoint this.integrationShutdown.stop(this.timeout); } this.application.setEnvironment(this.context.getEnvironment()); - this.context.close(); + close(); // If running in a webapp then the context classloader is probably going to // die so we need to revert to a safe place before starting again overrideClassLoaderForRestart(); @@ -151,6 +155,19 @@ public class RestartEndpoint return this.context; } + private void close() { + ApplicationContext context = this.context; + while (context instanceof Closeable) { + try { + ((Closeable) context).close(); + } + catch (IOException e) { + logger.error("Cannot close context: " + context.getId(), e); + } + context = context.getParent(); + } + } + // @ManagedAttribute public boolean isRunning() { if (this.context != null) { diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/context/restart/RestartIntegrationTests.java b/spring-cloud-context/src/test/java/org/springframework/cloud/context/restart/RestartIntegrationTests.java index cc3c3e88..579e7247 100644 --- a/spring-cloud-context/src/test/java/org/springframework/cloud/context/restart/RestartIntegrationTests.java +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/context/restart/RestartIntegrationTests.java @@ -16,16 +16,19 @@ package org.springframework.cloud.context.restart; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; - import org.junit.After; import org.junit.Test; + import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.LiveBeansView; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; public class RestartIntegrationTests { @@ -41,7 +44,10 @@ public class RestartIntegrationTests { @Test public void testRestartTwice() throws Exception { - context = SpringApplication.run(TestConfiguration.class, "--management.endpoint.restart.enabled=true", "--server.port=0"); + context = SpringApplication.run(TestConfiguration.class, + "--management.endpoint.restart.enabled=true", "--server.port=0", + "--spring.liveBeansView.mbeanDomain=livebeans"); + RestartEndpoint endpoint = context.getBean(RestartEndpoint.class); assertNotNull(context.getParent()); assertNull(context.getParent().getParent()); @@ -59,6 +65,10 @@ public class RestartIntegrationTests { assertNotNull(context.getParent()); assertNull(context.getParent().getParent()); + LiveBeansView beans = new LiveBeansView(); + String json = beans.getSnapshotAsJson(); + assertThat(json).containsOnlyOnce("parent\": \"bootstrap"); + assertThat(json).containsOnlyOnce("parent\": null"); } public static void main(String[] args) {