diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java
index 4c738bdbd4b..d40b6e4a40a 100644
--- a/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java
+++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/config/AbstractMessageBrokerConfiguration.java
@@ -45,6 +45,7 @@ import org.springframework.messaging.simp.user.UserDestinationResolver;
import org.springframework.messaging.simp.user.UserSessionRegistry;
import org.springframework.messaging.support.AbstractSubscribableChannel;
import org.springframework.messaging.support.ExecutorSubscribableChannel;
+import org.springframework.messaging.support.ImmutableMessageChannelInterceptor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.ClassUtils;
import org.springframework.util.MimeTypeUtils;
@@ -118,6 +119,7 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
if (this.clientInboundChannelRegistration == null) {
ChannelRegistration registration = new ChannelRegistration();
configureClientInboundChannel(registration);
+ registration.setInterceptors(new ImmutableMessageChannelInterceptor());
this.clientInboundChannelRegistration = registration;
}
return this.clientInboundChannelRegistration;
@@ -152,6 +154,7 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
if (this.clientOutboundChannelRegistration == null) {
ChannelRegistration registration = new ChannelRegistration();
configureClientOutboundChannel(registration);
+ registration.setInterceptors(new ImmutableMessageChannelInterceptor());
this.clientOutboundChannelRegistration = registration;
}
return this.clientOutboundChannelRegistration;
@@ -169,6 +172,7 @@ public abstract class AbstractMessageBrokerConfiguration implements ApplicationC
ChannelRegistration reg = getBrokerRegistry().getBrokerChannelRegistration();
ExecutorSubscribableChannel channel = reg.hasTaskExecutor() ?
new ExecutorSubscribableChannel(brokerChannelExecutor()) : new ExecutorSubscribableChannel();
+ reg.setInterceptors(new ImmutableMessageChannelInterceptor());
channel.setInterceptors(reg.getInterceptors());
return channel;
}
diff --git a/spring-messaging/src/main/java/org/springframework/messaging/support/ImmutableMessageChannelInterceptor.java b/spring-messaging/src/main/java/org/springframework/messaging/support/ImmutableMessageChannelInterceptor.java
new file mode 100644
index 00000000000..c2921857fd6
--- /dev/null
+++ b/spring-messaging/src/main/java/org/springframework/messaging/support/ImmutableMessageChannelInterceptor.java
@@ -0,0 +1,45 @@
+/*
+ * 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
+ *
+ * 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.messaging.support;
+
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageChannel;
+
+/**
+ * A simpler interceptor that calls {@link MessageHeaderAccessor#setImmutable()}
+ * on the headers of messages passed through the preSend method.
+ *
+ *
When configured as the last interceptor in a chain, it allows the component
+ * sending the message to leave headers mutable for interceptors to modify prior
+ * to the message actually being sent and exposed to concurrent access.
+ *
+ * @author Rossen Stoyanchev
+ * @since 4.1.2
+ */
+public class ImmutableMessageChannelInterceptor extends ChannelInterceptorAdapter {
+
+
+ @Override
+ public Message> preSend(Message> message, MessageChannel channel) {
+ MessageHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, MessageHeaderAccessor.class);
+ if (accessor != null && accessor.isMutable()) {
+ accessor.setImmutable();
+ }
+ return message;
+ }
+
+}
diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/config/MessageBrokerConfigurationTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/config/MessageBrokerConfigurationTests.java
index 5e2aa0b586b..bd45517c774 100644
--- a/spring-messaging/src/test/java/org/springframework/messaging/simp/config/MessageBrokerConfigurationTests.java
+++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/config/MessageBrokerConfigurationTests.java
@@ -111,7 +111,7 @@ public class MessageBrokerConfigurationTests {
AbstractSubscribableChannel channel = this.customContext.getBean(
"clientInboundChannel", AbstractSubscribableChannel.class);
- assertEquals(2, channel.getInterceptors().size());
+ assertEquals(3, channel.getInterceptors().size());
CustomThreadPoolTaskExecutor taskExecutor = this.customContext.getBean(
"clientInboundChannelExecutor", CustomThreadPoolTaskExecutor.class);
@@ -178,7 +178,7 @@ public class MessageBrokerConfigurationTests {
AbstractSubscribableChannel channel = this.customContext.getBean(
"clientOutboundChannel", AbstractSubscribableChannel.class);
- assertEquals(2, channel.getInterceptors().size());
+ assertEquals(3, channel.getInterceptors().size());
ThreadPoolTaskExecutor taskExecutor = this.customContext.getBean(
"clientOutboundChannelExecutor", ThreadPoolTaskExecutor.class);
@@ -257,7 +257,7 @@ public class MessageBrokerConfigurationTests {
AbstractSubscribableChannel channel = this.customContext.getBean(
"brokerChannel", AbstractSubscribableChannel.class);
- assertEquals(3, channel.getInterceptors().size());
+ assertEquals(4, channel.getInterceptors().size());
ThreadPoolTaskExecutor taskExecutor = this.customContext.getBean(
"brokerChannelExecutor", ThreadPoolTaskExecutor.class);
diff --git a/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java b/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java
index 22b55cdf562..0c06e0be850 100644
--- a/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java
+++ b/spring-websocket/src/main/java/org/springframework/web/socket/config/MessageBrokerBeanDefinitionParser.java
@@ -21,6 +21,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
+import org.springframework.messaging.support.ImmutableMessageChannelInterceptor;
import org.w3c.dom.Element;
import org.springframework.beans.MutablePropertyValues;
@@ -201,11 +202,14 @@ class MessageBrokerBeanDefinitionParser implements BeanDefinitionParser {
argValues.addIndexedArgumentValue(0, new RuntimeBeanReference(executorName));
}
RootBeanDefinition channelDef = new RootBeanDefinition(ExecutorSubscribableChannel.class, argValues, null);
+ ManagedList super Object> interceptors = new ManagedList