diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/BaseHttpInboundEndpoint.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/BaseHttpInboundEndpoint.java index 43655a70682..4ef10d1ac2e 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/BaseHttpInboundEndpoint.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/BaseHttpInboundEndpoint.java @@ -28,7 +28,6 @@ import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; -import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; import org.springframework.integration.context.OrderlyShutdownCapable; import org.springframework.integration.expression.ExpressionUtils; @@ -51,20 +50,20 @@ */ public class BaseHttpInboundEndpoint extends MessagingGatewaySupport implements OrderlyShutdownCapable { - protected static final boolean jaxb2Present = // NOSONAR lower case static + protected static final boolean JAXB_PRESENT = ClassUtils.isPresent("javax.xml.bind.Binder", BaseHttpInboundEndpoint.class.getClassLoader()); - protected static final boolean romeToolsPresent = // NOSONAR lower case static + protected static final boolean ROME_TOOLS_PRESENT = ClassUtils.isPresent("com.rometools.rome.feed.atom.Feed", BaseHttpInboundEndpoint.class.getClassLoader()); - protected static final List nonReadableBodyHttpMethods = // NOSONAR lower case static + protected static final List NON_READABLE_BODY_HTTP_METHODS = Arrays.asList(HttpMethod.GET, HttpMethod.HEAD, HttpMethod.OPTIONS); - protected final boolean expectReply; + protected final AtomicInteger activeCount = new AtomicInteger(); // NOSONAR - protected final AtomicInteger activeCount = new AtomicInteger(); + private final boolean expectReply; private ResolvableType requestPayloadType = null; @@ -268,13 +267,12 @@ protected void onInit() { } private void validateSupportedMethods() { - if (this.requestPayloadType != null - && CollectionUtils.containsAny(nonReadableBodyHttpMethods, - Arrays.asList(getRequestMapping().getMethods()))) { - if (logger.isWarnEnabled()) { - logger.warn("The 'requestPayloadType' attribute will have no relevance for one " + - "of the specified HTTP methods '" + nonReadableBodyHttpMethods + "'"); - } + if (this.requestPayloadType != null && logger.isWarnEnabled() && + CollectionUtils.containsAny(NON_READABLE_BODY_HTTP_METHODS, + Arrays.asList(getRequestMapping().getMethods()))) { + + logger.warn("The 'requestPayloadType' attribute will have no relevance for one " + + "of the specified HTTP methods '" + NON_READABLE_BODY_HTTP_METHODS + "'"); } } @@ -329,11 +327,11 @@ public String getComponentType() { /** * Checks if the request has a readable body (not a GET, HEAD, or OPTIONS request). - * @param request the HTTP request to check the method + * @param httpMethod the HTTP method to check * @return true or false if HTTP request can contain the body */ - protected boolean isReadable(HttpRequest request) { - return !(CollectionUtils.containsInstance(nonReadableBodyHttpMethods, request.getMethod())); + protected boolean isReadable(HttpMethod httpMethod) { + return !(CollectionUtils.containsInstance(NON_READABLE_BODY_HTTP_METHODS, httpMethod)); } } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingController.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingController.java index f398b7a3195..62b8a156ee0 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingController.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingController.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2019 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. @@ -45,9 +45,9 @@ *

* This endpoint will have request/reply behavior by default. That can be overridden by passing false to * the constructor. In the request/reply case, the core map will be passed to the view, and it will contain either the - * reply Message or payload depending on the value of {@link #extractReplyPayload} (true by default, meaning just the - * payload). The corresponding key in the map is determined by the {@link #replyKey} property (with a default of - * "reply"). + * reply Message or payload depending on the value of {@link #setExtractReplyPayload(boolean)} + * (true by default, meaning just the payload). + * The corresponding key in the map is determined by the {@link #replyKey} property (with a default of "reply"). * * @author Mark Fisher * @author Gary Russell @@ -72,15 +72,15 @@ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSu */ public static final String DEFAULT_ERRORS_KEY = "errors"; - private volatile Expression viewExpression; + private Expression viewExpression; - private volatile StandardEvaluationContext evaluationContext; + private StandardEvaluationContext evaluationContext; - private volatile String replyKey = DEFAULT_REPLY_KEY; + private String replyKey = DEFAULT_REPLY_KEY; - private volatile String errorsKey = DEFAULT_ERRORS_KEY; + private String errorsKey = DEFAULT_ERRORS_KEY; - private volatile String errorCode = DEFAULT_ERROR_CODE; + private String errorCode = DEFAULT_ERROR_CODE; public HttpRequestHandlingController() { this(true); @@ -157,7 +157,7 @@ public final ModelAndView handleRequest(HttpServletRequest servletRequest, HttpS RequestEntity httpEntity = prepareRequestEntity(request); - Message replyMessage = doHandleRequest(servletRequest, httpEntity, servletResponse); + Message replyMessage = doHandleRequest(servletRequest, httpEntity); ServletServerHttpResponse response = new ServletServerHttpResponse(servletResponse); if (replyMessage != null) { Object reply = setupResponseAndConvertReply(response, replyMessage); diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java index 15d109481ec..0bd6e46732e 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingEndpointSupport.java @@ -19,20 +19,19 @@ import java.io.IOException; import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Arrays; import java.util.List; import java.util.Map; -import java.util.Map.Entry; +import java.util.function.Function; +import java.util.stream.Collectors; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import javax.xml.transform.Source; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.core.ResolvableType; -import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.http.HttpEntity; import org.springframework.http.HttpMethod; @@ -53,11 +52,14 @@ import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.integration.MessageTimeoutException; +import org.springframework.integration.expression.ExpressionEvalMap; +import org.springframework.integration.http.HttpHeaders; import org.springframework.integration.http.converter.MultipartAwareFormHttpMessageConverter; import org.springframework.integration.http.multipart.MultipartHttpInputMessage; import org.springframework.integration.support.AbstractIntegrationMessageBuilder; import org.springframework.integration.support.json.JacksonPresent; import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessagingException; import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; @@ -109,21 +111,21 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound private final List> defaultMessageConverters = new ArrayList<>(); - private volatile List> messageConverters = new ArrayList<>(); + private List> messageConverters = new ArrayList<>(); - private volatile boolean convertersMerged; + private boolean convertersMerged; - private volatile boolean mergeWithDefaultConverters = false; + private boolean mergeWithDefaultConverters = false; - private volatile MultipartResolver multipartResolver; + private MultipartResolver multipartResolver; /** * Construct a gateway that will wait for the {@link #setReplyTimeout(long) * replyTimeout} for a reply; if the timeout is exceeded a '500 Internal Server Error' * status code is returned. This can be modified using the - * {@link #setStatusCodeExpression(Expression) statusCodeExpression}. + * {@link #setStatusCodeExpression statusCodeExpression}. * @see #setReplyTimeout(long) - * @see #setStatusCodeExpression(Expression) + * @see #setStatusCodeExpression */ public HttpRequestHandlingEndpointSupport() { this(true); @@ -133,12 +135,12 @@ public HttpRequestHandlingEndpointSupport() { * Construct a gateway. If 'expectReply' is true it will wait for the * {@link #setReplyTimeout(long) replyTimeout} for a reply; if the timeout is exceeded * a '500 Internal Server Error' status code is returned. This can be modified using - * the {@link #setStatusCodeExpression(Expression) statusCodeExpression}. + * the {@link #setStatusCodeExpression statusCodeExpression}. * If 'false', a 200 OK status will be returned; this can also be modified using - * {@link #setStatusCodeExpression(Expression) statusCodeExpression}. + * {@link #setStatusCodeExpression statusCodeExpression}. * @param expectReply true if a reply is expected from the downstream flow. * @see #setReplyTimeout(long) - * @see #setStatusCodeExpression(Expression) + * @see #setStatusCodeExpression */ public HttpRequestHandlingEndpointSupport(boolean expectReply) { super(expectReply); @@ -150,7 +152,7 @@ public HttpRequestHandlingEndpointSupport(boolean expectReply) { this.defaultMessageConverters.add(new ResourceHttpMessageConverter()); SourceHttpMessageConverter sourceConverter = new SourceHttpMessageConverter<>(); this.defaultMessageConverters.add(sourceConverter); - if (jaxb2Present) { + if (JAXB_PRESENT) { this.defaultMessageConverters.add(new Jaxb2RootElementHttpMessageConverter()); logger.debug("'Jaxb2RootElementHttpMessageConverter' was added to the 'defaultMessageConverters'."); } @@ -158,7 +160,7 @@ public HttpRequestHandlingEndpointSupport(boolean expectReply) { this.defaultMessageConverters.add(new MappingJackson2HttpMessageConverter()); logger.debug("'MappingJackson2HttpMessageConverter' was added to the 'defaultMessageConverters'."); } - if (romeToolsPresent) { + if (ROME_TOOLS_PRESENT) { this.defaultMessageConverters.add(new AtomFeedHttpMessageConverter()); this.defaultMessageConverters.add(new RssChannelHttpMessageConverter()); logger.debug("'AtomFeedHttpMessageConverter' was added to the 'defaultMessageConverters'."); @@ -244,63 +246,24 @@ protected void onInit() { * 'expectReply' property is true, it will also generate a response from the reply Message once received. * @param servletRequest The servlet request. * @param httpEntity the request entity to use. - * @param servletResponse The servlet response. * @return The response Message. */ - protected final Message doHandleRequest(HttpServletRequest servletRequest, RequestEntity httpEntity, - HttpServletResponse servletResponse) { + protected final Message doHandleRequest(HttpServletRequest servletRequest, RequestEntity httpEntity) { if (isRunning()) { - return actualDoHandleRequest(servletRequest, httpEntity, servletResponse); + return actualDoHandleRequest(servletRequest, httpEntity); } else { return createServiceUnavailableResponse(); } } - @SuppressWarnings("unchecked") - private Message actualDoHandleRequest(HttpServletRequest servletRequest, RequestEntity httpEntity, - HttpServletResponse servletResponse) { - + private Message actualDoHandleRequest(HttpServletRequest servletRequest, RequestEntity httpEntity) { this.activeCount.incrementAndGet(); try { - StandardEvaluationContext evaluationContext = createEvaluationContext(); - evaluationContext.setRootObject(httpEntity); - - evaluationContext.setVariable("requestAttributes", RequestContextHolder.currentRequestAttributes()); - MultiValueMap requestParams = convertParameterMap(servletRequest.getParameterMap()); - evaluationContext.setVariable("requestParams", requestParams); - evaluationContext.setVariable("requestHeaders", new ServletServerHttpRequest(servletRequest).getHeaders()); - - Cookie[] requestCookies = servletRequest.getCookies(); - if (!ObjectUtils.isEmpty(requestCookies)) { - Map cookies = new HashMap<>(requestCookies.length); - for (Cookie requestCookie : requestCookies) { - cookies.put(requestCookie.getName(), requestCookie); - } - evaluationContext.setVariable("cookies", cookies); - } - Map pathVariables = - (Map) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); - - if (!CollectionUtils.isEmpty(pathVariables)) { - if (logger.isDebugEnabled()) { - logger.debug("Mapped path variables: " + pathVariables); - } - evaluationContext.setVariable("pathVariables", pathVariables); - } - - Map> matrixVariables = - (Map>) servletRequest - .getAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE); - - if (!CollectionUtils.isEmpty(matrixVariables)) { - if (logger.isDebugEnabled()) { - logger.debug("Mapped matrix variables: " + matrixVariables); - } - evaluationContext.setVariable("matrixVariables", matrixVariables); - } + StandardEvaluationContext evaluationContext = + prepareEvaluationContext(servletRequest, httpEntity, requestParams); Map headers = getHeaderMapper().toHeaders(httpEntity.getHeaders()); Object payload = null; @@ -308,15 +271,12 @@ private Message actualDoHandleRequest(HttpServletRequest servletRequest, Requ // create payload based on SpEL payload = getPayloadExpression().getValue(evaluationContext); } + if (!CollectionUtils.isEmpty(getHeaderExpressions())) { - for (Entry entry : getHeaderExpressions().entrySet()) { - String headerName = entry.getKey(); - Expression headerExpression = entry.getValue(); - Object headerValue = headerExpression.getValue(evaluationContext); - if (headerValue != null) { - headers.put(headerName, headerValue); - } - } + headers.putAll( + ExpressionEvalMap.from(getHeaderExpressions()) + .usingEvaluationContext(evaluationContext) + .build()); } if (payload == null) { @@ -328,36 +288,10 @@ private Message actualDoHandleRequest(HttpServletRequest servletRequest, Requ } } - AbstractIntegrationMessageBuilder messageBuilder = null; - - if (payload instanceof Message) { - messageBuilder = - getMessageBuilderFactory() - .fromMessage((Message) payload) - .copyHeadersIfAbsent(headers); - } - else { - Assert.state(payload != null, "payload cannot be null"); - messageBuilder = getMessageBuilderFactory() - .withPayload(payload) - .copyHeaders(headers); - } - - HttpMethod method = httpEntity.getMethod(); - if (method != null) { - messageBuilder.setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_METHOD, - method.toString()); - } - - Message message = messageBuilder - .setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_URL, - httpEntity.getUrl().toString()) - .setHeader(org.springframework.integration.http.HttpHeaders.USER_PRINCIPAL, - servletRequest.getUserPrincipal()) - .build(); + Message message = prepareRequestMessage(servletRequest, httpEntity, headers, payload); Message reply = null; - if (this.expectReply) { + if (isExpectReply()) { try { reply = sendAndReceiveMessage(message); } @@ -365,8 +299,7 @@ private Message actualDoHandleRequest(HttpServletRequest servletRequest, Requ reply = getMessageBuilderFactory() .withPayload(e.getMessage()) - .setHeader(org.springframework.integration.http.HttpHeaders.STATUS_CODE, - evaluateHttpStatus(httpEntity)) + .setHeader(HttpHeaders.STATUS_CODE, evaluateHttpStatus(httpEntity)) .build(); } } @@ -381,13 +314,82 @@ private Message actualDoHandleRequest(HttpServletRequest servletRequest, Requ } } + private Message prepareRequestMessage(HttpServletRequest servletRequest, RequestEntity httpEntity, + Map headers, Object payload) { + + AbstractIntegrationMessageBuilder messageBuilder; + + if (payload instanceof Message) { + messageBuilder = + getMessageBuilderFactory() + .fromMessage((Message) payload) + .copyHeadersIfAbsent(headers); + } + else { + Assert.state(payload != null, "payload cannot be null"); + messageBuilder = getMessageBuilderFactory() + .withPayload(payload) + .copyHeaders(headers); + } + + HttpMethod method = httpEntity.getMethod(); + if (method != null) { + messageBuilder.setHeader(HttpHeaders.REQUEST_METHOD, method.toString()); + } + + return messageBuilder + .setHeader(HttpHeaders.REQUEST_URL, httpEntity.getUrl().toString()) + .setHeader(HttpHeaders.USER_PRINCIPAL, servletRequest.getUserPrincipal()) + .build(); + } + + private StandardEvaluationContext prepareEvaluationContext(HttpServletRequest servletRequest, + RequestEntity httpEntity, MultiValueMap requestParams) { + + StandardEvaluationContext evaluationContext = createEvaluationContext(); + evaluationContext.setRootObject(httpEntity); + + evaluationContext.setVariable("requestAttributes", RequestContextHolder.currentRequestAttributes()); + + evaluationContext.setVariable("requestParams", requestParams); + evaluationContext.setVariable("requestHeaders", new ServletServerHttpRequest(servletRequest).getHeaders()); + + Cookie[] requestCookies = servletRequest.getCookies(); + if (!ObjectUtils.isEmpty(requestCookies)) { + Map cookies = + Arrays.stream(requestCookies) + .collect(Collectors.toMap(Cookie::getName, Function.identity())); + evaluationContext.setVariable("cookies", cookies); + } + + Map pathVariables = + (Map) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + + if (!CollectionUtils.isEmpty(pathVariables)) { + if (logger.isDebugEnabled()) { + logger.debug("Mapped path variables: " + pathVariables); + } + evaluationContext.setVariable("pathVariables", pathVariables); + } + + Map matrixVariables = (Map) servletRequest.getAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE); + + if (!CollectionUtils.isEmpty(matrixVariables)) { + if (logger.isDebugEnabled()) { + logger.debug("Mapped matrix variables: " + matrixVariables); + } + evaluationContext.setVariable("matrixVariables", matrixVariables); + } + return evaluationContext; + } + private Message createServiceUnavailableResponse() { if (logger.isDebugEnabled()) { logger.debug("Endpoint is stopped; returning status " + HttpStatus.SERVICE_UNAVAILABLE); } return getMessageBuilderFactory() .withPayload("Endpoint is stopped") - .setHeader(org.springframework.integration.http.HttpHeaders.STATUS_CODE, HttpStatus.SERVICE_UNAVAILABLE) + .setHeader(HttpHeaders.STATUS_CODE, HttpStatus.SERVICE_UNAVAILABLE) .build(); } @@ -399,8 +401,9 @@ private Message createServiceUnavailableResponse() { * @return The message payload (if {@code extractReplyPayload}) otherwise the message. */ protected final Object setupResponseAndConvertReply(ServletServerHttpResponse response, Message replyMessage) { - getHeaderMapper().fromHeaders(replyMessage.getHeaders(), response.getHeaders()); - HttpStatus httpStatus = this.resolveHttpStatusFromHeaders(replyMessage.getHeaders()); + MessageHeaders replyMessageHeaders = replyMessage.getHeaders(); + getHeaderMapper().fromHeaders(replyMessageHeaders, response.getHeaders()); + HttpStatus httpStatus = resolveHttpStatusFromHeaders(replyMessageHeaders); if (httpStatus != null) { response.setStatusCode(httpStatus); } @@ -468,7 +471,7 @@ private MultiValueMap convertParameterMap(Map protected RequestEntity prepareRequestEntity(ServletServerHttpRequest request) throws IOException { Object requestBody = null; - if (isReadable(request)) { + if (isReadable(request.getMethod())) { requestBody = extractRequestBody(request); } @@ -499,8 +502,8 @@ protected Object extractRequestBody(ServletServerHttpRequest request) throws IOE ? (GenericHttpMessageConverter) converter : null; if (genericConverter != null - ? genericConverter.canRead(targetType, null, contentType) : - (converter.canRead(targetClass, contentType))) { + ? genericConverter.canRead(targetType, null, contentType) + : (converter.canRead(targetClass, contentType))) { if (genericConverter != null) { return genericConverter.read(targetType, null, request); diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGateway.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGateway.java index 38a3c46fa40..0383d28f7e6 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGateway.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGateway.java @@ -39,7 +39,7 @@ /** * Inbound Messaging Gateway that handles HTTP Requests. May be configured as a bean in the Application Context and * delegated to from a simple HttpRequestHandlerServlet in web.xml where the servlet and bean both have the - * same name. If the {@link #expectReply} property is set to true, a response can generated from a reply Message. + * same name. If the {@link #isExpectReply()} property is set to true, a response can generated from a reply Message. * Otherwise, the gateway will play the role of a unidirectional Channel Adapter with a simple status-based response * (e.g. 200 OK). *

@@ -109,14 +109,15 @@ public final void handleRequest(HttpServletRequest servletRequest, HttpServletRe RequestEntity httpEntity = prepareRequestEntity(request); try { - responseMessage = doHandleRequest(servletRequest, httpEntity, servletResponse); + responseMessage = doHandleRequest(servletRequest, httpEntity); if (responseMessage != null) { responseContent = setupResponseAndConvertReply(response, responseMessage); } } - catch (Exception e) { - responseContent = handleExceptionInternal(e); + catch (Exception ex) { + responseContent = handleExceptionInternal(ex); } + if (responseContent != null) { if (responseContent instanceof HttpStatus) { @@ -131,11 +132,10 @@ public final void handleRequest(HttpServletRequest servletRequest, HttpServletRe HttpHeaders outputHeaders = response.getHeaders(); HttpHeaders entityHeaders = responseEntity.getHeaders(); - if (!entityHeaders.isEmpty()) { - entityHeaders.entrySet().stream() - .filter(entry -> !outputHeaders.containsKey(entry.getKey())) - .forEach(entry -> outputHeaders.put(entry.getKey(), entry.getValue())); - } + entityHeaders.entrySet() + .stream() + .filter(entry -> !outputHeaders.containsKey(entry.getKey())) + .forEach(entry -> outputHeaders.put(entry.getKey(), entry.getValue())); } if (responseContent != null) { @@ -151,24 +151,23 @@ public final void handleRequest(HttpServletRequest servletRequest, HttpServletRe } } - private Object handleExceptionInternal(Exception e) throws IOException { + private Object handleExceptionInternal(Exception ex) throws IOException { if (this.convertExceptions && isExpectReply()) { - return e; + return ex; } else { - if (e instanceof IOException) { - throw (IOException) e; + if (ex instanceof IOException) { + throw (IOException) ex; } - else if (e instanceof RuntimeException) { - throw (RuntimeException) e; + else if (ex instanceof RuntimeException) { + throw (RuntimeException) ex; } else { - throw new MessagingException("error occurred handling HTTP request", e); + throw new MessagingException("error occurred handling HTTP request", ex); } } } - @SuppressWarnings({ "unchecked", "rawtypes" }) private void writeResponse(Object content, ServletServerHttpResponse response, List acceptTypesArg) throws IOException { @@ -176,10 +175,12 @@ private void writeResponse(Object content, ServletServerHttpResponse response, L if (CollectionUtils.isEmpty(acceptTypes)) { acceptTypes = Collections.singletonList(MediaType.ALL); } - for (HttpMessageConverter converter : getMessageConverters()) { + for (HttpMessageConverter converter : getMessageConverters()) { for (MediaType acceptType : acceptTypes) { if (converter.canWrite(content.getClass(), acceptType)) { - converter.write(content, acceptType, response); + @SuppressWarnings("unchecked") + HttpMessageConverter converterToUse = (HttpMessageConverter) converter; + converterToUse.write(content, acceptType, response); return; } } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java index de83f09b3cc..0ca840bc458 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/inbound/HttpRequestHandlingMessagingGatewayWithPathMappingTests.java @@ -82,7 +82,7 @@ public void withoutExpression() throws IOException { RequestEntity httpEntity = prepareRequestEntity(body, new ServletServerHttpRequest(request)); - Object result = gateway.doHandleRequest(request, httpEntity, response); + Object result = gateway.doHandleRequest(request, httpEntity); assertThat(result).isInstanceOf(Message.class); assertThat(((Message) result).getPayload()).isEqualTo("hello"); @@ -96,8 +96,6 @@ public void withPayloadExpressionPointingToPathVariable() throws Exception { replyChannel.send(message); }); MockHttpServletRequest request = new MockHttpServletRequest(); - MockHttpServletResponse response = new MockHttpServletResponse(); - request.setMethod("POST"); request.setContentType("text/plain"); request.setParameter("foo", "bar"); @@ -127,7 +125,7 @@ public void withPayloadExpressionPointingToPathVariable() throws Exception { RequestEntity httpEntity = prepareRequestEntity(body, new ServletServerHttpRequest(request)); - Object result = gateway.doHandleRequest(request, httpEntity, response); + Object result = gateway.doHandleRequest(request, httpEntity); assertThat(result).isInstanceOf(Message.class); assertThat(((Message) result).getPayload()).isEqualTo("bill"); } @@ -173,12 +171,12 @@ public void withoutPayloadExpressionPointingToUriVariables() throws Exception { RequestEntity httpEntity = prepareRequestEntity(body, new ServletServerHttpRequest(request)); - Object result = gateway.doHandleRequest(request, httpEntity, response); + Object result = gateway.doHandleRequest(request, httpEntity); assertThat(result).isInstanceOf(Message.class); assertThat(((Map) ((Message) result).getPayload()).get("f")).isEqualTo("bill"); } - private static RequestEntity prepareRequestEntity(Object body, ServletServerHttpRequest request) throws IOException { + private static RequestEntity prepareRequestEntity(Object body, ServletServerHttpRequest request) { return new RequestEntity<>(body, request.getHeaders(), request.getMethod(), request.getURI()); } diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java index b96e6cd5f6f..497958ce351 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/monitor/IntegrationMBeanExporter.java @@ -16,7 +16,6 @@ package org.springframework.integration.monitor; -import java.lang.reflect.Field; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -78,6 +77,7 @@ import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedMetric; import org.springframework.jmx.export.annotation.ManagedOperation; +import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.jmx.export.naming.MetadataNamingStrategy; import org.springframework.jmx.support.MetricType; import org.springframework.messaging.MessageChannel; @@ -114,7 +114,7 @@ * @author Artem Bilan * @author Meherzad Lahewala */ -@org.springframework.jmx.export.annotation.ManagedResource +@ManagedResource public class IntegrationMBeanExporter extends MBeanExporter implements ApplicationContextAware, EmbeddedValueResolverAware, DestructionAwareBeanPostProcessor { @@ -1036,32 +1036,21 @@ private MessageSourceMetrics enhanceSourceMonitor(MessageSourceMetrics monitor) return monitor; } - // Assignment algorithm and bean id, with bean id pulled reflectively out of enclosing endpoint if possible - String[] names = this.applicationContext.getBeanNamesForType(AbstractEndpoint.class); - - String name = null; String endpointName = null; String source = "endpoint"; Object endpoint = null; + String[] names = this.applicationContext.getBeanNamesForType(AbstractEndpoint.class); for (String beanName : names) { endpoint = this.applicationContext.getBean(beanName); - Object field = null; + Object target = null; if (monitor instanceof MessagingGatewaySupport && endpoint.equals(monitor)) { - field = monitor; + target = monitor; } - else { - try { - field = extractTarget(getField(endpoint, "source")); - } - catch (Exception e) { - logger.trace("Could not get source from bean = " + beanName); - endpoint = null; - } + else if (endpoint instanceof SourcePollingChannelAdapter) { + target = ((SourcePollingChannelAdapter) endpoint).getMessageSource(); } - - if (monitor.equals(field)) { - name = beanName; + if (monitor.equals(target)) { endpointName = beanName; break; } @@ -1070,12 +1059,13 @@ private MessageSourceMetrics enhanceSourceMonitor(MessageSourceMetrics monitor) if (endpointName == null) { endpoint = null; } - if (name != null && name.startsWith('_' + SI_PACKAGE)) { - name = getInternalComponentName(name); + if (endpointName != null && endpointName.startsWith('_' + SI_PACKAGE)) { + endpointName = getInternalComponentName(endpointName); source = "internal"; } - MessageSourceMetrics messageSourceMetrics = buildMessageSourceMetricsIfAny(monitor, name, source, endpoint); + MessageSourceMetrics messageSourceMetrics = + buildMessageSourceMetricsIfAny(monitor, endpointName, source, endpoint); if (endpointName != null) { this.endpointsByMonitor.put(messageSourceMetrics, endpointName); } @@ -1155,18 +1145,4 @@ private MessageSourceMetrics wrapMessageSourceInLifecycleMetrics(MessageSourceMe return result; } - private Object getField(Object target, String name) { - Assert.notNull(target, "Target object must not be null"); - Field field = ReflectionUtils.findField(target.getClass(), name); - if (field == null) { - throw new IllegalArgumentException("Could not find field [" + name + "] on target [" + target + "]"); - } - - if (logger.isDebugEnabled()) { - logger.debug("Getting field [" + name + "] from target [" + target + "]"); - } - ReflectionUtils.makeAccessible(field); - return ReflectionUtils.getField(field, target); - } - } diff --git a/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/inbound/WebFluxInboundEndpoint.java b/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/inbound/WebFluxInboundEndpoint.java index 95a6d16c633..31fba66e9aa 100644 --- a/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/inbound/WebFluxInboundEndpoint.java +++ b/spring-integration-webflux/src/main/java/org/springframework/integration/webflux/inbound/WebFluxInboundEndpoint.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2018 the original author or authors. + * Copyright 2017-2019 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. @@ -34,7 +34,6 @@ import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ResolvableType; import org.springframework.expression.EvaluationContext; -import org.springframework.expression.Expression; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -47,6 +46,7 @@ import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.integration.expression.ExpressionEvalMap; import org.springframework.integration.http.inbound.BaseHttpInboundEndpoint; import org.springframework.integration.support.AbstractIntegrationMessageBuilder; import org.springframework.messaging.Message; @@ -81,8 +81,6 @@ public class WebFluxInboundEndpoint extends BaseHttpInboundEndpoint implements W private static final MediaType MEDIA_TYPE_APPLICATION_ALL = new MediaType("application"); - private static final String UNCHECKED = "unchecked"; - private static final List SAFE_METHODS = Arrays.asList(HttpMethod.GET, HttpMethod.HEAD); private ServerCodecConfigurer codecConfigurer = ServerCodecConfigurer.create(); @@ -155,7 +153,7 @@ private Mono doHandle(ServerWebExchange exchange) { exchange.getRequest().getMethod(), exchange.getRequest().getURI())) .flatMap(entity -> buildMessage(entity, exchange)) .flatMap(requestTuple -> { - if (this.expectReply) { + if (isExpectReply()) { return sendAndReceiveMessageReactive(requestTuple.getT1()) .flatMap(replyMessage -> populateResponse(exchange, replyMessage)); } @@ -169,7 +167,7 @@ private Mono doHandle(ServerWebExchange exchange) { } private Mono extractRequestBody(ServerWebExchange exchange) { - if (isReadable(exchange.getRequest())) { + if (isReadable(exchange.getRequest().getMethod())) { return extractReadableRequestBody(exchange); } else { @@ -242,7 +240,6 @@ private Mono readRequestBody(ServerWebExchange exchange, MediaType contentTyp } } - @SuppressWarnings(UNCHECKED) private Mono, RequestEntity>> buildMessage(RequestEntity httpEntity, ServerWebExchange exchange) { @@ -260,20 +257,32 @@ private Mono, RequestEntity>> buildMessage(RequestEnti Map headers = getHeaderMapper().toHeaders(request.getHeaders()); if (!CollectionUtils.isEmpty(getHeaderExpressions())) { - for (Map.Entry entry : getHeaderExpressions().entrySet()) { - String headerName = entry.getKey(); - Expression headerExpression = entry.getValue(); - Object headerValue = headerExpression.getValue(evaluationContext); - if (headerValue != null) { - headers.put(headerName, headerValue); - } - } + headers.putAll( + ExpressionEvalMap.from(getHeaderExpressions()) + .usingEvaluationContext(evaluationContext) + .build()); } if (payload == null) { payload = requestParams; } + AbstractIntegrationMessageBuilder messageBuilder = + prepareRequestMessageBuilder(request, payload, headers); + + return exchange.getPrincipal() + .map(principal -> + messageBuilder + .setHeader(org.springframework.integration.http.HttpHeaders.USER_PRINCIPAL, principal)) + .defaultIfEmpty(messageBuilder) + .map(AbstractIntegrationMessageBuilder::build) + .zipWith(Mono.just(httpEntity)); + } + + @SuppressWarnings("unchecked") + private AbstractIntegrationMessageBuilder prepareRequestMessageBuilder(ServerHttpRequest request, + Object payload, Map headers) { + AbstractIntegrationMessageBuilder messageBuilder; if (payload instanceof Message) { @@ -296,17 +305,9 @@ private Mono, RequestEntity>> buildMessage(RequestEnti messageBuilder.setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_METHOD, httpMethod.toString()); } - - return exchange.getPrincipal() - .map(principal -> - messageBuilder - .setHeader(org.springframework.integration.http.HttpHeaders.USER_PRINCIPAL, principal)) - .defaultIfEmpty(messageBuilder) - .map(AbstractIntegrationMessageBuilder::build) - .zipWith(Mono.just(httpEntity)); + return messageBuilder; } - @SuppressWarnings(UNCHECKED) private EvaluationContext buildEvaluationContext(RequestEntity httpEntity, ServerWebExchange exchange) { ServerHttpRequest request = exchange.getRequest(); HttpHeaders requestHeaders = request.getHeaders(); @@ -322,16 +323,13 @@ private EvaluationContext buildEvaluationContext(RequestEntity httpEntity, Se evaluationContext.setVariable("cookies", request.getCookies()); } - Map pathVariables = - (Map) exchangeAttributes.get(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); + Map pathVariables = (Map) exchangeAttributes.get(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE); if (!CollectionUtils.isEmpty(pathVariables)) { evaluationContext.setVariable("pathVariables", pathVariables); } - Map> matrixVariables = - (Map>) exchangeAttributes - .get(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE); + Map matrixVariables = (Map) exchangeAttributes.get(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE); if (!CollectionUtils.isEmpty(matrixVariables)) { evaluationContext.setVariable("matrixVariables", matrixVariables); @@ -396,7 +394,7 @@ private Mono populateResponse(ServerWebExchange exchange, Message reply } } - @SuppressWarnings(UNCHECKED) + @SuppressWarnings("unchecked") private Mono writeResponseBody(ServerWebExchange exchange, Object body) { ResolvableType bodyType = ResolvableType.forInstance(body); ReactiveAdapter adapter = this.adapterRegistry.getAdapter(bodyType.resolve(), body);