diff --git a/build.gradle b/build.gradle index 9bbc55cb2e..1ab8a1f4c7 100644 --- a/build.gradle +++ b/build.gradle @@ -106,7 +106,7 @@ subprojects { subproject -> hibernateVersion = '5.2.5.Final' hsqldbVersion = '2.3.3' h2Version = '1.4.180' - jackson2Version = '2.8.5' + jackson2Version = '2.9.0.pr2' javaxActivationVersion = '1.1.1' javaxMailVersion = '1.5.6' jedisVersion = '2.9.0' @@ -130,15 +130,15 @@ subprojects { subproject -> servletApiVersion = '3.1.0' slf4jVersion = "1.7.21" smackVersion = '4.1.7' - springAmqpVersion = project.hasProperty('springAmqpVersion') ? project.springAmqpVersion : '2.0.0.M3' - springDataJpaVersion = '2.0.0.M2' - springDataMongoVersion = '2.0.0.M2' - springDataRedisVersion = '2.0.0.M2' - springGemfireVersion = '2.0.0.M2' - springSecurityVersion = '4.2.2.RELEASE' + springAmqpVersion = project.hasProperty('springAmqpVersion') ? project.springAmqpVersion : '2.0.0.BUILD-SNAPSHOT' + springDataJpaVersion = '2.0.0.BUILD-SNAPSHOT' + springDataMongoVersion = '2.0.0.BUILD-SNAPSHOT' + springDataRedisVersion = '2.0.0.BUILD-SNAPSHOT' + springGemfireVersion = '2.0.0.BUILD-SNAPSHOT' + springSecurityVersion = '5.0.0.BUILD-SNAPSHOT' springSocialTwitterVersion = '2.0.0.M1' springRetryVersion = '1.2.0.RELEASE' - springVersion = project.hasProperty('springVersion') ? project.springVersion : '5.0.0.M5' + springVersion = project.hasProperty('springVersion') ? project.springVersion : '5.0.0.BUILD-SNAPSHOT' springWsVersion = '2.4.0.RELEASE' tomcatVersion = "8.5.9" xmlUnitVersion = '1.6' diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java index 2b026aa04f..e26930d4ba 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/AbstractHttpRequestExecutingMessageHandler.java @@ -17,6 +17,7 @@ package org.springframework.integration.http.outbound; import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; @@ -25,6 +26,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.function.Supplier; import javax.xml.transform.Source; @@ -46,7 +48,6 @@ import org.springframework.integration.support.AbstractIntegrationMessageBuilder import org.springframework.integration.support.MessageBuilderFactory; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandlingException; -import org.springframework.messaging.MessagingException; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; @@ -253,46 +254,44 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac @Override protected Object handleRequestMessage(Message requestMessage) { + HttpMethod httpMethod = determineHttpMethod(requestMessage); + + if (!shouldIncludeRequestBody(httpMethod) && this.extractPayloadExplicitlySet) { + if (logger.isWarnEnabled()) { + logger.warn("The 'extractPayload' attribute has no relevance for the current request " + + "since the HTTP Method is '" + httpMethod + + "', and no request body will be sent for that method."); + } + } + + Object expectedResponseType = determineExpectedResponseType(requestMessage); + + HttpEntity httpRequest = generateHttpRequest(requestMessage, httpMethod); + return exchange(() -> generateUri(requestMessage), httpMethod, httpRequest, expectedResponseType, + requestMessage); + } + + protected abstract Object exchange(Supplier uriSupplier, HttpMethod httpMethod, HttpEntity httpRequest, + Object expectedResponseType, Message requestMessage); + + private URI generateUri(Message requestMessage) { Object uri = this.uriExpression.getValue(this.evaluationContext, requestMessage); Assert.state(uri instanceof String || uri instanceof URI, "'uriExpression' evaluation must result in a 'String' or 'URI' instance, not: " + (uri == null ? "null" : uri.getClass())); - URI realUri = null; + Map uriVariables = determineUriVariables(requestMessage); + UriComponentsBuilder uriComponentsBuilder = uri instanceof String + ? UriComponentsBuilder.fromUriString((String) uri) + : UriComponentsBuilder.fromUri((URI) uri); + UriComponents uriComponents = uriComponentsBuilder.buildAndExpand(uriVariables); try { - HttpMethod httpMethod = this.determineHttpMethod(requestMessage); - - if (!shouldIncludeRequestBody(httpMethod) && this.extractPayloadExplicitlySet) { - if (logger.isWarnEnabled()) { - logger.warn("The 'extractPayload' attribute has no relevance for the current request " + - "since the HTTP Method is '" + httpMethod + - "', and no request body will be sent for that method."); - } - } - - Object expectedResponseType = determineExpectedResponseType(requestMessage); - - HttpEntity httpRequest = generateHttpRequest(requestMessage, httpMethod); - Map uriVariables = this.determineUriVariables(requestMessage); - UriComponentsBuilder uriComponentsBuilder = uri instanceof String - ? UriComponentsBuilder.fromUriString((String) uri) - : UriComponentsBuilder.fromUri((URI) uri); - UriComponents uriComponents = uriComponentsBuilder.buildAndExpand(uriVariables); - realUri = this.encodeUri ? uriComponents.toUri() : new URI(uriComponents.toUriString()); - - return exchange(realUri, httpMethod, httpRequest, expectedResponseType, requestMessage); + return this.encodeUri ? uriComponents.toUri() : new URI(uriComponents.toUriString()); } - catch (MessagingException e) { - throw e; - } - catch (Exception e) { - throw new MessageHandlingException(requestMessage, "HTTP request execution failed for URI [" - + (realUri == null ? uri : realUri) + "]", e); + catch (URISyntaxException e) { + throw new MessageHandlingException(requestMessage, "Invalid URI [" + uri + "]", e); } } - protected abstract Object exchange(URI realUri, HttpMethod httpMethod, HttpEntity httpRequest, - Object expectedResponseType, Message requestMessage); - protected Object getReply(ResponseEntity httpResponse) { if (this.expectReply) { HttpHeaders httpHeaders = httpResponse.getHeaders(); @@ -341,7 +340,7 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac } } - private HttpEntity generateHttpRequest(Message message, HttpMethod httpMethod) throws Exception { + private HttpEntity generateHttpRequest(Message message, HttpMethod httpMethod) { Assert.notNull(message, "message must not be null"); return (this.extractPayload) ? this.createHttpEntityFromPayload(message, httpMethod) : this.createHttpEntityFromMessage(message, httpMethod); @@ -355,7 +354,7 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac } HttpHeaders httpHeaders = this.mapHeaders(message); if (!shouldIncludeRequestBody(httpMethod)) { - return new HttpEntity(httpHeaders); + return new HttpEntity<>(httpHeaders); } // otherwise, we are creating a request with a body and need to deal with the content-type header as well if (httpHeaders.getContentType() == null) { @@ -370,7 +369,7 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac payload = this.convertToMultiValueMap((Map) payload); } } - return new HttpEntity(payload, httpHeaders); + return new HttpEntity<>(payload, httpHeaders); } private HttpEntity createHttpEntityFromMessage(Message message, HttpMethod httpMethod) { @@ -379,7 +378,7 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac httpHeaders.setContentType(new MediaType("application", "x-java-serialized-object")); return new HttpEntity(message, httpHeaders); } - return new HttpEntity(httpHeaders); + return new HttpEntity<>(httpHeaders); } protected HttpHeaders mapHeaders(Message message) { @@ -471,7 +470,7 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac * If all keys and values are Strings, we'll consider the Map to be form data. */ private boolean isFormData(Map map) { - for (Object key : map.keySet()) { + for (Object key : map.keySet()) { if (!(key instanceof String)) { return false; } @@ -498,7 +497,7 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac } } - private Object determineExpectedResponseType(Message requestMessage) throws Exception { + private Object determineExpectedResponseType(Message requestMessage) { Object expectedResponseType = null; if (this.expectedResponseTypeExpression != null) { expectedResponseType = this.expectedResponseTypeExpression.getValue(this.evaluationContext, requestMessage); @@ -507,11 +506,17 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac Assert.state(expectedResponseType instanceof Class || expectedResponseType instanceof String || expectedResponseType instanceof ParameterizedTypeReference, - "'expectedResponseType' can be an instance of 'Class', 'String' or 'ParameterizedTypeReference'; " - + "evaluation resulted in a" + expectedResponseType.getClass() + "."); + "'expectedResponseType' can be an instance of 'Class', 'String' " + + "or 'ParameterizedTypeReference'; " + + "evaluation resulted in a" + expectedResponseType.getClass() + "."); if (expectedResponseType instanceof String && StringUtils.hasText((String) expectedResponseType)) { - expectedResponseType = ClassUtils.forName((String) expectedResponseType, - getApplicationContext().getClassLoader()); + try { + expectedResponseType = ClassUtils.forName((String) expectedResponseType, + getApplicationContext().getClassLoader()); + } + catch (ClassNotFoundException e) { + throw new IllegalStateException("Cannot load class for name: " + expectedResponseType, e); + } } } return expectedResponseType; @@ -532,9 +537,9 @@ public abstract class AbstractHttpRequestExecutingMessageHandler extends Abstrac } return ExpressionEvalMap.from(expressions) - .usingEvaluationContext(this.evaluationContext) - .withRoot(requestMessage) - .build(); + .usingEvaluationContext(this.evaluationContext) + .withRoot(requestMessage) + .build(); } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java index 8846a78afc..978c23eb43 100755 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/HttpRequestExecutingMessageHandler.java @@ -18,6 +18,7 @@ package org.springframework.integration.http.outbound; import java.net.URI; import java.util.List; +import java.util.function.Supplier; import org.springframework.beans.factory.BeanFactory; import org.springframework.core.ParameterizedTypeReference; @@ -33,8 +34,10 @@ import org.springframework.integration.expression.ValueExpression; import org.springframework.integration.mapping.HeaderMapper; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandler; +import org.springframework.messaging.MessageHandlingException; import org.springframework.util.Assert; import org.springframework.web.client.ResponseErrorHandler; +import org.springframework.web.client.RestClientException; import org.springframework.web.client.RestTemplate; /** @@ -149,16 +152,24 @@ public class HttpRequestExecutingMessageHandler extends AbstractHttpRequestExecu } @Override - protected Object exchange(URI uri, HttpMethod httpMethod, HttpEntity httpRequest, Object expectedResponseType, - Message requestMessage) { + protected Object exchange(Supplier uriSupplier, HttpMethod httpMethod, HttpEntity httpRequest, + Object expectedResponseType, Message requestMessage) { + URI uri = uriSupplier.get(); ResponseEntity httpResponse; - if (expectedResponseType instanceof ParameterizedTypeReference) { - httpResponse = this.restTemplate.exchange(uri, httpMethod, httpRequest, - (ParameterizedTypeReference) expectedResponseType); + try { + if (expectedResponseType instanceof ParameterizedTypeReference) { + httpResponse = this.restTemplate.exchange(uri, httpMethod, httpRequest, + (ParameterizedTypeReference) expectedResponseType); + } + else { + httpResponse = this.restTemplate.exchange(uri, httpMethod, httpRequest, + (Class) expectedResponseType); + } + return getReply(httpResponse); } - else { - httpResponse = this.restTemplate.exchange(uri, httpMethod, httpRequest, (Class) expectedResponseType); + catch (RestClientException e) { + throw new MessageHandlingException(requestMessage, + "HTTP request execution failed for URI [" + uri + "]", e); } - return getReply(httpResponse); } } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/ReactiveHttpRequestExecutingMessageHandler.java b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/ReactiveHttpRequestExecutingMessageHandler.java index ad07f7fe08..6d2733aea2 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/ReactiveHttpRequestExecutingMessageHandler.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/outbound/ReactiveHttpRequestExecutingMessageHandler.java @@ -17,6 +17,7 @@ package org.springframework.integration.http.outbound; import java.net.URI; +import java.util.function.Supplier; import org.springframework.beans.factory.BeanFactory; import org.springframework.core.ParameterizedTypeReference; @@ -32,7 +33,6 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandler; import org.springframework.util.Assert; import org.springframework.web.reactive.function.BodyExtractors; -import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.WebClientException; @@ -111,53 +111,29 @@ public class ReactiveHttpRequestExecutingMessageHandler extends AbstractHttpRequ } @Override - protected Object exchange(URI uri, HttpMethod httpMethod, HttpEntity httpRequest, Object expectedResponseType, - Message requestMessage) { - WebClient.UriSpec uriSpec; + protected Object exchange(Supplier uriSupplier, HttpMethod httpMethod, HttpEntity httpRequest, + Object expectedResponseType, Message requestMessage) { - // TODO use WebClient.method(HttpMethod) in the future version + WebClient.RequestBodySpec requestSpec = + this.webClient.method(httpMethod) + .uri(b -> uriSupplier.get()) + .headers(httpRequest.getHeaders()); - switch (httpMethod) { - case GET: - uriSpec = this.webClient.get(); - break; - case HEAD: - uriSpec = this.webClient.head(); - break; - case POST: - uriSpec = this.webClient.post(); - break; - case PUT: - uriSpec = this.webClient.put(); - break; - case PATCH: - uriSpec = this.webClient.patch(); - break; - case DELETE: - uriSpec = this.webClient.delete(); - break; - case OPTIONS: - uriSpec = this.webClient.options(); - break; - case TRACE: - throw new UnsupportedOperationException("WebClient doesn't support the TRACE HTTP method"); - default: - throw new UnsupportedOperationException("Unsupported HTTP method"); - } - - WebClient.HeaderSpec spec = uriSpec.uri(uri) - .headers(httpRequest.getHeaders()); - - Mono responseMono; if (httpRequest.hasBody()) { - responseMono = spec.exchange(BodyInserters.fromObject(httpRequest.getBody())); - } - else { - responseMono = spec.exchange(); + requestSpec.body(httpRequest.getBody()); } + Mono responseMono = requestSpec.exchange() + .doOnNext(response -> { + HttpStatus httpStatus = response.statusCode(); + if (httpStatus.is4xxClientError() || httpStatus.is5xxServerError()) { + throw new WebClientException( + "ClientResponse has erroneous status code: " + httpStatus.value() + + " " + httpStatus.getReasonPhrase()); + } + }); + if (isExpectReply()) { - ResolvableType responseType; if (expectedResponseType instanceof ParameterizedTypeReference) { @@ -180,16 +156,7 @@ public class ReactiveHttpRequestExecutingMessageHandler extends AbstractHttpRequ .map(this::getReply); } else { - responseMono - .doOnNext(response -> { - HttpStatus httpStatus = response.statusCode(); - if (httpStatus.is4xxClientError() || httpStatus.is5xxServerError()) { - throw new WebClientException( - "ClientResponse has erroneous status code: " + httpStatus.value() + - " " + httpStatus.getReasonPhrase()); - } - }) - .subscribe(v -> { }, ex -> sendErrorMessage(requestMessage, ex)); + responseMono.subscribe(v -> { }, ex -> sendErrorMessage(requestMessage, ex)); return null; } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java index 972844b34b..c817dae20b 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/outbound/UriVariableExpressionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-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. @@ -16,9 +16,7 @@ package org.springframework.integration.http.outbound; -import static org.hamcrest.Matchers.containsString; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; @@ -52,6 +50,7 @@ import org.springframework.messaging.support.GenericMessage; * @author Wallace Wadge * @author Gary Russell * @author Artem Bilan + * * @since 2.0 */ public class UriVariableExpressionTests { @@ -78,7 +77,6 @@ public class UriVariableExpressionTests { } catch (Exception e) { assertEquals("intentional", e.getCause().getMessage()); - assertThat(e.getMessage(), containsString("http://test/bar")); } assertEquals("http://test/bar", uriHolder.get().toString()); }