public void updateAfterNewSubscription(String destination, String sessionId, String subsId) { synchronized (this.updateCache) { this.updateCache.forEach((cachedDestination, subscriptions) -> { if (getPathMatcher().match(destination, cachedDestination)) { // Subscription id's may also be populated via getSubscriptions() List<String> subsForSession = subscriptions.get(sessionId); if (subsForSession == null || !subsForSession.contains(subsId)) { subscriptions.add(sessionId, subsId); this.accessCache.put(cachedDestination, subscriptions.deepCopy()); } } }); } }
@Override protected boolean removeEldestEntry(Map.Entry<String, LinkedMultiValueMap<String, String>> eldest) { if (size() > getCacheLimit()) { accessCache.remove(eldest.getKey()); return true; } else { return false; } } };
@Nullable private Expression getSelectorExpression(MessageHeaders headers) { Expression expression = null; if (getSelectorHeaderName() != null) { String selector = SimpMessageHeaderAccessor.getFirstNativeHeader(getSelectorHeaderName(), headers); if (selector != null) { try { expression = this.expressionParser.parseExpression(selector); this.selectorHeaderInUse = true; if (logger.isTraceEnabled()) { logger.trace("Subscription selector: [" + selector + "]"); } } catch (Throwable ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to parse selector: " + selector, ex); } } } } return expression; }
@Test // SPR-13555 public void cacheLimitExceeded() throws Exception { this.registry.setCacheLimit(1); this.registry.registerSubscription(subscribeMessage("sess1", "1", "/foo")); this.registry.registerSubscription(subscribeMessage("sess1", "2", "/bar")); assertEquals(1, this.registry.findSubscriptions(createMessage("/foo")).size()); assertEquals(1, this.registry.findSubscriptions(createMessage("/bar")).size()); this.registry.registerSubscription(subscribeMessage("sess2", "1", "/foo")); this.registry.registerSubscription(subscribeMessage("sess2", "2", "/bar")); assertEquals(2, this.registry.findSubscriptions(createMessage("/foo")).size()); assertEquals(2, this.registry.findSubscriptions(createMessage("/bar")).size()); }
private void initSelectorHeaderNameToUse() { if (this.subscriptionRegistry instanceof DefaultSubscriptionRegistry) { ((DefaultSubscriptionRegistry) this.subscriptionRegistry).setSelectorHeaderName(this.selectorHeaderName); } }
private void initCacheLimitToUse() { if (this.cacheLimit != null && this.subscriptionRegistry instanceof DefaultSubscriptionRegistry) { ((DefaultSubscriptionRegistry) this.subscriptionRegistry).setCacheLimit(this.cacheLimit); } }
@Test public void registerSubscriptionWithSelectorNotSupported() { String sessionId = "sess01"; String subscriptionId = "subs01"; String destination = "/foo"; String selector = "headers.foo == 'bar'"; this.registry.setSelectorHeaderName(null); this.registry.registerSubscription(subscribeMessage(sessionId, subscriptionId, destination, selector)); SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(); accessor.setDestination(destination); accessor.setNativeHeader("foo", "bazz"); Message<?> message = MessageBuilder.createMessage("", accessor.getMessageHeaders()); MultiValueMap<String, String> actual = this.registry.findSubscriptions(message); assertNotNull(actual); assertEquals(1, actual.size()); assertEquals(Collections.singletonList(subscriptionId), actual.get(sessionId)); }
@Override protected MultiValueMap<String, String> findSubscriptionsInternal(String destination, Message<?> message) { MultiValueMap<String, String> result = this.destinationCache.getSubscriptions(destination, message); return filterSubscriptions(result, message); }
@Override protected void addSubscriptionInternal( String sessionId, String subsId, String destination, Message<?> message) { Expression expression = getSelectorExpression(message.getHeaders()); this.subscriptionRegistry.addSubscription(sessionId, subsId, destination, expression); this.destinationCache.updateAfterNewSubscription(destination, sessionId, subsId); }
private void initPathMatcherToUse() { if (this.pathMatcher != null && this.subscriptionRegistry instanceof DefaultSubscriptionRegistry) { ((DefaultSubscriptionRegistry) this.subscriptionRegistry).setPathMatcher(this.pathMatcher); } }
/** * Create a SimpleBrokerMessageHandler instance with the given message channels * and destination prefixes. * @param clientInboundChannel the channel for receiving messages from clients (e.g. WebSocket clients) * @param clientOutboundChannel the channel for sending messages to clients (e.g. WebSocket clients) * @param brokerChannel the channel for the application to send messages to the broker * @param destinationPrefixes prefixes to use to filter out messages */ public SimpleBrokerMessageHandler(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel, SubscribableChannel brokerChannel, Collection<String> destinationPrefixes) { super(clientInboundChannel, clientOutboundChannel, brokerChannel, destinationPrefixes); this.subscriptionRegistry = new DefaultSubscriptionRegistry(); }
private void initSelectorHeaderNameToUse() { if (this.subscriptionRegistry instanceof DefaultSubscriptionRegistry) { ((DefaultSubscriptionRegistry) this.subscriptionRegistry).setSelectorHeaderName(this.selectorHeaderName); } }
private void initCacheLimitToUse() { if (this.cacheLimit != null && this.subscriptionRegistry instanceof DefaultSubscriptionRegistry) { ((DefaultSubscriptionRegistry) this.subscriptionRegistry).setCacheLimit(this.cacheLimit); } }
@Override protected MultiValueMap<String, String> findSubscriptionsInternal(String destination, Message<?> message) { MultiValueMap<String, String> result = this.destinationCache.getSubscriptions(destination, message); return filterSubscriptions(result, message); }
@Override protected void addSubscriptionInternal( String sessionId, String subsId, String destination, Message<?> message) { Expression expression = getSelectorExpression(message.getHeaders()); this.subscriptionRegistry.addSubscription(sessionId, subsId, destination, expression); this.destinationCache.updateAfterNewSubscription(destination, sessionId, subsId); }
private void initPathMatcherToUse() { if (this.pathMatcher != null && this.subscriptionRegistry instanceof DefaultSubscriptionRegistry) { ((DefaultSubscriptionRegistry) this.subscriptionRegistry).setPathMatcher(this.pathMatcher); } }
/** * Create a SimpleBrokerMessageHandler instance with the given message channels * and destination prefixes. * @param clientInboundChannel the channel for receiving messages from clients (e.g. WebSocket clients) * @param clientOutboundChannel the channel for sending messages to clients (e.g. WebSocket clients) * @param brokerChannel the channel for the application to send messages to the broker * @param destinationPrefixes prefixes to use to filter out messages */ public SimpleBrokerMessageHandler(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel, SubscribableChannel brokerChannel, Collection<String> destinationPrefixes) { super(clientInboundChannel, clientOutboundChannel, brokerChannel, destinationPrefixes); this.subscriptionRegistry = new DefaultSubscriptionRegistry(); }
public LinkedMultiValueMap<String, String> getSubscriptions(String destination, Message<?> message) { LinkedMultiValueMap<String, String> result = this.accessCache.get(destination); if (result == null) { synchronized (this.updateCache) { result = new LinkedMultiValueMap<>(); for (SessionSubscriptionInfo info : subscriptionRegistry.getAllSubscriptions()) { for (String destinationPattern : info.getDestinations()) { if (getPathMatcher().match(destinationPattern, destination)) { for (Subscription sub : info.getSubscriptions(destinationPattern)) { result.add(info.sessionId, sub.getId()); } } } } if (!result.isEmpty()) { this.updateCache.put(destination, result.deepCopy()); this.accessCache.put(destination, result); } } } return result; }
@Test public void customCacheLimit() { ApplicationContext context = loadConfig(CustomConfig.class); SimpleBrokerMessageHandler broker = context.getBean(SimpleBrokerMessageHandler.class); DefaultSubscriptionRegistry registry = (DefaultSubscriptionRegistry) broker.getSubscriptionRegistry(); assertEquals(8192, registry.getCacheLimit()); }
assertEquals(Arrays.asList("/topic", "/queue"), new ArrayList<>(prefixes)); DefaultSubscriptionRegistry registry = (DefaultSubscriptionRegistry) brokerMessageHandler.getSubscriptionRegistry(); assertEquals("my-selector", registry.getSelectorHeaderName()); assertNotNull(brokerMessageHandler.getTaskScheduler()); assertArrayEquals(new long[] {15000, 15000}, brokerMessageHandler.getHeartbeatValue());