From c92d7a5da90118501cb69defc31462de202095fa Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Mon, 30 Jun 2014 19:38:03 +0300 Subject: [PATCH] INT-3458: Compatibility with Spring Framework 4.1 JIRA: https://jira.spring.io/browse/INT-3458 * Change Spring AMQP to `1.3.5.RELEASE` * `HttpRequestHandlingEndpointSupport`: remove `MappingJacksonHttpMessageConverter` registration * `IntegrationRequestMappingHandlerMapping`: add 'fake' `name()` attribute to the inline `RequestMapping` annotation * `StoredProcJmxManagedBeanTests`: remove `context.stop();` code, Since `MBeanExporter` deregister MBeans on `stop()` now * `StoredProcPollingChannelAdapterParserTests`: change deprecated `ParameterizedSingleColumnRowMapper` to the `SingleColumnRowMapper` * `Jms`: comment out the reflection code to check the value for the `recoveryInterval`, because it is removed already in favor of `backOff` * `NotificationListeningMessageProducer`: move the start-up listener registration to the `onApplicationEvent`, because `MBeanExporter` moved `registerBeans()` to the `start()` now. The same `phase` might cause the issue, that MBeans aren't registered yet for `NotificationListeningMessageProducer` * `JpaOutboundGatewayTests`: change `@TransactionConfiguration` to the `@Transactional`. Don't know why the first doesn't work now. **Cherry-pick to 4.0.x** INT-3458: Addressing PR comments `NotificationListeningMessageProducer`: defer listener registration until `onApplicationEvent()` --- build.gradle | 2 +- .../gateway/GatewayInterfaceTests-context.xml | 2 +- .../HttpRequestHandlingEndpointSupport.java | 118 ++++++------------ ...tegrationRequestMappingHandlerMapping.java | 55 ++++---- .../jdbc/StoredProcJmxManagedBeanTests.java | 14 +-- ...dProcPollingChannelAdapterParserTests.java | 17 +-- ...redProcPollingChannelAdapterParserTest.xml | 3 +- .../config/JmsInboundGatewayParserTests.java | 22 ++-- ...essageDrivenChannelAdapterParserTests.java | 16 ++- .../config/JmsOutboundGatewayParserTests.java | 31 ++--- .../NotificationListeningMessageProducer.java | 25 +++- ...ficationListeningMessageProducerTests.java | 9 +- ...eningChannelAdapterParserTests-context.xml | 3 +- ...ionListeningChannelAdapterParserTests.java | 4 + .../jpa/outbound/JpaOutboundGatewayTests.java | 12 +- 15 files changed, 170 insertions(+), 163 deletions(-) diff --git a/build.gradle b/build.gradle index e80994a8c9..164f2a50b3 100644 --- a/build.gradle +++ b/build.gradle @@ -109,7 +109,7 @@ subprojects { subproject -> slf4jVersion = "1.7.6" smack3Version = '3.2.1' smackVersion = '4.0.0' - springAmqpVersion = project.hasProperty('springAmqpVersion') ? project.springAmqpVersion : '1.3.5.BUILD-SNAPSHOT' + springAmqpVersion = project.hasProperty('springAmqpVersion') ? project.springAmqpVersion : '1.3.5.RELEASE' springDataMongoVersion = '1.5.0.RELEASE' springDataRedisVersion = '1.3.0.RELEASE' springGemfireVersion = '1.4.0.RELEASE' diff --git a/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests-context.xml index e39484b141..951073d286 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/gateway/GatewayInterfaceTests-context.xml @@ -26,7 +26,7 @@ - + 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 1bfc2c65aa..a9afcf049f 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 @@ -27,6 +27,7 @@ import java.util.concurrent.atomic.AtomicInteger; 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; @@ -75,23 +76,29 @@ import org.springframework.web.servlet.HandlerMapping; /** * Base class for HTTP request handling endpoints. *

- * By default GET and POST requests are accepted via a supplied default instance of {@link RequestMapping}. + * By default GET and POST requests are accepted via a supplied default instance + * of {@link RequestMapping}. * A GET request will generate a payload containing its 'parameterMap' while a POST - * request will be converted to a Message payload according to the registered {@link HttpMessageConverter}s. Several are - * registered by default, but the list can be explicitly set via {@link #setMessageConverters(List)}. + * request will be converted to a Message payload according to the registered + * {@link HttpMessageConverter}s. + * Several are registered by default, but the list can be explicitly set via + * {@link #setMessageConverters(List)}. *

* To customize the mapping of request headers to the MessageHeaders, provide a * reference to a {@code HeaderMapper} implementation * to the {@link #setHeaderMapper(HeaderMapper)} method. *

- * The behavior is "request/reply" by default. Pass {@code false} to the constructor to force send-only as opposed - * to sendAndReceive. Send-only means that as soon as the Message is created and passed to the - * {@link #setRequestChannel(org.springframework.messaging.MessageChannel) request channel}, a response will be - * generated. Subclasses determine how that response is generated (e.g. simple status response or rendering a View). + * The behavior is "request/reply" by default. Pass {@code false} to the constructor + * to force send-only as opposed to sendAndReceive. Send-only means that as soon as + * the Message is created and passed to the + * {@link #setRequestChannel(org.springframework.messaging.MessageChannel) request channel}, + * a response will be generated. Subclasses determine how that response is generated + * (e.g. simple status response or rendering a View). *

- * In a request-reply scenario, the reply Message's payload will be extracted prior to generating a response by default. - * To have the entire serialized Message available for the response, switch the {@link #extractReplyPayload} value to - * {@code false}. + * In a request-reply scenario, the reply Message's payload will be extracted prior + * to generating a response by default. + * To have the entire serialized Message available for the response, switch the + * {@link #extractReplyPayload} value to {@code false}. * * @author Mark Fisher * @author Oleg Zhurakousky @@ -144,7 +151,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa this(true); } - @SuppressWarnings("deprecation") public HttpRequestHandlingEndpointSupport(boolean expectReply) { this.expectReply = expectReply; this.defaultMessageConverters.add(new MultipartAwareFormHttpMessageConverter()); @@ -153,8 +159,7 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa stringHttpMessageConverter.setWriteAcceptCharset(false); this.defaultMessageConverters.add(stringHttpMessageConverter); this.defaultMessageConverters.add(new ResourceHttpMessageConverter()); - @SuppressWarnings("rawtypes") - SourceHttpMessageConverter sourceConverter = new SourceHttpMessageConverter(); + SourceHttpMessageConverter sourceConverter = new SourceHttpMessageConverter(); this.defaultMessageConverters.add(sourceConverter); if (jaxb2Present) { this.defaultMessageConverters.add(new Jaxb2RootElementHttpMessageConverter()); @@ -168,12 +173,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa logger.debug("'MappingJackson2HttpMessageConverter' was added to the 'defaultMessageConverters'."); } } - else if (JacksonJsonUtils.isJacksonPresent()) { - this.defaultMessageConverters.add(new org.springframework.http.converter.json.MappingJacksonHttpMessageConverter()); - if (logger.isDebugEnabled()) { - logger.debug("'MappingJacksonHttpMessageConverter' was added to the 'defaultMessageConverters'."); - } - } if (romePresent) { this.defaultMessageConverters.add(new AtomFeedHttpMessageConverter()); this.defaultMessageConverters.add(new RssChannelHttpMessageConverter()); @@ -191,29 +190,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa return expectReply; } - /** - * Set the path template for which this endpoint expects requests. - * May include path variable {keys} to match against. - * - * @param path The path. - * - * @deprecated since 3.0 in favor of {@linkplain #requestMapping} - */ - @Deprecated - public void setPath(String path) { - this.requestMapping.setPathPatterns(path); - } - - /** - * @return The path. - * @deprecated since 3.0 in favor of {@linkplain #requestMapping} - */ - @Deprecated - String getPath() { - String[] pathPatterns = this.requestMapping.getPathPatterns(); - return !ObjectUtils.isEmpty(pathPatterns) ? pathPatterns[0] : null; - } - /** * Specifies a SpEL expression to evaluate in order to generate the Message payload. * The EvaluationContext will be populated with an HttpEntity instance as the root object, @@ -226,7 +202,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa *

  • #matrixVariables
  • *
  • #cookies * - * * @param payloadExpression The payload expression. */ public void setPayloadExpression(Expression payloadExpression) { @@ -246,7 +221,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa *
  • #matrixVariables
  • *
  • #cookies * - * * @param headerExpressions The header expressions. */ public void setHeaderExpressions(Map headerExpressions) { @@ -256,7 +230,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa /** * Set the message body converters to use. These converters are used to convert from and to HTTP requests and * responses. - * * @param messageConverters The message converters. */ public void setMessageConverters(List> messageConverters) { @@ -277,7 +250,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa /** * Flag which determines if the default converters should be available after * custom converters. - * * @param mergeWithDefaultConverters true to merge, false to replace. */ public void setMergeWithDefaultConverters(boolean mergeWithDefaultConverters) { @@ -286,7 +258,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa /** * Set the {@link HeaderMapper} to use when mapping between HTTP headers and MessageHeaders. - * * @param headerMapper The header mapper. */ public void setHeaderMapper(HeaderMapper headerMapper) { @@ -296,7 +267,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa /** * Set the {@link RequestMapping} which allows you to specify a flexible RESTFul-mapping for this endpoint. - * * @param requestMapping The request mapping. */ public void setRequestMapping(RequestMapping requestMapping) { @@ -308,24 +278,10 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa return requestMapping; } - /** - * Specify the supported request methods for this gateway. By default, only GET and POST are supported. - * - * @param supportedMethods The supported methods. - * - * @deprecated since 3.0 in favor to {@linkplain #requestMapping} - */ - @Deprecated - public void setSupportedMethods(HttpMethod... supportedMethods) { - Assert.notEmpty(supportedMethods, "at least one supported method is required"); - this.requestMapping.setMethods(supportedMethods); - } - /** * Specify the type of payload to be generated when the inbound HTTP request content is read by the * {@link HttpMessageConverter}s. By default this value is null which means at runtime any "text" Content-Type will * result in String while all others default to byte[].class. - * * @param requestPayloadType The payload type. */ public void setRequestPayloadType(Class requestPayloadType) { @@ -335,7 +291,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa /** * Specify whether only the reply Message's payload should be passed in the response. If this is set to 'false', the * entire Message will be used to generate the response. The default is 'true'. - * * @param extractReplyPayload true to extract the reply payload. */ public void setExtractReplyPayload(boolean extractReplyPayload) { @@ -346,7 +301,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa * Specify the {@link MultipartResolver} to use when checking requests. If no resolver is provided, the * "multipartResolver" bean in the context will be used as a fallback. If that is not available either, this * endpoint will not support multipart requests. - * * @param multipartResolver The multipart resolver. */ public void setMultipartResolver(MultipartResolver multipartResolver) { @@ -394,13 +348,13 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa /** * Handles the HTTP request by generating a Message and sending it to the request channel. If this gateway's * 'expectReply' property is true, it will also generate a response from the reply Message once received. - * * @param servletRequest The servlet request. * @param servletResponse The servlet response. * @return The response Message. * @throws IOException Any IOException. */ - protected final Message doHandleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException { + protected final Message doHandleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse) + throws IOException { if (this.isShuttingDown()) { return createServiceUnavailableResponse(); } @@ -410,7 +364,8 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa } @SuppressWarnings({"rawtypes", "unchecked"}) - private Message actualDoHandleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws IOException { + private Message actualDoHandleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse) + throws IOException { this.activeCount.incrementAndGet(); try { ServletServerHttpRequest request = this.prepareRequest(servletRequest); @@ -451,7 +406,8 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa } Map> matrixVariables = - (Map>) servletRequest.getAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE); + (Map>) servletRequest + .getAttribute(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE); if (!CollectionUtils.isEmpty(matrixVariables)) { if (logger.isDebugEnabled()) { @@ -488,16 +444,20 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa AbstractIntegrationMessageBuilder messageBuilder = null; if (payload instanceof Message) { - messageBuilder = this.getMessageBuilderFactory().fromMessage((Message) payload).copyHeadersIfAbsent(headers); + messageBuilder = this.getMessageBuilderFactory().fromMessage((Message) payload) + .copyHeadersIfAbsent(headers); } else { messageBuilder = this.getMessageBuilderFactory().withPayload(payload).copyHeaders(headers); } Message message = messageBuilder - .setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_URL, request.getURI().toString()) - .setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_METHOD, request.getMethod().toString()) - .setHeader(org.springframework.integration.http.HttpHeaders.USER_PRINCIPAL, servletRequest.getUserPrincipal()) + .setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_URL, + request.getURI().toString()) + .setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_METHOD, + request.getMethod().toString()) + .setHeader(org.springframework.integration.http.HttpHeaders.USER_PRINCIPAL, + servletRequest.getUserPrincipal()) .build(); Message reply = null; @@ -527,7 +487,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa /** * Converts the reply message to the appropriate HTTP reply object and * sets up the {@link ServletServerHttpResponse}. - * * @param response The ServletServerHttpResponse. * @param replyMessage The reply message. * @return The message payload (if {@link #extractReplyPayload}) otherwise the message. @@ -549,8 +508,9 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa } /** - * Prepares an instance of {@link ServletServerHttpRequest} from the raw {@link HttpServletRequest}. Also converts - * the request into a multipart request to make multiparts available if necessary. If no multipart resolver is set, + * Prepares an instance of {@link ServletServerHttpRequest} from the raw + * {@link HttpServletRequest}. Also converts the request into a multipart request to + * make multiparts available if necessary. If no multipart resolver is set, * simply returns the existing request. * @param servletRequest current HTTP request * @return the processed request (multipart wrapper if necessary) @@ -576,7 +536,6 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa /** * Clean up any resources used by the given multipart request (if any). - * * @param request current HTTP request * @see MultipartResolver#cleanupMultipart */ @@ -638,10 +597,11 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa private void validateSupportedMethods() { if (this.requestPayloadType != null - && CollectionUtils.containsAny(nonReadableBodyHttpMethods, Arrays.asList(this.requestMapping.getMethods()))) { + && CollectionUtils.containsAny(nonReadableBodyHttpMethods, + Arrays.asList(this.requestMapping.getMethods()))) { if (logger.isWarnEnabled()) { - logger.warn("The 'requestPayloadType' attribute will have no relevance for one of the specified HTTP methods '" + - nonReadableBodyHttpMethods + "'"); + logger.warn("The 'requestPayloadType' attribute will have no relevance for one " + + "of the specified HTTP methods '" + nonReadableBodyHttpMethods + "'"); } } } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/IntegrationRequestMappingHandlerMapping.java b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/IntegrationRequestMappingHandlerMapping.java index 47c5c18816..de696efc31 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/IntegrationRequestMappingHandlerMapping.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/inbound/IntegrationRequestMappingHandlerMapping.java @@ -36,31 +36,37 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl /** * The {@link org.springframework.web.servlet.HandlerMapping} implementation that - * detects and registers {@link RequestMappingInfo}s for {@link HttpRequestHandlingEndpointSupport} - * from a Spring Integration HTTP configuration of - * {@code } and {@code } elements. + * detects and registers {@link RequestMappingInfo}s for + * {@link HttpRequestHandlingEndpointSupport} from a Spring Integration HTTP configuration + * of {@code } and {@code } elements. *

    - * This class is automatically configured as a bean in the application context during the parsing phase of - * the {@code } and {@code } elements, if there is none registered, yet. - * However it can be configured as a regular bean with appropriate configuration for - * {@link RequestMappingHandlerMapping}. It is recommended to have only one similar bean in the application context - * using the 'id' {@link org.springframework.integration.http.support.HttpContextUtils#HANDLER_MAPPING_BEAN_NAME}. + * This class is automatically configured as a bean in the application context during the + * parsing phase of the {@code } and {@code } + * elements, if there is none registered, yet. However it can be configured as a regular + * bean with appropriate configuration for {@link RequestMappingHandlerMapping}. + * It is recommended to have only one similar bean in the application context using the 'id' + * {@link org.springframework.integration.http.support.HttpContextUtils#HANDLER_MAPPING_BEAN_NAME}. *

    - * In most cases, Spring MVC offers to configure Request Mapping via {@code org.springframework.stereotype.Controller} - * and {@link org.springframework.web.bind.annotation.RequestMapping}. - * That's why Spring MVC's Handler Mapping infrastructure relies on {@link org.springframework.web.method.HandlerMethod}, - * as different methods at the same {@code org.springframework.stereotype.Controller} user-class may have their own - * {@link org.springframework.web.bind.annotation.RequestMapping}. On the other side, all Spring Integration HTTP Inbound - * Endpoints are configured on the basis of the same {@link HttpRequestHandlingEndpointSupport} class and there is no - * single {@link RequestMappingInfo} configuration without {@link org.springframework.web.method.HandlerMethod} in Spring MVC. - * Accordingly {@link IntegrationRequestMappingHandlerMapping} is a {@link org.springframework.web.servlet.HandlerMapping} - * compromise implementation between method-level annotations and component-level (e.g. Spring Integration XML) configurations. + * In most cases, Spring MVC offers to configure Request Mapping via + * {@code org.springframework.stereotype.Controller} and + * {@link org.springframework.web.bind.annotation.RequestMapping}. + * That's why Spring MVC's Handler Mapping infrastructure relies on + * {@link org.springframework.web.method.HandlerMethod}, as different methods at the same + * {@code org.springframework.stereotype.Controller} user-class may have their own + * {@link org.springframework.web.bind.annotation.RequestMapping}. + * On the other side, all Spring Integration HTTP Inbound Endpoints are configured on + * the basis of the same {@link HttpRequestHandlingEndpointSupport} class and there is no + * single {@link RequestMappingInfo} configuration without + * {@link org.springframework.web.method.HandlerMethod} in Spring MVC. + * Accordingly {@link IntegrationRequestMappingHandlerMapping} is a + * {@link org.springframework.web.servlet.HandlerMapping} + * compromise implementation between method-level annotations and component-level + * (e.g. Spring Integration XML) configurations. * * @author Artem Bilan + * @since 3.0 * @see RequestMapping * @see RequestMappingHandlerMapping - * - * @since 3.0 */ public final class IntegrationRequestMappingHandlerMapping extends RequestMappingHandlerMapping implements ApplicationListener { @@ -96,8 +102,8 @@ public final class IntegrationRequestMappingHandlerMapping extends RequestMappin } /** - * Created a {@link RequestMappingInfo} from a 'Spring Integration HTTP Inbound Endpoint' {@link RequestMapping}. - * + * Created a {@link RequestMappingInfo} from a + * 'Spring Integration HTTP Inbound Endpoint' {@link RequestMapping}. * @see RequestMappingHandlerMapping#getMappingForMethod */ private RequestMappingInfo getMappingForEndpoint(HttpRequestHandlingEndpointSupport endpoint) { @@ -109,6 +115,12 @@ public final class IntegrationRequestMappingHandlerMapping extends RequestMappin org.springframework.web.bind.annotation.RequestMapping requestMappingAnnotation = new org.springframework.web.bind.annotation.RequestMapping() { + + //TODO consider add 'name' support when SF 4.1 will be minimal + public String name() { + return null; + } + @Override public String[] value() { return requestMapping.getPathPatterns(); @@ -157,7 +169,6 @@ public final class IntegrationRequestMappingHandlerMapping extends RequestMappin * {@link HttpRequestHandlingEndpointSupport}s may depend on auto-created * {@code requestChannel}s, so MVC Handlers detection should be postponed * as late as possible. - * * @see RequestMappingHandlerMapping#afterPropertiesSet() */ @Override diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcJmxManagedBeanTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcJmxManagedBeanTests.java index 6aa871dc36..ed7f092afe 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcJmxManagedBeanTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/StoredProcJmxManagedBeanTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2014 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,8 +16,7 @@ package org.springframework.integration.jdbc; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.*; import java.util.ArrayList; import java.util.Collection; @@ -35,12 +34,12 @@ import javax.management.ObjectName; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.support.AbstractApplicationContext; -import org.springframework.messaging.Message; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.jdbc.storedproc.CreateUser; import org.springframework.integration.jdbc.storedproc.User; +import org.springframework.messaging.Message; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; @@ -48,15 +47,13 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Gunnar Hillert + * @author Artem Bilan */ @ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) @DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) public class StoredProcJmxManagedBeanTests { - @Autowired - private AbstractApplicationContext context; - @Autowired private Consumer consumer; @@ -147,7 +144,6 @@ public class StoredProcJmxManagedBeanTests { received.add(consumer.poll(2000)); Message> message = received.get(0); - context.stop(); assertNotNull(message); assertNotNull(message.getPayload()); diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParserTests.java b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParserTests.java index 9fe36b00ae..18c572bb28 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParserTests.java +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/StoredProcPollingChannelAdapterParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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 @@ -13,12 +13,7 @@ package org.springframework.integration.jdbc.config; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.sql.Types; import java.util.Iterator; @@ -33,24 +28,22 @@ import org.springframework.beans.DirectFieldAccessor; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.expression.Expression; -import org.springframework.messaging.MessageChannel; import org.springframework.integration.endpoint.SourcePollingChannelAdapter; import org.springframework.integration.jdbc.storedproc.PrimeMapper; import org.springframework.integration.jdbc.storedproc.ProcedureParameter; import org.springframework.integration.test.util.TestUtils; import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.SingleColumnRowMapper; import org.springframework.jdbc.core.SqlInOutParameter; import org.springframework.jdbc.core.SqlOutParameter; import org.springframework.jdbc.core.SqlParameter; -import org.springframework.jdbc.core.simple.ParameterizedSingleColumnRowMapper; +import org.springframework.messaging.MessageChannel; /** * @author Gunnar Hillert * @author Gary Russell * @author Artem Bilan - * * @since 2.1 - * */ public class StoredProcPollingChannelAdapterParserTests { @@ -216,7 +209,7 @@ public class StoredProcPollingChannelAdapterParserTests { mapEntry = iterator.next(); assertEquals("out2", mapEntry.getKey()); - assertTrue(mapEntry.getValue() instanceof ParameterizedSingleColumnRowMapper); + assertTrue(mapEntry.getValue() instanceof SingleColumnRowMapper); } diff --git a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/storedProcPollingChannelAdapterParserTest.xml b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/storedProcPollingChannelAdapterParserTest.xml index 320988640f..ec4c936d46 100644 --- a/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/storedProcPollingChannelAdapterParserTest.xml +++ b/spring-integration-jdbc/src/test/java/org/springframework/integration/jdbc/config/storedProcPollingChannelAdapterParserTest.xml @@ -29,8 +29,7 @@ - + diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundGatewayParserTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundGatewayParserTests.java index b0cfc34f23..8a0987339b 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundGatewayParserTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsInboundGatewayParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2014 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,10 +16,7 @@ package org.springframework.integration.jms.config; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.*; import java.util.Properties; @@ -27,11 +24,11 @@ import javax.jms.ConnectionFactory; import javax.jms.DeliveryMode; import org.junit.Test; + import org.springframework.beans.DirectFieldAccessor; +import org.springframework.beans.NotReadablePropertyException; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.context.support.ClassPathXmlApplicationContext; -import org.springframework.messaging.Message; -import org.springframework.messaging.PollableChannel; import org.springframework.integration.history.MessageHistory; import org.springframework.integration.jms.JmsMessageDrivenEndpoint; import org.springframework.integration.test.util.TestUtils; @@ -40,6 +37,8 @@ import org.springframework.jms.core.JmsTemplate; import org.springframework.jms.listener.AbstractMessageListenerContainer; import org.springframework.jms.listener.DefaultMessageListenerContainer; import org.springframework.jms.support.destination.JmsDestinationAccessor; +import org.springframework.messaging.Message; +import org.springframework.messaging.PollableChannel; /** * @author Mark Fisher @@ -259,7 +258,14 @@ public class JmsInboundGatewayParserTests { gateway.start(); AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) new DirectFieldAccessor(gateway).getPropertyValue("listenerContainer"); - assertEquals(2222L, new DirectFieldAccessor(container).getPropertyValue("recoveryInterval")); + Object recoveryInterval; + try { + recoveryInterval = TestUtils.getPropertyValue(container, "recoveryInterval"); + } + catch (NotReadablePropertyException e) { + recoveryInterval = TestUtils.getPropertyValue(container, "backOff.interval"); + } + assertEquals(2222L, recoveryInterval); gateway.stop(); } diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java index eda222fd42..05bb0770f8 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsMessageDrivenChannelAdapterParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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,15 +16,14 @@ package org.springframework.integration.jms.config; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; +import static org.junit.Assert.*; import java.util.Properties; import org.junit.Test; import org.springframework.beans.DirectFieldAccessor; +import org.springframework.beans.NotReadablePropertyException; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.history.MessageHistory; import org.springframework.integration.jms.JmsMessageDrivenEndpoint; @@ -121,7 +120,14 @@ public class JmsMessageDrivenChannelAdapterParserTests { adapter.start(); AbstractMessageListenerContainer container = (AbstractMessageListenerContainer) new DirectFieldAccessor(adapter).getPropertyValue("listenerContainer"); - assertEquals(2222L, new DirectFieldAccessor(container).getPropertyValue("recoveryInterval")); + Object recoveryInterval; + try { + recoveryInterval = TestUtils.getPropertyValue(container, "recoveryInterval"); + } + catch (NotReadablePropertyException e) { + recoveryInterval = TestUtils.getPropertyValue(container, "backOff.interval"); + } + assertEquals(2222L, recoveryInterval); adapter.stop(); context.close(); } diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundGatewayParserTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundGatewayParserTests.java index 84c75c9ee0..fe1276b00a 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundGatewayParserTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/JmsOutboundGatewayParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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,17 +16,8 @@ package org.springframework.integration.jms.config; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; import java.lang.reflect.Method; import java.util.Properties; @@ -38,10 +29,13 @@ import javax.jms.Session; import org.junit.Test; import org.mockito.Mockito; + import org.springframework.beans.DirectFieldAccessor; +import org.springframework.beans.NotReadablePropertyException; import org.springframework.beans.factory.parsing.BeanDefinitionParsingException; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.expression.Expression; +import org.springframework.integration.core.MessagingTemplate; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.endpoint.PollingConsumer; import org.springframework.integration.handler.ExpressionEvaluatingMessageProcessor; @@ -49,7 +43,6 @@ import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvi import org.springframework.integration.history.MessageHistory; import org.springframework.integration.jms.JmsOutboundGateway; import org.springframework.integration.jms.StubMessageConverter; -import org.springframework.messaging.support.GenericMessage; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; import org.springframework.jms.listener.DefaultMessageListenerContainer; @@ -59,7 +52,7 @@ import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessagingException; import org.springframework.messaging.SubscribableChannel; -import org.springframework.integration.core.MessagingTemplate; +import org.springframework.messaging.support.GenericMessage; /** * @author Jonas Partner @@ -87,7 +80,15 @@ public class JmsOutboundGatewayParserTests { assertEquals(5, TestUtils.getPropertyValue(container, "maxConcurrentConsumers")); assertEquals(10, TestUtils.getPropertyValue(container, "maxMessagesPerTask")); assertEquals(2000L, TestUtils.getPropertyValue(container, "receiveTimeout")); - assertEquals(10000L, TestUtils.getPropertyValue(container, "recoveryInterval")); + Object recoveryInterval; + try { + recoveryInterval = TestUtils.getPropertyValue(container, "recoveryInterval"); + } + catch (NotReadablePropertyException e) { + recoveryInterval = TestUtils.getPropertyValue(container, "backOff.interval"); + } + assertEquals(10000L, recoveryInterval); + assertEquals(7, TestUtils.getPropertyValue(container, "idleConsumerLimit")); assertEquals(2, TestUtils.getPropertyValue(container, "idleTaskExecutionLimit")); assertEquals(3, TestUtils.getPropertyValue(container, "cacheLevel")); diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/NotificationListeningMessageProducer.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/NotificationListeningMessageProducer.java index 8add0dbc39..696695e2e5 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/NotificationListeningMessageProducer.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/NotificationListeningMessageProducer.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; import javax.management.InstanceNotFoundException; import javax.management.ListenerNotFoundException; @@ -35,6 +36,8 @@ import javax.management.ObjectName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.integration.endpoint.MessageProducerSupport; import org.springframework.integration.support.AbstractIntegrationMessageBuilder; import org.springframework.messaging.Message; @@ -47,12 +50,16 @@ import org.springframework.util.ObjectUtils; * * @author Mark Fisher * @author Gary Russell + * @author Artem Bilan * @since 2.0 */ -public class NotificationListeningMessageProducer extends MessageProducerSupport implements NotificationListener { +public class NotificationListeningMessageProducer extends MessageProducerSupport + implements NotificationListener, ApplicationListener { private final Log logger = LogFactory.getLog(this.getClass()); + private final AtomicBoolean listenerRegisteredOnStartup = new AtomicBoolean(); + private volatile MBeanServerConnection server; private volatile ObjectName[] objectNames; @@ -128,11 +135,26 @@ public class NotificationListeningMessageProducer extends MessageProducerSupport return "jmx:notification-listening-channel-adapter"; } + /** + * The {@link NotificationListener} might not be registered on {@link #start()} + * because the {@code MBeanExporter} might not been started yet. + * @param event the ContextRefreshedEvent event + */ + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + if (!this.listenerRegisteredOnStartup.getAndSet(true) && isAutoStartup()) { + doStart(); + } + } + /** * Registers the notification listener with the specified ObjectNames. */ @Override protected void doStart() { + if (!this.listenerRegisteredOnStartup.get()) { + return; + } logger.debug("Registering to receive notifications"); try { Assert.notNull(this.server, "MBeanServer is required."); @@ -201,4 +223,5 @@ public class NotificationListeningMessageProducer extends MessageProducerSupport } return objectNames; } + } diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/NotificationListeningMessageProducerTests.java b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/NotificationListeningMessageProducerTests.java index d4f0b37875..ac38c9cf36 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/NotificationListeningMessageProducerTests.java +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/NotificationListeningMessageProducerTests.java @@ -32,8 +32,11 @@ import javax.management.ObjectName; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; import org.springframework.beans.factory.BeanFactory; +import org.springframework.context.ApplicationContext; +import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.integration.channel.QueueChannel; import org.springframework.jmx.export.MBeanExporter; import org.springframework.jmx.export.notification.NotificationPublisher; @@ -44,6 +47,7 @@ import org.springframework.messaging.Message; /** * @author Mark Fisher + * @author Artem Bilan * @since 2.0 */ public class NotificationListeningMessageProducerTests { @@ -83,6 +87,7 @@ public class NotificationListeningMessageProducerTests { adapter.setBeanFactory(mock(BeanFactory.class)); adapter.afterPropertiesSet(); adapter.start(); + adapter.onApplicationEvent(new ContextRefreshedEvent(Mockito.mock(ApplicationContext.class))); this.numberHolder.publish("foo"); Message message = outputChannel.receive(0); assertNotNull(message); @@ -100,11 +105,12 @@ public class NotificationListeningMessageProducerTests { adapter.setServer(this.server); adapter.setObjectName(this.objectName); adapter.setOutputChannel(outputChannel); - Integer handback = new Integer(123); + Integer handback = 123; adapter.setHandback(handback); adapter.setBeanFactory(mock(BeanFactory.class)); adapter.afterPropertiesSet(); adapter.start(); + adapter.onApplicationEvent(new ContextRefreshedEvent(Mockito.mock(ApplicationContext.class))); this.numberHolder.publish("foo"); Message message = outputChannel.receive(0); assertNotNull(message); @@ -132,6 +138,7 @@ public class NotificationListeningMessageProducerTests { adapter.setBeanFactory(mock(BeanFactory.class)); adapter.afterPropertiesSet(); adapter.start(); + adapter.onApplicationEvent(new ContextRefreshedEvent(Mockito.mock(ApplicationContext.class))); this.numberHolder.publish("bad"); Message message = outputChannel.receive(0); assertNull(message); diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationListeningChannelAdapterParserTests-context.xml b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationListeningChannelAdapterParserTests-context.xml index 9896a90e5a..f0746eae95 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationListeningChannelAdapterParserTests-context.xml +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationListeningChannelAdapterParserTests-context.xml @@ -45,7 +45,8 @@ + object-name="#{patterns}" + auto-startup="false"/> org.springframework.integration.jmx.config:type=*,name=testPublisher diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationListeningChannelAdapterParserTests.java b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationListeningChannelAdapterParserTests.java index 68e42f91b6..c9ace1652b 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationListeningChannelAdapterParserTests.java +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/NotificationListeningChannelAdapterParserTests.java @@ -66,8 +66,12 @@ public class NotificationListeningChannelAdapterParserTests { @Autowired @Qualifier("autoChannel.adapter") private NotificationListeningMessageProducer autoChannelAdapter; + @Autowired @Qualifier("multiAdapter") + private NotificationListeningMessageProducer multiAdapter; + @Test public void receiveNotification() throws Exception { + this.multiAdapter.start(); assertNull(channel.receive(0)); testPublisher.send("ABC"); verifyReceipt(channel, "testPublisher"); diff --git a/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/outbound/JpaOutboundGatewayTests.java b/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/outbound/JpaOutboundGatewayTests.java index 0c7fbc0da9..d374222134 100644 --- a/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/outbound/JpaOutboundGatewayTests.java +++ b/spring-integration-jpa/src/test/java/org/springframework/integration/jpa/outbound/JpaOutboundGatewayTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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 @@ -10,6 +10,7 @@ * 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.integration.jpa.outbound; import java.util.List; @@ -20,13 +21,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.messaging.MessagingException; import org.springframework.integration.jpa.test.JpaTestUtils; import org.springframework.integration.jpa.test.entity.StudentDomain; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.messaging.MessagingException; +import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.test.context.transaction.TransactionConfiguration; import org.springframework.transaction.annotation.Transactional; /** @@ -34,13 +35,12 @@ import org.springframework.transaction.annotation.Transactional; * @author Gunnar Hillert * @author Artem Bilan * @author Amol Nayak - * * @since 2.2 - * */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration -@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true) +@Transactional +@DirtiesContext public class JpaOutboundGatewayTests { @Autowired