Spring WebSocket @SendToSession: envie mensagem para uma sessão específica

É possível enviar uma mensagem para uma sessão específica?

Eu tenho um websocket não autenticado entre clientes e um servlet Spring. Preciso enviar uma mensagem não solicitada para uma conexão específica quando um trabalho assíncrono termina.

@Controller
public class WebsocketTest {


     @Autowired
    public SimpMessageSendingOperations messagingTemplate;

    ExecutorService executor = Executors.newSingleThreadExecutor();

    @MessageMapping("/start")
    public void start(SimpMessageHeaderAccessor accessor) throws Exception {
        String applicantId=accessor.getSessionId();        
        executor.submit(() -> {
            //... slow job
            jobEnd(applicantId);
        });
    }

    public void jobEnd(String sessionId){
        messagingTemplate.convertAndSend("/queue/jobend"); //how to send only to that session?
    }
}

Como você pode ver neste código, o cliente pode iniciar um trabalho assíncrono e, quando terminar, precisa da mensagem final. Obviamente, preciso enviar uma mensagem apenas ao candidato e não transmitir a todos. Seria ótimo ter um@SendToSession anotação oumessagingTemplate.convertAndSendToSession método.

ATUALIZAR

Eu tentei isso:

messagingTemplate.convertAndSend("/queue/jobend", true, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, sessionId));

Mas isso é transmitido para todas as sessões, não apenas a especificada.

ATUALIZAÇÃO 2

Teste com o método convertAndSendToUser (). Este teste é um truque do tutorial oficial do Spring:https://spring.io/guides/gs/messaging-stomp-websocket/

Este é o código do servidor:

@Controller
public class WebsocketTest {

    @PostConstruct
    public void init(){
        ScheduledExecutorService statusTimerExecutor=Executors.newSingleThreadScheduledExecutor();
        statusTimerExecutor.scheduleAtFixedRate(new Runnable() {                
            @Override
            public void run() {
                messagingTemplate.convertAndSendToUser("1","/queue/test", new Return("test"));
            }
        }, 5000,5000, TimeUnit.MILLISECONDS);
    } 

     @Autowired
        public SimpMessageSendingOperations messagingTemplate;
}

e este é o código do cliente:

function connect() {
            var socket = new WebSocket('ws://localhost:8080/hello');
            stompClient = Stomp.over(socket);
            stompClient.connect({}, function(frame) {
                setConnected(true);
                console.log('Connected: ' + frame);
                stompClient.subscribe('/user/queue/test', function(greeting){
                    console.log(JSON.parse(greeting.body));
                });
            });
        }

Infelizmente, o cliente não recebe sua resposta por sessão a cada 5000ms, conforme o esperado. Tenho certeza de que "1" é um sessionId válido para o segundo cliente conectado porque o vejo no modo de depuração comSimpMessageHeaderAccessor.getSessionId()

CENÁRIO DE FUNDO

Desejo criar uma barra de progresso para um trabalho remoto, o cliente solicita ao servidor um trabalho assíncrono e verifica seu progresso por mensagem do websocket enviada do servidor. Este não é um upload de arquivo, mas uma computação remota; portanto, apenas o servidor sabe o andamento de cada trabalho. Preciso enviar uma mensagem para uma sessão específica porque cada trabalho é iniciado por sessão. O cliente solicita uma computação remota O servidor inicia esse trabalho e, para cada etapa do trabalho, responde ao cliente solicitante com seu status de progresso do trabalho. O cliente recebe mensagens sobre seu trabalho e cria uma barra de progresso / status. É por isso que preciso de mensagens por sessão. Eu também poderia usar mensagens por usuário, mas o Springnão fornece por usuário mensagens não solicitadas. (Não é possível enviar mensagem do usuário com o Spring Websocket)

SOLUÇÃO DE TRABALHO

 __      __ ___   ___  _  __ ___  _  _   ___      ___   ___   _    _   _  _____  ___  ___   _  _ 
 \ \    / // _ \ | _ \| |/ /|_ _|| \| | / __|    / __| / _ \ | |  | | | ||_   _||_ _|/ _ \ | \| |
  \ \/\/ /| (_) ||   /| ' <  | | | .` || (_ |    \__ \| (_) || |__| |_| |  | |   | || (_) || .` |
   \_/\_/  \___/ |_|_\|_|\_\|___||_|\_| \___|    |___/ \___/ |____|\___/   |_|  |___|\___/ |_|\_|

A partir da solução UPDATE2, tive que concluir o método convertAndSendToUser com o último parâmetro (MessageHeaders):

messagingTemplate.convertAndSendToUser("1","/queue/test", new Return("test"), createHeaders("1"));

OndecreateHeaders() é este método:

private MessageHeaders createHeaders(String sessionId) {
        SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
        headerAccessor.setSessionId(sessionId);
        headerAccessor.setLeaveMutable(true);
        return headerAccessor.getMessageHeaders();
    }

questionAnswers(3)

yourAnswerToTheQuestion