Merge branch '6.0.x'

This commit is contained in:
Sam Brannen
2023-06-04 16:27:23 +02:00
6 changed files with 131 additions and 40 deletions

View File

@@ -61,6 +61,7 @@ import org.springframework.util.StringUtils;
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @author Juergen Hoeller
* @author Sam Brannen
* @since 4.0
*/
public class DefaultSubscriptionRegistry extends AbstractSubscriptionRegistry {
@@ -121,11 +122,12 @@ public class DefaultSubscriptionRegistry extends AbstractSubscriptionRegistry {
/**
* Configure the name of a header that a subscription message can have for
* the purpose of filtering messages matched to the subscription. The header
* value is expected to be a Spring EL boolean expression to be applied to
* the headers of messages matched to the subscription.
* the purpose of filtering messages matched to the subscription.
* <p>The header value is expected to be a Spring Expression Language (SpEL)
* boolean expression to be applied to the headers of messages matched to the
* subscription.
* <p>For example:
* <pre>
* <pre style="code">
* headers.foo == 'bar'
* </pre>
* <p>By default this is set to "selector". You can set it to a different
@@ -138,8 +140,9 @@ public class DefaultSubscriptionRegistry extends AbstractSubscriptionRegistry {
}
/**
* Return the name for the selector header name.
* Return the name of the selector header.
* @since 4.2
* @see #setSelectorHeaderName(String)
*/
@Nullable
public String getSelectorHeaderName() {

View File

@@ -42,10 +42,11 @@ import org.springframework.util.PathMatcher;
/**
* A "simple" message broker that recognizes the message types defined in
* {@link SimpMessageType}, keeps track of subscriptions with the help of a
* {@link SubscriptionRegistry} and sends messages to subscribers.
* {@link SubscriptionRegistry}, and sends messages to subscribers.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @author Sam Brannen
* @since 4.0
*/
public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
@@ -97,11 +98,12 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
/**
* Configure a custom SubscriptionRegistry to use for storing subscriptions.
* <p><strong>Note</strong> that when a custom PathMatcher is configured via
* {@link #setPathMatcher}, if the custom registry is not an instance of
* {@link DefaultSubscriptionRegistry}, the provided PathMatcher is not used
* and must be configured directly on the custom registry.
* Configure a custom {@link SubscriptionRegistry} to use for storing subscriptions.
* <p><strong>NOTE</strong>: If the custom registry is not an instance of
* {@link DefaultSubscriptionRegistry}, the configured {@link #setPathMatcher
* PathMatcher}, {@linkplain #setCacheLimit cache limit}, and
* {@linkplain #setSelectorHeaderName selector header name} are not used and
* must be configured directly on the custom registry.
*/
public void setSubscriptionRegistry(SubscriptionRegistry subscriptionRegistry) {
Assert.notNull(subscriptionRegistry, "SubscriptionRegistry must not be null");
@@ -119,6 +121,8 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
* When configured, the given PathMatcher is passed down to the underlying
* SubscriptionRegistry to use for matching destination to subscriptions.
* <p>Default is a standard {@link org.springframework.util.AntPathMatcher}.
* <p>Setting this property has no effect if the underlying SubscriptionRegistry
* is not an instance of {@link DefaultSubscriptionRegistry}.
* @since 4.1
* @see #setSubscriptionRegistry
* @see DefaultSubscriptionRegistry#setPathMatcher
@@ -140,6 +144,8 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
* underlying SubscriptionRegistry, overriding any default there.
* <p>With a standard {@link DefaultSubscriptionRegistry}, the default
* cache limit is 1024.
* <p>Setting this property has no effect if the underlying SubscriptionRegistry
* is not an instance of {@link DefaultSubscriptionRegistry}.
* @since 4.3.2
* @see #setSubscriptionRegistry
* @see DefaultSubscriptionRegistry#setCacheLimit
@@ -158,15 +164,18 @@ public class SimpleBrokerMessageHandler extends AbstractBrokerMessageHandler {
/**
* Configure the name of a header that a subscription message can have for
* the purpose of filtering messages matched to the subscription. The header
* value is expected to be a Spring EL boolean expression to be applied to
* the headers of messages matched to the subscription.
* the purpose of filtering messages matched to the subscription.
* <p>The header value is expected to be a Spring Expression Language (SpEL)
* boolean expression to be applied to the headers of messages matched to the
* subscription.
* <p>For example:
* <pre>
* <pre style="code">
* headers.foo == 'bar'
* </pre>
* <p>By default this is set to "selector". You can set it to a different
* name, or to {@code null} to turn off support for a selector header.
* <p>Setting this property has no effect if the underlying SubscriptionRegistry
* is not an instance of {@link DefaultSubscriptionRegistry}.
* @param selectorHeaderName the name to use for a selector header
* @since 4.3.17
* @see #setSubscriptionRegistry

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2023 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.
@@ -26,6 +26,7 @@ import org.springframework.scheduling.TaskScheduler;
* Registration class for configuring a {@link SimpleBrokerMessageHandler}.
*
* @author Rossen Stoyanchev
* @author Sam Brannen
* @since 4.0
*/
public class SimpleBrokerRegistration extends AbstractBrokerRegistration {
@@ -81,11 +82,12 @@ public class SimpleBrokerRegistration extends AbstractBrokerRegistration {
/**
* Configure the name of a header that a subscription message can have for
* the purpose of filtering messages matched to the subscription. The header
* value is expected to be a Spring EL boolean expression to be applied to
* the headers of messages matched to the subscription.
* the purpose of filtering messages matched to the subscription.
* <p>The header value is expected to be a Spring Expression Language (SpEL)
* boolean expression to be applied to the headers of messages matched to the
* subscription.
* <p>For example:
* <pre>
* <pre style="code">
* headers.foo == 'bar'
* </pre>
* <p>By default this is set to "selector". You can set it to a different

View File

@@ -253,7 +253,7 @@ class DefaultSubscriptionRegistryTests {
}
@Test
void registerSubscriptionWithSelector() {
void registerSubscriptionWithSelectorHeaderEnabledByDefault() {
String sessionId1 = "sess01";
String sessionId2 = "sess02";
String sessionId3 = "sess03";
@@ -278,41 +278,70 @@ class DefaultSubscriptionRegistryTests {
accessor.setNativeHeader("foo", "bar");
Message<?> message = MessageBuilder.createMessage("", accessor.getMessageHeaders());
MultiValueMap<String, String> actual = this.registry.findSubscriptions(message);
assertThat(actual).hasSize(2);
MultiValueMap<String, String> subscriptions = this.registry.findSubscriptions(message);
assertThat(subscriptions).hasSize(2);
// Subscription #1 has a 'selector' header that DOES match.
assertThat(actual.get(sessionId1)).containsExactly(subscriptionId1);
assertThat(subscriptions.get(sessionId1)).containsExactly(subscriptionId1);
// Subscription #2 has a 'selector' header that does NOT match.
assertThat(actual.get(sessionId2)).isNull();
assertThat(subscriptions.get(sessionId2)).isNull();
// Subscription #3 does NOT have a 'selector' header, so it matches anyway.
assertThat(actual.get(sessionId3)).containsExactly(subscriptionId3);
assertThat(subscriptions.get(sessionId3)).containsExactly(subscriptionId3);
// Then try with message WITHOUT selected 'foo' header present
actual = this.registry.findSubscriptions(createMessage(destination));
subscriptions = this.registry.findSubscriptions(createMessage(destination));
assertThat(subscriptions).hasSize(1);
// Subscription #3 does NOT have a 'selector' header, so it matches anyway.
assertThat(actual.get(sessionId3)).containsExactly(subscriptionId3);
assertThat(subscriptions.get(sessionId3)).containsExactly(subscriptionId3);
}
@Test
void registerSubscriptionWithSelectorNotSupported() {
String sessionId = "sess01";
String subscriptionId = "subs01";
void registerSubscriptionWithSelectorHeaderDisabled() {
String sessionId1 = "sess01";
String sessionId2 = "sess02";
String sessionId3 = "sess03";
String subscriptionId1 = "subs01";
String subscriptionId2 = "subs02";
String subscriptionId3 = "subs02";
String destination = "/foo";
String selector = "headers.foo == 'bar'";
String selector1 = "headers.foo == 'bar'";
String selector2 = "headers.foo == 'enigma'";
// Explicitly disable selector header support
this.registry.setSelectorHeaderName(null);
this.registry.registerSubscription(subscribeMessage(sessionId, subscriptionId, destination, selector));
// Register subscription with matching selector header
this.registry.registerSubscription(subscribeMessage(sessionId1, subscriptionId1, destination, selector1));
// Register subscription with non-matching selector header
this.registry.registerSubscription(subscribeMessage(sessionId2, subscriptionId2, destination, selector2));
// Register subscription without a selector header
this.registry.registerSubscription(subscribeMessage(sessionId3, subscriptionId3, destination, null));
// First, try with message WITH selected 'foo' header present
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create();
accessor.setDestination(destination);
accessor.setNativeHeader("foo", "bazz");
accessor.setNativeHeader("foo", "bar");
Message<?> message = MessageBuilder.createMessage("", accessor.getMessageHeaders());
MultiValueMap<String, String> actual = this.registry.findSubscriptions(message);
assertThat(actual).hasSize(1);
assertThat(actual.get(sessionId)).containsExactly(subscriptionId);
MultiValueMap<String, String> subscriptions = this.registry.findSubscriptions(message);
// 'selector' header is ignored, so all 3 subscriptions should be found
assertThat(subscriptions).hasSize(3);
assertThat(subscriptions.get(sessionId1)).containsExactly(subscriptionId1);
assertThat(subscriptions.get(sessionId2)).containsExactly(subscriptionId2);
assertThat(subscriptions.get(sessionId3)).containsExactly(subscriptionId3);
// Then try with message WITHOUT selected 'foo' header present
subscriptions = this.registry.findSubscriptions(createMessage(destination));
// 'selector' header is ignored, so all 3 subscriptions should be found
assertThat(subscriptions).hasSize(3);
assertThat(subscriptions.get(sessionId1)).containsExactly(subscriptionId1);
assertThat(subscriptions.get(sessionId2)).containsExactly(subscriptionId2);
assertThat(subscriptions.get(sessionId3)).containsExactly(subscriptionId3);
}
@Test // SPR-11931