All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.messaging.simp.annotation.support.SendToMethodReturnValueHandler Maven / Gradle / Ivy

There is a newer version: 6.1.6
Show newest version
/*
 * Copyright 2002-2018 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
 *
 *      https://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.simp.annotation.support;

import java.lang.annotation.Annotation;
import java.security.Principal;
import java.util.Collections;
import java.util.Map;

import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.Nullable;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.handler.DestinationPatternsMessageCondition;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.handler.annotation.support.DestinationVariableMethodArgumentResolver;
import org.springframework.messaging.handler.invocation.HandlerMethodReturnValueHandler;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.messaging.simp.user.DestinationUserNameProvider;
import org.springframework.messaging.support.MessageHeaderInitializer;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.PropertyPlaceholderHelper;
import org.springframework.util.PropertyPlaceholderHelper.PlaceholderResolver;
import org.springframework.util.StringUtils;

/**
 * A {@link HandlerMethodReturnValueHandler} for sending to destinations specified in a
 * {@link SendTo} or {@link SendToUser} method-level annotations.
 *
 * 

The value returned from the method is converted, and turned to a {@link Message} and * sent through the provided {@link MessageChannel}. The message is then enriched with the * session id of the input message as well as the destination from the annotation(s). * If multiple destinations are specified, a copy of the message is sent to each destination. * * @author Rossen Stoyanchev * @author Sebastien Deleuze * @since 4.0 */ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueHandler { private final SimpMessageSendingOperations messagingTemplate; private final boolean annotationRequired; private String defaultDestinationPrefix = "/topic"; private String defaultUserDestinationPrefix = "/queue"; private PropertyPlaceholderHelper placeholderHelper = new PropertyPlaceholderHelper("{", "}", null, false); @Nullable private MessageHeaderInitializer headerInitializer; public SendToMethodReturnValueHandler(SimpMessageSendingOperations messagingTemplate, boolean annotationRequired) { Assert.notNull(messagingTemplate, "'messagingTemplate' must not be null"); this.messagingTemplate = messagingTemplate; this.annotationRequired = annotationRequired; } /** * Configure a default prefix to add to message destinations in cases where a method * is not annotated with {@link SendTo @SendTo} or does not specify any destinations * through the annotation's value attribute. *

By default, the prefix is set to "/topic". */ public void setDefaultDestinationPrefix(String defaultDestinationPrefix) { this.defaultDestinationPrefix = defaultDestinationPrefix; } /** * Return the configured default destination prefix. * @see #setDefaultDestinationPrefix(String) */ public String getDefaultDestinationPrefix() { return this.defaultDestinationPrefix; } /** * Configure a default prefix to add to message destinations in cases where a * method is annotated with {@link SendToUser @SendToUser} but does not specify * any destinations through the annotation's value attribute. *

By default, the prefix is set to "/queue". */ public void setDefaultUserDestinationPrefix(String prefix) { this.defaultUserDestinationPrefix = prefix; } /** * Return the configured default user destination prefix. * @see #setDefaultUserDestinationPrefix(String) */ public String getDefaultUserDestinationPrefix() { return this.defaultUserDestinationPrefix; } /** * Configure a {@link MessageHeaderInitializer} to apply to the headers of all * messages sent to the client outbound channel. *

By default this property is not set. */ public void setHeaderInitializer(@Nullable MessageHeaderInitializer headerInitializer) { this.headerInitializer = headerInitializer; } /** * Return the configured header initializer. */ @Nullable public MessageHeaderInitializer getHeaderInitializer() { return this.headerInitializer; } @Override public boolean supportsReturnType(MethodParameter returnType) { return (returnType.hasMethodAnnotation(SendTo.class) || AnnotatedElementUtils.hasAnnotation(returnType.getDeclaringClass(), SendTo.class) || returnType.hasMethodAnnotation(SendToUser.class) || AnnotatedElementUtils.hasAnnotation(returnType.getDeclaringClass(), SendToUser.class) || !this.annotationRequired); } @Override public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, Message message) throws Exception { if (returnValue == null) { return; } MessageHeaders headers = message.getHeaders(); String sessionId = SimpMessageHeaderAccessor.getSessionId(headers); DestinationHelper destinationHelper = getDestinationHelper(headers, returnType); SendToUser sendToUser = destinationHelper.getSendToUser(); if (sendToUser != null) { boolean broadcast = sendToUser.broadcast(); String user = getUserName(message, headers); if (user == null) { if (sessionId == null) { throw new MissingSessionUserException(message); } user = sessionId; broadcast = false; } String[] destinations = getTargetDestinations(sendToUser, message, this.defaultUserDestinationPrefix); for (String destination : destinations) { destination = destinationHelper.expandTemplateVars(destination); if (broadcast) { this.messagingTemplate.convertAndSendToUser( user, destination, returnValue, createHeaders(null, returnType)); } else { this.messagingTemplate.convertAndSendToUser( user, destination, returnValue, createHeaders(sessionId, returnType)); } } } SendTo sendTo = destinationHelper.getSendTo(); if (sendTo != null || sendToUser == null) { String[] destinations = getTargetDestinations(sendTo, message, this.defaultDestinationPrefix); for (String destination : destinations) { destination = destinationHelper.expandTemplateVars(destination); this.messagingTemplate.convertAndSend(destination, returnValue, createHeaders(sessionId, returnType)); } } } private DestinationHelper getDestinationHelper(MessageHeaders headers, MethodParameter returnType) { SendToUser m1 = AnnotatedElementUtils.findMergedAnnotation(returnType.getExecutable(), SendToUser.class); SendTo m2 = AnnotatedElementUtils.findMergedAnnotation(returnType.getExecutable(), SendTo.class); if ((m1 != null && !ObjectUtils.isEmpty(m1.value())) || (m2 != null && !ObjectUtils.isEmpty(m2.value()))) { return new DestinationHelper(headers, m1, m2); } SendToUser c1 = AnnotatedElementUtils.findMergedAnnotation(returnType.getDeclaringClass(), SendToUser.class); SendTo c2 = AnnotatedElementUtils.findMergedAnnotation(returnType.getDeclaringClass(), SendTo.class); if ((c1 != null && !ObjectUtils.isEmpty(c1.value())) || (c2 != null && !ObjectUtils.isEmpty(c2.value()))) { return new DestinationHelper(headers, c1, c2); } return (m1 != null || m2 != null ? new DestinationHelper(headers, m1, m2) : new DestinationHelper(headers, c1, c2)); } @Nullable protected String getUserName(Message message, MessageHeaders headers) { Principal principal = SimpMessageHeaderAccessor.getUser(headers); if (principal != null) { return (principal instanceof DestinationUserNameProvider ? ((DestinationUserNameProvider) principal).getDestinationUserName() : principal.getName()); } return null; } protected String[] getTargetDestinations(@Nullable Annotation annotation, Message message, String defaultPrefix) { if (annotation != null) { String[] value = (String[]) AnnotationUtils.getValue(annotation); if (!ObjectUtils.isEmpty(value)) { return value; } } String name = DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER; String destination = (String) message.getHeaders().get(name); if (!StringUtils.hasText(destination)) { throw new IllegalStateException("No lookup destination header in " + message); } return (destination.startsWith("/") ? new String[] {defaultPrefix + destination} : new String[] {defaultPrefix + '/' + destination}); } private MessageHeaders createHeaders(@Nullable String sessionId, MethodParameter returnType) { SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE); if (getHeaderInitializer() != null) { getHeaderInitializer().initHeaders(headerAccessor); } if (sessionId != null) { headerAccessor.setSessionId(sessionId); } headerAccessor.setHeader(SimpMessagingTemplate.CONVERSION_HINT_HEADER, returnType); headerAccessor.setLeaveMutable(true); return headerAccessor.getMessageHeaders(); } @Override public String toString() { return "SendToMethodReturnValueHandler [annotationRequired=" + this.annotationRequired + "]"; } private class DestinationHelper { private final PlaceholderResolver placeholderResolver; @Nullable private final SendTo sendTo; @Nullable private final SendToUser sendToUser; public DestinationHelper(MessageHeaders headers, @Nullable SendToUser sendToUser, @Nullable SendTo sendTo) { Map variables = getTemplateVariables(headers); this.placeholderResolver = variables::get; this.sendTo = sendTo; this.sendToUser = sendToUser; } @SuppressWarnings("unchecked") private Map getTemplateVariables(MessageHeaders headers) { String name = DestinationVariableMethodArgumentResolver.DESTINATION_TEMPLATE_VARIABLES_HEADER; return (Map) headers.getOrDefault(name, Collections.emptyMap()); } @Nullable public SendTo getSendTo() { return this.sendTo; } @Nullable public SendToUser getSendToUser() { return this.sendToUser; } public String expandTemplateVars(String destination) { return placeholderHelper.replacePlaceholders(destination, this.placeholderResolver); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy