Add ImmutableMessageChannelInterceptor

This change adds a ChannelInterceptor that flips the immutable flag on
messages being sent. This allows components sending messages to leave
the message mutable for interceptors to further apply modifications
before the message is sent (and exposed to concurrency).

The interceptor is automatically added with the STOMP/WebSocket Java
and XML config and the StompSubProtocolHandler leaves parsed incoming
messages mutable so they can be further modified before being sent.

Issue: SPR-12321
This commit is contained in:
Rossen Stoyanchev
2014-10-23 15:21:09 -04:00
parent d5bf6713ed
commit 687955a704
8 changed files with 192 additions and 27 deletions

View File

@@ -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<Object>();
if (element != null) {
Element interceptorsElement = DomUtils.getChildElementByTagName(element, "interceptors");
ManagedList<?> interceptors = WebSocketNamespaceUtils.parseBeanSubElements(interceptorsElement, context);
channelDef.getPropertyValues().add("interceptors", interceptors);
interceptors.addAll(WebSocketNamespaceUtils.parseBeanSubElements(interceptorsElement, context));
}
interceptors.add(new ImmutableMessageChannelInterceptor());
channelDef.getPropertyValues().add("interceptors", interceptors);
registerBeanDefByName(name, channelDef, context, source);
return new RuntimeBeanReference(name);
}

View File

@@ -45,6 +45,9 @@ import org.springframework.messaging.simp.stomp.StompEncoder;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.simp.user.DestinationUserNameProvider;
import org.springframework.messaging.simp.user.UserSessionRegistry;
import org.springframework.messaging.support.AbstractMessageChannel;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.ImmutableMessageChannelInterceptor;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.messaging.support.MessageHeaderInitializer;
@@ -99,6 +102,8 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
private MessageHeaderInitializer headerInitializer;
private Boolean immutableMessageInterceptorPresent;
private ApplicationEventPublisher eventPublisher;
private final Stats stats = new Stats();
@@ -234,7 +239,9 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
headerAccessor.setSessionId(session.getId());
headerAccessor.setSessionAttributes(session.getAttributes());
headerAccessor.setUser(session.getPrincipal());
headerAccessor.setImmutable();
if (!detectImmutableMessageInterceptor(outputChannel)) {
headerAccessor.setImmutable();
}
if (StompCommand.CONNECT.equals(headerAccessor.getCommand())) {
this.stats.incrementConnectCount();
@@ -271,6 +278,22 @@ public class StompSubProtocolHandler implements SubProtocolHandler, ApplicationE
}
}
private boolean detectImmutableMessageInterceptor(MessageChannel channel) {
if (this.immutableMessageInterceptorPresent != null) {
return this.immutableMessageInterceptorPresent;
}
if (channel instanceof AbstractMessageChannel) {
for (ChannelInterceptor interceptor : ((AbstractMessageChannel) channel).getInterceptors()) {
if (interceptor instanceof ImmutableMessageChannelInterceptor) {
this.immutableMessageInterceptorPresent = true;
return true;
}
}
}
this.immutableMessageInterceptorPresent = false;
return false;
}
private void publishEvent(ApplicationEvent event) {
try {
this.eventPublisher.publishEvent(event);