INT-3778: STOMP Namespace and Documentation

JIRA: https://jira.spring.io/browse/INT-3778

* Add Namespace support for STOMP adapters
* Document the STOMP module
* Fix race condition in the `AbstractStompSessionManager`
* Add `handleTransportError` to the `StompInboundChannelAdapter` and `StompMessageHandler` to log errors during STOMP interactions
* Fix `StompMessageHandler` to handle `RECEIPT` in case of `RECEIPT` header existence, not the common `autoReceipt` option
which can be disable, but the `RECEIPT` header may be present in the message to send
* Increase logging level for the STOMP tests to trace sporadic failures on the CI Server
* Fix several typos

INT-3778: Doc Polishing
This commit is contained in:
Artem Bilan
2015-08-04 11:06:23 -04:00
committed by Gary Russell
parent e82bfdc9a6
commit d89dabe72f
17 changed files with 943 additions and 24 deletions

View File

@@ -17,6 +17,7 @@
package org.springframework.integration.stomp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.logging.Log;
@@ -178,7 +179,8 @@ public abstract class AbstractStompSessionManager implements StompSessionManager
private static class CompositeStompSessionHandler extends StompSessionHandlerAdapter {
private final List<StompSessionHandler> delegates = new ArrayList<StompSessionHandler>();
private final List<StompSessionHandler> delegates =
Collections.synchronizedList(new ArrayList<StompSessionHandler>());
private volatile StompSession session;
@@ -188,41 +190,53 @@ public abstract class AbstractStompSessionManager implements StompSessionManager
if (this.session != null) {
delegate.afterConnected(this.session, this.connectedHeaders);
}
this.delegates.add(delegate);
synchronized (this.delegates) {
this.delegates.add(delegate);
}
}
void removeHandler(StompSessionHandler delegate) {
this.delegates.remove(delegate);
synchronized (this.delegates) {
this.delegates.remove(delegate);
}
}
@Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
this.session = session;
this.connectedHeaders = connectedHeaders;
for (StompSessionHandler delegate : this.delegates) {
delegate.afterConnected(session, connectedHeaders);
synchronized (this.delegates) {
for (StompSessionHandler delegate : this.delegates) {
delegate.afterConnected(session, connectedHeaders);
}
}
}
@Override
public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload,
Throwable exception) {
for (StompSessionHandler delegate : this.delegates) {
delegate.handleException(session, command, headers, payload, exception);
synchronized (this.delegates) {
for (StompSessionHandler delegate : this.delegates) {
delegate.handleException(session, command, headers, payload, exception);
}
}
}
@Override
public void handleTransportError(StompSession session, Throwable exception) {
for (StompSessionHandler delegate : this.delegates) {
delegate.handleTransportError(session, exception);
synchronized (this.delegates) {
for (StompSessionHandler delegate : this.delegates) {
delegate.handleTransportError(session, exception);
}
}
}
@Override
public void handleFrame(StompHeaders headers, Object payload) {
for (StompSessionHandler delegate : this.delegates) {
delegate.handleFrame(headers, payload);
synchronized (this.delegates) {
for (StompSessionHandler delegate : this.delegates) {
delegate.handleFrame(headers, payload);
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.stomp.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.stomp.support.StompHeaderMapper;
import org.springframework.util.StringUtils;
/**
* @author Artem Bilan
* @since 4.2
*/
abstract class StompAdapterParserUtils {
static void configureStompAdapter(BeanDefinitionBuilder builder, ParserContext parserContext, Element element) {
String stompSessionManager = element.getAttribute("stomp-session-manager");
if (!StringUtils.hasText(stompSessionManager)) {
parserContext.getReaderContext().error("The 'stomp-session-manager' is required", element);
}
builder.addConstructorArgReference(stompSessionManager);
String headerMapper = element.getAttribute("header-mapper");
String mappedHeaders = element.getAttribute("mapped-headers");
boolean hasMappedHeaders = StringUtils.hasText(mappedHeaders);
if (StringUtils.hasText(headerMapper)) {
if (hasMappedHeaders) {
parserContext.getReaderContext().error("The 'mapped-headers' " +
"attribute is not allowed when a 'header-mapper' has been specified.",
parserContext.extractSource(element));
}
builder.addPropertyReference("headerMapper", headerMapper);
}
else if (hasMappedHeaders) {
BeanDefinitionBuilder headerMapperBuilder =
BeanDefinitionBuilder.genericBeanDefinition(StompHeaderMapper.class);
// This is tricky a bit, but from one side the 'headerMapper' is an internal instance
// and isn't accessible outside and from other side allow us to avoid extra 'boolean' variable
headerMapperBuilder.addPropertyValue("inboundHeaderNames", mappedHeaders);
headerMapperBuilder.addPropertyValue("outboundHeaderNames", mappedHeaders);
builder.addPropertyValue("headerMapper", headerMapperBuilder.getBeanDefinition());
}
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.stomp.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.stomp.inbound.StompInboundChannelAdapter;
/**
* The {@link AbstractSingleBeanDefinitionParser} implementation for
* the {@code <stomp:inbound-channel-adapter/>} element.
*
* @author Artem Bilan
* @since 4.2
*/
public class StompInboundChannelAdapterParser extends AbstractChannelAdapterParser {
@Override
protected AbstractBeanDefinition doParse(Element element, ParserContext parserContext, String channelName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(StompInboundChannelAdapter.class);
StompAdapterParserUtils.configureStompAdapter(builder, parserContext, element);
builder.addConstructorArgValue(element.getAttribute("destinations"));
builder.addPropertyReference("outputChannel", channelName);
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "error-channel");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "send-timeout");
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "payload-type");
return builder.getBeanDefinition();
}
}

View File

@@ -25,7 +25,8 @@ import org.springframework.integration.config.xml.AbstractIntegrationNamespaceHa
public class StompNamespaceHandler extends AbstractIntegrationNamespaceHandler {
public void init() {
registerBeanDefinitionParser("inbound-channel-adapter", new StompInboundChannelAdapterParser());
registerBeanDefinitionParser("outbound-channel-adapter", new StompOutboundChannelAdapterParser());
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.stomp.config;
import org.w3c.dom.Element;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.integration.config.xml.AbstractOutboundChannelAdapterParser;
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
import org.springframework.integration.stomp.outbound.StompMessageHandler;
/**
* The {@link AbstractOutboundChannelAdapterParser} implementation for
* the {@code <stomp:outbound-channel-adapter/>} element.
*
* @author Artem Bilan
* @since 4.2
*/
public class StompOutboundChannelAdapterParser extends AbstractOutboundChannelAdapterParser {
@Override
protected AbstractBeanDefinition parseConsumer(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(StompMessageHandler.class);
StompAdapterParserUtils.configureStompAdapter(builder, parserContext, element);
BeanDefinition expressionDef =
IntegrationNamespaceUtils.createExpressionDefinitionFromValueOrExpression("destination",
"destination-expression", parserContext, element, false);
if (expressionDef != null) {
builder.addPropertyValue("destinationExpression", expressionDef);
}
return builder.getBeanDefinition();
}
}

View File

@@ -292,6 +292,11 @@ public class StompInboundChannelAdapter extends MessageProducerSupport implement
}
}
@Override
public void handleTransportError(StompSession session, Throwable exception) {
logger.error("STOMP transport error for session: [" + session + "]", exception);
}
}
}

View File

@@ -119,7 +119,7 @@ public class StompMessageHandler extends AbstractMessageHandler implements Appli
}
final StompSession.Receiptable receiptable = this.stompSession.send(stompHeaders, message.getPayload());
if (this.stompSessionManager.isAutoReceiptEnabled()) {
if (receiptable.getReceiptId() != null) {
final String destination = stompHeaders.getDestination();
if (this.applicationEventPublisher != null) {
receiptable.addReceiptTask(new Runnable() {
@@ -199,6 +199,11 @@ public class StompMessageHandler extends AbstractMessageHandler implements Appli
}
}
@Override
public void handleTransportError(StompSession session, Throwable exception) {
logger.error("STOMP transport error for session: [" + session + "]", exception);
}
}
}

View File

@@ -1,10 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.springframework.org/schema/integration/websocket"
<xsd:schema xmlns="http://www.springframework.org/schema/integration/stomp"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:tool="http://www.springframework.org/schema/tool"
xmlns:integration="http://www.springframework.org/schema/integration"
targetNamespace="http://www.springframework.org/schema/integration/websocket"
targetNamespace="http://www.springframework.org/schema/integration/stomp"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
@@ -19,5 +18,152 @@
]]></xsd:documentation>
</xsd:annotation>
<xsd:element name="inbound-channel-adapter">
<xsd:annotation>
<xsd:documentation>
Configures an endpoint that will receive STOMP Messages using the provided
'StompSessionManager'.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="stompAdapterType">
<xsd:attribute name="destinations">
<xsd:annotation>
<xsd:documentation>
Comma-separated list of STOMP destination names to subscribe.
The list of destinations (and therefore subscriptions) can be modified at runtime
through the 'addDestination()' and 'removeDestination()' '@ManagedOperation's.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="error-channel">
<xsd:annotation>
<xsd:documentation>
Message Channel to which error Messages should be sent.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type="org.springframework.messaging.MessageChannel"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="send-timeout">
<xsd:annotation>
<xsd:documentation>
Maximum amount of time in milliseconds to wait when sending a message
to the channel if such channel may block.
For example, a Queue Channel can block until space is available
if its maximum capacity has been reached.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="payload-type" default="java.lang.String">
<xsd:annotation>
<xsd:documentation source="java:java.lang.Class">
Fully qualified name of the java type for the target 'payload'
to convert from the incoming Stomp Message.
Defaults to 'java.lang.String'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:element name="outbound-channel-adapter">
<xsd:annotation>
<xsd:documentation>
Configures an endpoint that will send a STOMP Message to the provided
'StompSessionManager'.
</xsd:documentation>
</xsd:annotation>
<xsd:complexType>
<xsd:complexContent>
<xsd:extension base="stompAdapterType">
<xsd:choice minOccurs="0" maxOccurs="2">
<xsd:element ref="integration:poller" minOccurs="0" maxOccurs="1"/>
<xsd:element name="request-handler-advice-chain" type="integration:handlerAdviceChainType"
minOccurs="0" maxOccurs="1"/>
</xsd:choice>
<xsd:attribute name="destination">
<xsd:annotation>
<xsd:documentation>
Name of the destination to which STOMP Messages will be sent.
Mutually exclusive with the 'destination-expression'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="destination-expression" type="xsd:string">
<xsd:annotation>
<xsd:documentation>
A SpEL expression to be evaluated at runtime against each Spring Integration Message as
the root object.
Mutually exclusive with the 'destination'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="stompAdapterType">
<xsd:annotation>
<xsd:documentation>
Base type for the 'inbound-channel-adapter' and 'outbound-channel-adapter' elements.
</xsd:documentation>
</xsd:annotation>
<xsd:attribute name="stomp-session-manager" use="required">
<xsd:annotation>
<xsd:documentation>
The reference to the 'StompSessionManager' bean, which encapsulates the low-level
connection and StompSession handling operations. Required.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type
type="org.springframework.integration.stomp.StompSessionManager"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="header-mapper">
<xsd:annotation>
<xsd:documentation>
Reference to a bean implementing 'HeaderMapper' that maps Spring Integration MessageHeaders to/from
STOMP frame headers.
This is mutually exclusive with 'mapped-headers'.
Defaults to StompHeaderMapper.
</xsd:documentation>
<xsd:appinfo>
<tool:annotation kind="ref">
<tool:expected-type type=" org.springframework.integration.mapping.HeaderMapper"/>
</tool:annotation>
</xsd:appinfo>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="mapped-headers">
<xsd:annotation>
<xsd:documentation>
Comma-separated list of names of STOMP Headers to be mapped from/to the STOMP frame headers.
This can only be provided if the 'header-mapper' reference is not being set directly.
The values in this list can also be simple patterns to be matched against
the header names (e.g. "foo*" or "*foo").
Special tokens 'STOMP_INBOUND_HEADERS' and 'STOMP_OUTBOUND_HEADERS' represent
all the standard STOMP headers (content-length, receipt, heart-beat etc)
for the inbound and outbound channel adapters respectively;
they are included by default.
If you wish to add your own headers, you must also include these tokens if you wish the
standard headers to also be mapped or provide your own 'HeaderMapper'
implementation using 'header-mapper'.
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attributeGroup ref="integration:channelAdapterAttributes"/>
</xsd:complexType>
</xsd:schema>

View File

@@ -71,7 +71,6 @@ public class StompServerIntegrationTests {
int port = SocketUtils.findAvailableTcpPort(61613);
activeMQBroker = new BrokerService();
activeMQBroker.addConnector("stomp://127.0.0.1:" + port);
activeMQBroker.setStartAsync(false);
activeMQBroker.setPersistent(false);
activeMQBroker.setUseJmx(false);
activeMQBroker.getSystemUsage().getMemoryUsage().setLimit(1024 * 1024 * 5);

View File

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-stomp="http://www.springframework.org/schema/integration/stomp"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/stomp
http://www.springframework.org/schema/integration/stomp/spring-integration-stomp.xsd">
<bean id="stompSessionManager" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="org.springframework.integration.stomp.StompSessionManager"/>
</bean>
<bean id="stompHeaderMapper" class="org.springframework.integration.stomp.support.StompHeaderMapper"/>
<int-stomp:inbound-channel-adapter id="defaultInboundAdapter" stomp-session-manager="stompSessionManager"/>
<int-stomp:inbound-channel-adapter id="customInboundAdapter"
stomp-session-manager="stompSessionManager"
auto-startup="false"
payload-type="java.lang.Integer"
destinations="foo"
role="bar"
mapped-headers="foo, bar"
channel="inboundChannel"
error-channel="errorChannel"
send-timeout="2000"
phase="200"/>
<int:channel id="inboundChannel"/>
<!-- Invalid config -->
<!--<int-stomp:inbound-channel-adapter id="invalidInboundAdapter"
stomp-session-manager="stompSessionManager"
header-mapper="stompHeaderMapper"
mapped-headers="foo, bar"/>-->
<!-- Invalid config -->
<int-stomp:outbound-channel-adapter id="defaultOutboundAdapter" stomp-session-manager="stompSessionManager"/>
<int-stomp:outbound-channel-adapter id="customOutboundAdapter"
stomp-session-manager="stompSessionManager"
auto-startup="false"
phase="100"
role="foo"
destination="baz"
header-mapper="stompHeaderMapper"
channel="outboundChannel"/>
<int:channel id="outboundChannel"/>
<!-- Invalid config -->
<!--<int-stomp:outbound-channel-adapter id="invalidOutboundAdapter"
stomp-session-manager="stompSessionManager"
header-mapper="stompHeaderMapper"
mapped-headers="foo, bar"/>-->
<!-- Invalid config -->
</beans>

View File

@@ -0,0 +1,171 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.stomp.config;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.SmartLifecycle;
import org.springframework.integration.endpoint.AbstractEndpoint;
import org.springframework.integration.mapping.HeaderMapper;
import org.springframework.integration.stomp.StompSessionManager;
import org.springframework.integration.stomp.inbound.StompInboundChannelAdapter;
import org.springframework.integration.support.SmartLifecycleRoleController;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.MultiValueMap;
/**
* @author Artem Bilan
* @since 4.2
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class StompAdaptersParserTests {
@Autowired
private StompSessionManager stompSessionManager;
@Autowired
private HeaderMapper<?> headerMapper;
@Autowired
@Qualifier("defaultInboundAdapter")
private MessageChannel defaultInboundAdapterChannel;
@Autowired
private MessageChannel errorChannel;
@Autowired
private MessageChannel inboundChannel;
@Autowired
@Qualifier("defaultInboundAdapter.adapter")
private StompInboundChannelAdapter defaultInboundAdapter;
@Autowired
private StompInboundChannelAdapter customInboundAdapter;
@Autowired
@Qualifier("defaultOutboundAdapter")
private MessageChannel defaultOutboundAdapterChannel;
@Autowired
@Qualifier("defaultOutboundAdapter.handler")
private MessageHandler defaultOutboundAdapterHandler;
@Autowired
@Qualifier("defaultOutboundAdapter.adapter")
private AbstractEndpoint defaultOutboundAdapter;
@Autowired
private MessageChannel outboundChannel;
@Autowired
@Qualifier("customOutboundAdapter.handler")
private MessageHandler customOutboundAdapterHandler;
@Autowired
@Qualifier("customOutboundAdapter")
private AbstractEndpoint customOutboundAdapter;
@Autowired
private SmartLifecycleRoleController roleController;
@Test
public void testParsers() {
assertSame(this.defaultInboundAdapterChannel,
TestUtils.getPropertyValue(this.defaultInboundAdapter, "outputChannel"));
assertSame(this.stompSessionManager,
TestUtils.getPropertyValue(this.defaultInboundAdapter, "stompSessionManager"));
assertNull(TestUtils.getPropertyValue(this.defaultInboundAdapter, "errorChannel"));
Object headerMapper = TestUtils.getPropertyValue(this.defaultInboundAdapter, "headerMapper");
assertNotNull(headerMapper);
assertNotSame(this.headerMapper, headerMapper);
assertEquals(String.class, TestUtils.getPropertyValue(this.defaultInboundAdapter, "payloadType", Class.class));
assertTrue(TestUtils.getPropertyValue(this.defaultInboundAdapter, "autoStartup", Boolean.class));
assertSame(this.inboundChannel,
TestUtils.getPropertyValue(this.customInboundAdapter, "outputChannel"));
assertSame(this.stompSessionManager,
TestUtils.getPropertyValue(this.customInboundAdapter, "stompSessionManager"));
assertSame(this.errorChannel, TestUtils.getPropertyValue(this.customInboundAdapter, "errorChannel"));
assertEquals(Collections.singleton("foo"),
TestUtils.getPropertyValue(this.customInboundAdapter, "destinations"));
headerMapper = TestUtils.getPropertyValue(this.customInboundAdapter, "headerMapper");
assertNotNull(headerMapper);
assertNotSame(this.headerMapper, headerMapper);
assertArrayEquals(new String[] {"bar", "foo"},
TestUtils.getPropertyValue(headerMapper, "inboundHeaderNames", String[].class));
assertEquals(Integer.class, TestUtils.getPropertyValue(this.customInboundAdapter, "payloadType", Class.class));
assertFalse(TestUtils.getPropertyValue(this.customInboundAdapter, "autoStartup", Boolean.class));
assertEquals(200, TestUtils.getPropertyValue(this.customInboundAdapter, "phase"));
assertEquals(2000L, TestUtils.getPropertyValue(this.customInboundAdapter, "messagingTemplate.sendTimeout"));
assertSame(this.stompSessionManager,
TestUtils.getPropertyValue(this.defaultOutboundAdapterHandler, "stompSessionManager"));
headerMapper = TestUtils.getPropertyValue(this.defaultOutboundAdapterHandler, "headerMapper");
assertNotNull(headerMapper);
assertNotSame(this.headerMapper, headerMapper);
assertNull(TestUtils.getPropertyValue(this.defaultOutboundAdapterHandler, "destinationExpression"));
assertSame(this.defaultOutboundAdapterHandler,
TestUtils.getPropertyValue(this.defaultOutboundAdapter, "handler"));
assertSame(this.defaultOutboundAdapterChannel,
TestUtils.getPropertyValue(this.defaultOutboundAdapter, "inputChannel"));
assertTrue(TestUtils.getPropertyValue(this.defaultOutboundAdapter, "autoStartup", Boolean.class));
assertSame(this.stompSessionManager,
TestUtils.getPropertyValue(this.customOutboundAdapterHandler, "stompSessionManager"));
assertSame(this.headerMapper, TestUtils.getPropertyValue(this.customOutboundAdapterHandler, "headerMapper"));
assertEquals("baz",
TestUtils.getPropertyValue(this.customOutboundAdapterHandler, "destinationExpression.literalValue"));
assertSame(this.customOutboundAdapterHandler,
TestUtils.getPropertyValue(this.customOutboundAdapter, "handler"));
assertSame(this.outboundChannel, TestUtils.getPropertyValue(this.customOutboundAdapter, "inputChannel"));
assertFalse(TestUtils.getPropertyValue(this.customOutboundAdapter, "autoStartup", Boolean.class));
assertEquals(100, TestUtils.getPropertyValue(this.customOutboundAdapter, "phase"));
@SuppressWarnings("unchecked")
MultiValueMap<String, SmartLifecycle> lifecycles = (MultiValueMap<String, SmartLifecycle>)
TestUtils.getPropertyValue(this.roleController, "lifecycles", MultiValueMap.class);
assertTrue(lifecycles.containsKey("bar"));
List<SmartLifecycle> bars = lifecycles.get("bar");
bars.contains(this.customInboundAdapter);
assertTrue(lifecycles.containsKey("foo"));
List<SmartLifecycle> foos = lifecycles.get("bar");
bars.contains(this.customOutboundAdapter);
}
}

View File

@@ -4,5 +4,6 @@ log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %c{1} [%t] : %m%n
log4j.category.org.springframework.integration=WARN
log4j.category.org.springframework.integration.stomp=WARN
log4j.category.org.springframework.messaging=DEBUG
log4j.category.org.springframework.integration=DEBUG
#log4j.category.org.springframework.integration.stomp=WARN