webSocket com java e angular

Estou tentando criar este webSocket para buscar a quantidade de registros de uma tabela; Assim sempre atualizando no frontend quando for atualizado no back.

Fiz assim:

angular

import { Stomp } from '@stomp/stompjs';
import * as SockJS from 'sockjs-client';
webSocketServer(): void {
    const socket = new SockJS(
      'http://localhost:8500/modulo-cotacao-api/websocket/buscar-mensagens'
    );
    this.stompClient = Stomp.over(socket);
    const _this = this;
    this.stompClient.connect({}, function (frame) {
      _this.setConnected(true);
      console.log('Connected: ' + frame);
      _this.stompClient.subscribe('/buscar/qtd', function (hello) {
        _this.showGreeting(JSON.parse(hello.body).greeting);
      });
    });
  }

  showGreeting(message) {
    this.greetings.push(message);
  }

  setConnected(connected: boolean) {
    this.disabled = !connected;
    if (connected) {
      this.greetings = [];
    }
  }

Backend

Config

package br.com.ghnetsoft.comprasfood.cotacao.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

	@Override
	public void configureMessageBroker(MessageBrokerRegistry config) {
		config.enableSimpleBroker("/topic");
		config.setApplicationDestinationPrefixes("/app");
	}

	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		registry.addEndpoint("/websocket/buscar-mensagens").setAllowedOrigins("http://localhost:4100").withSockJS();
	}
}

controller Entendi que aqui tenho que chamar um repository que faça a query do contador.

package br.com.ghnetsoft.comprasfood.cotacao.resource;

import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

@Controller
public class WebSocketResource {

	@MessageMapping("/hello")
	@SendTo("/buscar/qtd")
	public Integer buscarQuantidadeMensagens(@Payload String message) {
		return 1;
	}
}

No log mostra isso

2021-06-10 17:18:00.703  INFO 177004 --- [MessageBroker-1] o.s.w.s.c.WebSocketMessageBrokerStats    : WebSocketSession[0 current WS(0)-HttpStream(0)-HttpPoll(0), 0 total, 0 closed abnormally (0 connect failure, 0 send limit, 0 transport error)], stompSubProtocol[processed CONNECT(0)-CONNECTED(0)-DISCONNECT(0)], stompBrokerRelay[null], inboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], outboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], sockJsScheduler[pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 0]
2021-06-10 17:48:00.716  INFO 177004 --- [MessageBroker-1] o.s.w.s.c.WebSocketMessageBrokerStats    : WebSocketSession[0 current WS(0)-HttpStream(0)-HttpPoll(0), 0 total, 0 closed abnormally (0 connect failure, 0 send limit, 0 transport error)], stompSubProtocol[processed CONNECT(0)-CONNECTED(0)-DISCONNECT(0)], stompBrokerRelay[null], inboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], outboundChannel[pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0], sockJsScheduler[pool size = 2, active threads = 1, queued tasks = 0, completed tasks = 1]

Quando chamo assim no postman: http://localhost:8500/modulo-cotacao-api/api/websocket/buscar-mensagens/buscar/qtd/7/, mostra no log:

2021-06-10 18:27:57.716 WARN 136272 — [nio-8500-exec-3] o.s.w.s.s.t.h.DefaultSockJsService : Origin check enabled but transport ‘7’ does not support it.

Quando chamo assim no postman: http://localhost:8500/modulo-cotacao-api/api/websocket/buscar-mensagens/buscar/qtd

2021-06-10 18:28:43.778 WARN 136272 — [nio-8500-exec-4] o.s.w.s.s.t.h.DefaultSockJsService : Invalid SockJS path ‘/buscar/qtd’ - required to have 3 path segments

Quero fazer isto:
image

Segui este tutorial: https://www.stackextend.com/angular/websocket-with-spring-boot-and-angular/
Mas não funciona.

O que pode ser ?

Vi algumas diferenças em relação ao tutorial seguindo. Talvez vc possa tentar fazer exatamente ao tutorial para ver se funciona.

Os pontes que vi foram:

  • Na classe WebSocketConfig, vc implementou WebSocketMessageBrokerConfigurer, e no tutorial é AbstractWebSocketMessageBrokerConfigurer (se bem que pode ser uma questão de versão da lib usada).

  • O controller WebSocketResource está como @Controller, e no tutorial @RestController (apesar de estar diferente, acredito que não vá incluenciar no websocket, mas como vc está usando angular, acredito que deveria ser @RestController mesmo)

  • Ainda no controller, o envio da mensagem para o socket no tutorial é feito via SimpMessagingTemplate, e no seu controller é através da anotação @MessageMapping (talvez seja interessante tentar fazer igual ao tutorial para ver se funciona).

Na versão do spring a classe AbstractWebSocketMessageBrokerConfigurer está Deprecated.

Mas mudei para ela.

1 curtida

Mudei para @RestController

Ficando assim:

package br.com.ghnetsoft.comprasfood.cotacao.resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import br.com.ghnetsoft.comprasfood.cotacao.service.cotacaoitemfornecedorusuario.CotacaoItemFornecedorUsuarioService;

@RestController
public class WebSocketResource {

	@Autowired
	private CotacaoItemFornecedorUsuarioService service;
	@Autowired
	private SimpMessagingTemplate template;

	@GetMapping("buscar-qtd/")
	public Long buscarQuantidadeMensagens() {
		var contador = service.contadorCotacaoItemFornecedorUsuario();
		template.convertAndSend("/topic/notification", contador);
		return contador;
	}
}

Está também.

Vou colocar os códigos alterados

WebSocketConfig

package br.com.ghnetsoft.comprasfood.cotacao.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

	@Override
	public void configureMessageBroker(MessageBrokerRegistry config) {
		config.enableSimpleBroker("/cotacao");
		config.setApplicationDestinationPrefixes("/app");
	}

	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		registry.addEndpoint("ws").setAllowedOrigins("*").withSockJS();
	}
}

WebSocketResource

package br.com.ghnetsoft.comprasfood.cotacao.resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import br.com.ghnetsoft.comprasfood.cotacao.service.cotacaoitemfornecedorusuario.CotacaoItemFornecedorUsuarioService;

@RestController
public class WebSocketResource {

	@Autowired
	private CotacaoItemFornecedorUsuarioService service;
	@Autowired
	private SimpMessagingTemplate template;

	@GetMapping("buscar-qtd/")
	public Long buscarQuantidadeMensagens() {
		var contador = service.contadorCotacaoItemFornecedorUsuario();
		template.convertAndSend("/cotacao/qtd", contador);
		return contador;
	}
}

Angular

WsService.ts

import { Injectable } from '@angular/core';
import { Stomp } from '@stomp/stompjs';
import * as SockJs from 'sockjs-client';
@Injectable()
export class WebSocketService {
  public connect() {
    const socket = new SockJs('http://localhost:8500/modulo-cotacao-api/ws/');
    let stompClient = Stomp.over(socket);
    return stompClient;
  }
}

collapsable.component.ts

No ngOnInit() chamo o metodo this.webSocketServer();

webSocketServer(): void {
    let stompClient = this.webSocketService.connect();
    stompClient.connect({}, () => {
      stompClient.subscribe('/buscar-qtd/', (notifications: any) => {
        this.notifications = JSON.parse(notifications.body).count;
      });
    });
  }

O que falta ?
O que estou errando ?

O que falta ?
O que estou errando ?