Add support for custom message converters
The Java and XML config for STOMP WebSocket applications now supports configuring message converters. Issue: SPR-11184
This commit is contained in:
@@ -153,7 +153,8 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser {
|
||||
RuntimeBeanReference brokerChannel = getMessageChannel(beanName, channelElem, parserCxt, source);
|
||||
registerMessageBroker(element, clientInChannel, clientOutChannel, brokerChannel, parserCxt, source);
|
||||
|
||||
RuntimeBeanReference messageConverter = registerBrokerMessageConverter(parserCxt, source);
|
||||
RuntimeBeanReference messageConverter = registerBrokerMessageConverter(element, parserCxt, source);
|
||||
|
||||
RuntimeBeanReference messagingTemplate = registerBrokerMessagingTemplate(element, brokerChannel,
|
||||
messageConverter, parserCxt, source);
|
||||
|
||||
@@ -339,24 +340,39 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
}
|
||||
|
||||
private RuntimeBeanReference registerBrokerMessageConverter(ParserContext parserCxt, Object source) {
|
||||
private RuntimeBeanReference registerBrokerMessageConverter(Element element,
|
||||
ParserContext parserCxt, Object source) {
|
||||
|
||||
Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");
|
||||
ManagedList<? super Object> convertersDef = new ManagedList<Object>();
|
||||
if (convertersElement != null) {
|
||||
convertersDef.setSource(source);
|
||||
for (Element beanElement : DomUtils.getChildElementsByTagName(convertersElement, "bean", "ref")) {
|
||||
Object object = parserCxt.getDelegate().parsePropertySubElement(beanElement, null);
|
||||
convertersDef.add(object);
|
||||
}
|
||||
}
|
||||
|
||||
if (convertersElement == null || Boolean.valueOf(convertersElement.getAttribute("register-defaults"))) {
|
||||
convertersDef.setSource(source);
|
||||
if (jackson2Present) {
|
||||
convertersDef.add(new RootBeanDefinition(MappingJackson2MessageConverter.class));
|
||||
}
|
||||
convertersDef.add(new RootBeanDefinition(StringMessageConverter.class));
|
||||
convertersDef.add(new RootBeanDefinition(ByteArrayMessageConverter.class));
|
||||
}
|
||||
|
||||
RootBeanDefinition contentTypeResolverDef = new RootBeanDefinition(DefaultContentTypeResolver.class);
|
||||
|
||||
ManagedList<RootBeanDefinition> convertersDef = new ManagedList<RootBeanDefinition>();
|
||||
if (jackson2Present) {
|
||||
convertersDef.add(new RootBeanDefinition(MappingJackson2MessageConverter.class));
|
||||
contentTypeResolverDef.getPropertyValues().add("defaultMimeType", MimeTypeUtils.APPLICATION_JSON);
|
||||
}
|
||||
convertersDef.add(new RootBeanDefinition(StringMessageConverter.class));
|
||||
convertersDef.add(new RootBeanDefinition(ByteArrayMessageConverter.class));
|
||||
|
||||
ConstructorArgumentValues cavs = new ConstructorArgumentValues();
|
||||
cavs.addIndexedArgumentValue(0, convertersDef);
|
||||
cavs.addIndexedArgumentValue(1, contentTypeResolverDef);
|
||||
|
||||
RootBeanDefinition brokerMessage = new RootBeanDefinition(CompositeMessageConverter.class, cavs, null);
|
||||
return new RuntimeBeanReference(registerBeanDef(brokerMessage,parserCxt, source));
|
||||
return new RuntimeBeanReference(registerBeanDef(brokerMessage, parserCxt, source));
|
||||
}
|
||||
|
||||
private RuntimeBeanReference registerBrokerMessagingTemplate(
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2002-2013 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.web.socket.config.annotation;
|
||||
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
import org.springframework.messaging.simp.config.ChannelRegistration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A convenient abstract base class for {@link WebSocketMessageBrokerConfigurer}
|
||||
* implementations providing empty method implementations for optional methods.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public abstract class AbstractWebSocketMessageBrokerConfigurer implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Override
|
||||
public void configureClientInboundChannel(ChannelRegistration registration) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureClientOutboundChannel(ChannelRegistration registration) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
import org.springframework.messaging.simp.config.ChannelRegistration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -71,6 +72,17 @@ public class DelegatingWebSocketMessageBrokerConfiguration extends WebSocketMess
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean configureMessageConverters(List<MessageConverter> messageConverters) {
|
||||
boolean registerDefaults = true;
|
||||
for (WebSocketMessageBrokerConfigurer c : this.configurers) {
|
||||
if (!c.configureMessageConverters(messageConverters)) {
|
||||
registerDefaults = false;
|
||||
}
|
||||
}
|
||||
return registerDefaults;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||
for (WebSocketMessageBrokerConfigurer c : this.configurers) {
|
||||
|
||||
@@ -34,12 +34,13 @@ import org.springframework.context.annotation.Import;
|
||||
* </pre>
|
||||
* <p>
|
||||
* Customize the imported configuration by implementing the
|
||||
* {@link WebSocketMessageBrokerConfigurer} interface:
|
||||
* {@link WebSocketMessageBrokerConfigurer} interface or more likely the convenient
|
||||
* base class {@link AbstractWebSocketMessageBrokerConfigurer}:
|
||||
*
|
||||
* <pre class="code">
|
||||
* @Configuration
|
||||
* @EnableWebSocketMessageBroker
|
||||
* public class MyConfiguration implements implements WebSocketMessageBrokerConfigurer {
|
||||
* public class MyConfiguration implements extends AbstractWebSocketMessageBrokerConfigurer {
|
||||
*
|
||||
* @Override
|
||||
* public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
|
||||
@@ -17,9 +17,12 @@
|
||||
package org.springframework.web.socket.config.annotation;
|
||||
|
||||
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
import org.springframework.messaging.simp.config.ChannelRegistration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Defines methods for configuring message handling with simple messaging
|
||||
* protocols (e.g. STOMP) from WebSocket clients. Typically used to customize
|
||||
@@ -52,6 +55,20 @@ public interface WebSocketMessageBrokerConfigurer {
|
||||
*/
|
||||
void configureClientOutboundChannel(ChannelRegistration registration);
|
||||
|
||||
/**
|
||||
* Configure the message converters to use when extracting the payload of
|
||||
* messages in annotated methods and when sending messages (e.g. through the
|
||||
* "broker" SimpMessagingTemplate).
|
||||
* <p>
|
||||
* The provided list, initially empty, can be used to add message converters
|
||||
* while the boolean return value is used to determine if default message should
|
||||
* be added as well.
|
||||
*
|
||||
* @param messageConverters initially an empty list of converters
|
||||
* @return whether to also add default converter or not
|
||||
*/
|
||||
boolean configureMessageConverters(List<MessageConverter> messageConverters);
|
||||
|
||||
/**
|
||||
* Configure message broker options.
|
||||
*/
|
||||
|
||||
@@ -481,6 +481,43 @@
|
||||
<xsd:element name="simple-broker" type="simple-broker" />
|
||||
<xsd:element name="stomp-broker-relay" type="stomp-broker-relay" />
|
||||
</xsd:choice>
|
||||
<xsd:element name="message-converters" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Configure the message converters to use when extracting the payload of messages in annotated methods
|
||||
and when sending messages (e.g. through the "broker" SimpMessagingTemplate.
|
||||
MessageConverter registrations provided here will take precedence over MessageConverter types registered by default.
|
||||
Also see the register-defaults attribute if you want to turn off default registrations entirely.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element ref="beans:bean">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A MessageConverter bean definition.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element ref="beans:ref">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
A reference to an HttpMessageConverter bean.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="register-defaults" type="xsd:boolean" default="true">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Whether or not default MessageConverter registrations should be added in addition to the ones provided within this element.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="client-inbound-channel" type="channel" minOccurs="0" maxOccurs="1">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.messaging.converter.CompositeMessageConverter;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
import org.springframework.messaging.converter.StringMessageConverter;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
|
||||
import org.springframework.messaging.simp.user.DefaultUserDestinationResolver;
|
||||
@@ -246,6 +247,29 @@ public class MessageBrokerBeanDefinitionParserTests {
|
||||
testExecutor("brokerChannel", 102, 202, 602);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void messageConverters() {
|
||||
loadBeanDefinitions("websocket-config-broker-converters.xml");
|
||||
|
||||
CompositeMessageConverter compositeConverter = this.appContext.getBean(CompositeMessageConverter.class);
|
||||
assertNotNull(compositeConverter);
|
||||
|
||||
assertEquals(4, compositeConverter.getConverters().size());
|
||||
assertEquals(StringMessageConverter.class, compositeConverter.getConverters().iterator().next().getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void messageConvertersDefaultsOff() {
|
||||
loadBeanDefinitions("websocket-config-broker-converters-defaults-off.xml");
|
||||
|
||||
CompositeMessageConverter compositeConverter = this.appContext.getBean(CompositeMessageConverter.class);
|
||||
assertNotNull(compositeConverter);
|
||||
|
||||
assertEquals(1, compositeConverter.getConverters().size());
|
||||
assertEquals(StringMessageConverter.class, compositeConverter.getConverters().iterator().next().getClass());
|
||||
}
|
||||
|
||||
|
||||
private void testChannel(String channelName, List<Class<? extends MessageHandler>> subscriberTypes,
|
||||
int interceptorCount) {
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ public class WebSocketMessageBrokerConfigurationSupportTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestSimpleMessageBrokerConfig implements WebSocketMessageBrokerConfigurer {
|
||||
static class TestSimpleMessageBrokerConfig extends AbstractWebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Bean
|
||||
public TestController subscriptionController() {
|
||||
@@ -130,19 +130,6 @@ public class WebSocketMessageBrokerConfigurationSupportTests {
|
||||
registry.addEndpoint("/simpleBroker");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureClientInboundChannel(ChannelRegistration registration) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureClientOutboundChannel(ChannelRegistration registration) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry registry) {
|
||||
// SimpleBroker used by default
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -47,6 +47,7 @@ import org.springframework.web.socket.JettyWebSocketTestServer;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.TomcatWebSocketTestServer;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
|
||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
|
||||
import org.springframework.web.socket.client.jetty.JettyWebSocketClient;
|
||||
@@ -205,7 +206,7 @@ public class SimpAnnotationMethodIntegrationTests extends AbstractWebSocketInteg
|
||||
@ComponentScan(basePackageClasses=SimpAnnotationMethodIntegrationTests.class,
|
||||
useDefaultFilters=false,
|
||||
includeFilters=@ComponentScan.Filter(IntegrationTestController.class))
|
||||
static class TestMessageBrokerConfigurer implements WebSocketMessageBrokerConfigurer {
|
||||
static class TestMessageBrokerConfigurer extends AbstractWebSocketMessageBrokerConfigurer {
|
||||
|
||||
@Autowired
|
||||
private HandshakeHandler handshakeHandler; // can't rely on classpath for server detection
|
||||
@@ -215,14 +216,6 @@ public class SimpAnnotationMethodIntegrationTests extends AbstractWebSocketInteg
|
||||
registry.addEndpoint("/ws").setHandshakeHandler(this.handshakeHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureClientInboundChannel(ChannelRegistration registration) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureClientOutboundChannel(ChannelRegistration registration) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureMessageBroker(MessageBrokerRegistry configurer) {
|
||||
configurer.setApplicationDestinationPrefixes("/app");
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:websocket="http://www.springframework.org/schema/websocket"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd">
|
||||
|
||||
<websocket:message-broker>
|
||||
|
||||
<websocket:stomp-endpoint path="/foo" />
|
||||
|
||||
<websocket:simple-broker />
|
||||
|
||||
<websocket:message-converters register-defaults="false">
|
||||
<bean class="org.springframework.messaging.converter.StringMessageConverter"/>
|
||||
</websocket:message-converters>
|
||||
|
||||
</websocket:message-broker>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,19 @@
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:websocket="http://www.springframework.org/schema/websocket"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd">
|
||||
|
||||
<websocket:message-broker>
|
||||
|
||||
<websocket:stomp-endpoint path="/foo" />
|
||||
|
||||
<websocket:simple-broker />
|
||||
|
||||
<websocket:message-converters>
|
||||
<bean class="org.springframework.messaging.converter.StringMessageConverter"/>
|
||||
</websocket:message-converters>
|
||||
|
||||
</websocket:message-broker>
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user