Spring Boot WebSockets #EventListener doesn't detect SessionConnectEvent but detect SessionDisconnectEvent at the same class - spring-boot

I try to create simple chat via WebSockets. I have configuration class which register appropriate endpoints and I try to create service which will save user id which is generated in handshake service. The problem is that I cannot inject any services to the handshake service so I decided to create class as below:
#Slf4j
#RequiredArgsConstructor
public class WebSocketEventListener {
#EventListener
public void handleWebSocketConnectListener(SessionConnectEvent event) {
log.debug("Handled connection event");
}
#EventListener
public void handleWebSocketConnectedListener(SessionConnectedEvent event) {
log.debug("Handled connected event");
}
#EventListener
public void handleWebSocketDisconnectListener(SessionDisconnectEvent event) {
log.debug("Handled disconnection event");
}
}
Register it as a Bean in configuration class:
#Bean
public WebsocketEventListener websocketEventListener() {
return new WebsocketEventListener();
}
And there try to save user id into database.
Unfortunately when I connect to the webSocket via android app, Spring doesn't detect any event connected with Connection session but when I close the connection between android app and spring server I'm getting log from handleWebSocketDisconnectListener method.
I also tried to add annotation #Component to the class WebSocketEventListener instead of registering it as a Bean but I got the same situation.
I tried to implement ApplicationListener and register it in META-INF folder but also without any results.
P.S. I use Spring Boot version: 2.6.4 and Java 17
I don't know what I'm doing wrong. Any suggestions?

Related

websocket spring boot setup

I have a spring boot application. I am trying to add the websocket piece to it. The problem is my angular client can't connect to it. I used smart websocket client google plugin, but still not able to connect. Here is the setup.
I am using Intellij Idea on localhost. the spring boot application is running on localhost:8080. I can see the WebSocketSession is runnign from intellij idea console.
Here is the setup:
#Slf4j
#RestController
public class WebsocketController {
#MessageMapping("/ws-on/hello")
#SendTo("/ws-on/greetings")
public UserStateIndicator greeting(UserStateIndicator indicator) throws Exception {
Thread.sleep(1000); // simulated delay
log.debug("websocket " + indicator.toString());
return indicator;
}
}
#Configuration
#EnableWebSocketMessageBroker
public class WebsocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/ws-on");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws-on")
.setAllowedOrigins("http://localhost:4200")
.withSockJS();
}
}
my angular is running on localhost:4200.
I used ws://localhost:8080/ws-on as the url from StompJS to connect.
My question is how do I find the websocket url to connect, and how do I know the websocket is running on the spring boot server?
finally I figured it out. Because I am using SockJS on both angular and spring boot, so, the URL is actaully http not ws. the correct url to connect is then http://localost:8080/ws-on

Spring Boot web socket has issues while sending messages for the first time

I am trying to create a chat application with spring boot web socket. The implementation is completed with spring boot and my Angular 7 app is connecting to this. The issue I face is, when i connect to the socket for the very first time after server reboot or so, the first 5-6 messages to the socket are not sent. From then on it works flawlessly and super fast. What is it that I am missing?
I am implementing WebSocketConfigurer and trying to use registerWebSocketHandlers to connect to /socket// where i pass my user id and conversation id to initiate the chat similar to /socket/1/2.
#Configuration
#EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
Logger logger = LogManager.getLogger(WebSocketConfiguration.class);
#Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new
ServletServerContainerFactoryBean();
container.setMaxBinaryMessageBufferSize(1024000);
return container;
}
#Bean
public SessionHandler sessionHandler() {
return new SessionHandler();
}
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry
registry){
registry.addHandler(sessionHandler(),
"/socket/*/*").setAllowedOrigins("*");
}
}
Expect the system to transport messages flawlessly from message 1. But it is perfect after the first few messages.

Invoke a service method after the Spring Boot deployed successfully

I have a spring boot application and i want to invoke a service method once the application gets deployed successfully.
Ex:
#Service
public class MyServiceImpl implements MyUtilityService<Object, Object>{
#Override
public Object runOnce(Object credential) {
return null;
}
}
I want to invoke myService.runOnce(...) only after my application deployed successfully
You can register an EventListener for the ApplicationReadyEvent.
From the docs:
Event published as late as conceivably possible to indicate that the
application is ready to service requests. The source of the event is
the SpringApplication itself, but beware of modifying its internal
state since all initialization steps will have been completed by then.
#Service
public class MyServiceImpl implements MyUtilityService<Object, Object>{
#Override
#EventListener(ApplicationReadyEvent.class)
public Object runOnce(Object credential) {
return null;
}
}

How to Create Spring WebSocket Application With HTML5 WebSocket API?

Recent Version of Spring WebSocket works with SockJS and StompJS libraries. But i don't like to use theme in my application. So how to create Spring WebSocket application with HTML5 WebSocket API and integrate our application with Spring Security?
I could not find any good example on how to configure spring websocket without sockjs but i found some helpful documentation in spring documentation site and i like to share that. Well, How to Create Spring WebSocket Application With HTML5 WebSocket API?
First: Create a Class that extends TextWebSocketHandler or BinaryWebSocketHandler and Annotate it with #Component annotation and Override its appropriate method.This Class works like handler methods in controllers.
#Component
public class SimpleWebSocketHandler extends TextWebSocketHandler {
#Override
protected void handleTextMessage(WebSocketSession session,
TextMessage message) throws Exception {
// Sends back response to client.
session.sendMessage(new TextMessage("Connection is all right."));
}
}
Second: Create a Configuration Class that implements WebSocketConfigurer and Annotate it with #Configuration and #EnableWebSocket annoations and Override its appropriate method.This Class uses Handler Class that we created already.
#Configuration
#EnableWebSocket
public class WebSocketConfigurations implements WebSocketConfigurer {
#Autowired
private SimpleWebSocketHandler simpleWebSocketHandler;
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// Regsiters a handler for related endpoint.
registry.addHandler(simpleWebSocketHandler, "/chat");
}
}
Third: Add all your WebSokcet Endpoints to your Spring Security Configuration.
httpSecurity.authorizeRequests()
.antMatchers("/chat").permitAll();
Fourth: We create a new javascript WebSocket objet with appropriate URL.
// Create WebSocket Object.
var ws = new WebSocket("ws://localhost:8080/chat");
// Runs when connecion is estabilished.
ws.onopen = function () {
// Sends request to server with string value.
ws.send("webSocket");
};
// Runs when response is ready.
// Use event to get response value.
ws.onmessage = function (event) {
};
Note: WebSocket URLs Format: ws://domain:port/endpoint

Spring 4.1 #JmsListener configuration

I would like to use the new annotations and features provided in Spring 4.1 for an application that needs a JMS listener.
I've carefully read the notes in the Spring 4.1 JMS improvements post but I continue to miss the relationship between #JmsListener and maybe the DestinationResolver and how I would setup the application to indicate the proper Destination or Endpoint.
Here is the suggested use of #JmsListener
#Component
public class MyService {
#JmsListener(containerFactory = "myContainerFactory", destination = "myQueue")
public void processOrder(String data) { ... }
}
Now, I can't use this in my actual code because the "myQueue" needs to be read from a configuration file using Environment.getProperty().
I can setup an appropriate myContainerFactory with a DestinationResolver but mostly, it seems you would just use DynamicDestinationResolver if you don't need JNDI to lookup a queue in an app server and didn't need to do some custom reply logic. I'm simply trying to understand how Spring wants me to indicate the name of the queue in a parameterized fashion using the #JmsListener annotation.
Further down the blog post, I find a reference to this Configurer:
#Configuration
#EnableJms
public class AppConfig implements JmsListenerConfigurer {
#Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
registrar.setDefaultContainerFactory(defaultContainerFactory());
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setDestination("anotherQueue");
endpoint.setMessageListener(message -> {
// processing
});
registrar.registerEndpoint(endpoint);
}
Now, this makes some amount of sense and I could see where this would allow me to set a Destination at runtime from some external string, but this seems to be in conflict with using #JmsListener as it appears to be overriding the annotation in favor of endpoint.setMessageListener in the code above.
Any tips on how to specify the appropriate queue name using #JmsListener?
Also note that depending on use case you can already parameterize using properties file per environment and PropertySourcesPlaceholderConfigurer
#JmsListener(destinations = "${some.key}")
As per https://jira.spring.io/browse/SPR-12289
In case people are using #JmsListener with spring boot, you do not have to configure PropertySourcesPlaceholderConfigurer. It work's out the box
Sample:
class
#JmsListener(destination = "${spring.activemq.queue.name}")
public void receiveEntityMessage(final TextMessage message) {
// process stuff
}
}
application.properties
spring.activemq.queue.name=some.weird.queue.name.that.does.not.exist
Spring boot output
[26-Aug;15:07:53.475]-[INFO ]-[,]-[DefaultMes]-[o.s.j.l.DefaultMessageListenerContainer ]-[931 ]-Successfully refreshed JMS Connection
[26-Aug;15:07:58.589]-[WARN ]-[,]-[DefaultMes]-[o.s.j.l.DefaultMessageListenerContainer ]-[880 ]-Setup of JMS message listener invoker failed for destination 'some.weird.queue.name.that.does.not.exist' - trying to recover. Cause: User user is not authorized to read from some.weird.queue.name.that.does.not.exist
[26-Aug;15:07:59.787]-[INFO ]-[,]-[DefaultMes]-[o.s.j.l.DefaultMessageListenerContainer ]-[931 ]-Successfully refreshed JMS Connection
[26-Aug;15:08:04.881]-[WARN ]-[,]-[DefaultMes]-[o.s.j.l.DefaultMessageListenerContainer ]-[880 ]-Setup of JMS message listener invoker failed for destination 'some.weird.queue.name.that.does.not.exist' - trying to recover. Cause: User user is not authorized to read from some.weird.queue.name.that.does.not.exist
This proves that #JmsListener is able to pickup property values from application.properties without actually setting up any explicit PropertySourcesPlaceholderConfigurer
I hope this helps!
You could eventually do that right now but it's a bit convoluted. You can set a custom JmsListenerEndpointRegistry using JmsListenerConfigurer
#Configuration
#EnableJms
public class AppConfig implements JmsListenerConfigurer {
#Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
registrar.setEndpointRegistry(customRegistry());
}
}
and then override the registerListenerContainer method, something like
public void registerListenerContainer(JmsListenerEndpoint endpoint, JmsListenerContainerFactory<?> factory) {
// resolve destination according to whatever -> resolvedDestination
((AbstractJmsListenerEndpoint)endpoint).setDestination(resolvedDestination);
super.registerListenerContainer(endpoint, factory);
}
But we could do better. Please watch/vote for SPR-12280

Resources