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:
committed by
Gary Russell
parent
e82bfdc9a6
commit
d89dabe72f
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user