I hope for your help, I have already looked through everything but have not found an answer. The task is this: I need to send information from my Java application to the client (frontend). The essence of the information is that the user has a password expiration date in the system and I need to send a notification to the user that his password is about to expire. Of course, to do this, I need to make a request to the database for a specific user, calculate how many days he still has left before the password expires. I want to use Websocket for this. I made a config class for training videos and other servers and controllers, but I still didn’t understand how my application should send information to the client. Help! I already thought of using the @Sheduled annotation, but a method marked with this annotation cannot accept method arguments, then I will not be able to execute a timer request for any user. Here is my code, please tell me!
WebSocketConfig class
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(final MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/ws");
}
@Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/our-websocket")
.setHandshakeHandler(new UserHandshakeHandler())
.withSockJS();
}
}
UserHandshakeHandler class
public class UserHandshakeHandler extends DefaultHandshakeHandler {
private final Logger LOG = LoggerFactory.getLogger(UserHandshakeHandler.class);
@Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
final String randomId = UUID.randomUUID().toString();
LOG.info("User with ID '{}' opened the page", randomId);
return new UserPrincipal(randomId);
}
}
WSService class
@Service
public class WSService {
private final SimpMessagingTemplate messagingTemplate;
private final NotificationService notificationService;
@Autowired
public WSService(SimpMessagingTemplate messagingTemplate, NotificationService notificationService) {
this.messagingTemplate = messagingTemplate;
this.notificationService = notificationService;
}
public void notifyFrontend(final String message) {
ResponseMessage response = new ResponseMessage(message);
notificationService.sendGlobalNotification();
messagingTemplate.convertAndSend("/topic/messages", response);
}
public void notifyUser(final String id, final String message) {
ResponseMessage response = new ResponseMessage(message);
notificationService.sendPrivateNotification(id);
messagingTemplate.convertAndSendToUser(id, "/topic/private-messages", response);
}
}
WSController class
@RestController
public class WSController {
@Autowired
private WSService service;
@Autowired
private UsersRepository usersRepository;
@PostMapping("/send-message")
public void sendMessage(@RequestBody final Message message) {
service.notifyFrontend(message.getMessageContent());
}
@PostMapping("/send-private-message/{id}")
public void sendPrivateMessage(@PathVariable final String id,
@RequestBody final Message message) {
service.notifyUser(id, message.getMessageContent());
}
@Scheduled(fixedDelay = 10000)
public void sendPrivateMessage() {
List<Users> list = this.usersRepository.findAll();
for (Users users: list){
service.notifyUser(users.getId().toString(), "testString");
}
}
}
index.html code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Hello WS</title>
<link href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<script src="/webjars/jquery/jquery.min.js"></script>
<script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
<script src="/scripts.js"></script>
</head>
<body>
<div class="container" style="margin-top: 50px">
<div class="row">
<div class="col-md-12">
<form class="form-inline">
<div class="form-group">
<label for="message">Message</label>
<input type="text" id="message" class="form-control" placeholder="Enter your message here...">
</div>
<button id="send" class="btn btn-default" type="button">Send</button>
</form>
</div>
</div>
<div class="row" style="margin-top: 10px">
<div class="col-md-12">
<form class="form-inline">
<div class="form-group">
<label for="private-message">Private Message</label>
<input type="text" id="private-message" class="form-control" placeholder="Enter your message here...">
</div>
<button id="send-private" class="btn btn-default" type="button">Send Private Message</button>
</form>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="message-history" class="table table-striped">
<thead>
<tr>
<th>Messages
<span
id="notifications"
style="
color: white;
background-color: red;
padding-left: 15px;
padding-right: 15px;">
</span>
</th>
</tr>
</thead>
<tbody id="messages">
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
script.js code
var stompClient = null;
var notificationCount = 0;
$(document).ready(function() {
console.log("Index page is ready");
connect();
$("#send").click(function() {
sendMessage();
});
$("#send-private").click(function() {
sendPrivateMessage();
});
$("#notifications").click(function() {
resetNotificationCount();
});
});
function connect() {
var socket = new SockJS('/our-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
updateNotificationDisplay();
stompClient.subscribe('/topic/messages', function (message) {
showMessage(JSON.parse(message.body).content);
});
stompClient.subscribe('/user/topic/private-messages', function (message) {
showMessage(JSON.parse(message.body).content);
});
stompClient.subscribe('/topic/global-notifications', function (message) {
notificationCount = notificationCount + 1;
updateNotificationDisplay();
});
stompClient.subscribe('/user/topic/private-notifications', function (message) {
notificationCount = notificationCount + 1;
updateNotificationDisplay();
});
});
}
function showMessage(message) {
$("#messages").append("<tr><td>" + message + "</td></tr>");
}
function sendMessage() {
console.log("sending message");
stompClient.send("/ws/message", {}, JSON.stringify({'messageContent': $("#message").val()}));
}
function sendPrivateMessage() {
console.log("sending private message");
stompClient.send("/ws/private-message", {}, JSON.stringify({'messageContent': $("#private-message").val()}));
}
function updateNotificationDisplay() {
if (notificationCount == 0) {
$('#notifications').hide();
} else {
$('#notifications').show();
$('#notifications').text(notificationCount);
}
}
function resetNotificationCount() {
notificationCount = 0;
updateNotificationDisplay();
}
If you want to use websocket, the users you send notifications to must be using the application at that moment. That is, if the user to whom you send a notification is not logged in to the system, they cannot receive the message. Therefore, it would be more logical to use a mail server instead of websocket.
If you still want to use websocket, please share the client(frontend) code as well. Before you can send information to the client, the client must connect to websocket. After the connection is successfully established, you must subscribe to the channel.
First of all, the WebSocketConfig class needs to be edited as follows.
Then, you should correct the UseHandshakeHandler class. You must access and use the information of the requesting user with the following method.
I added an example of html and js code below. you can use this for testing.
src/main/resources/static/app.js:
src/main/resources/static/index.html:
Now, run the application and open the
index.html
file. Press the connect button and send a request to theuser-name
value shown on the screen from postman. You will see that the message reaches the user.Test pictures.
connect to websocket
send message to user by id
show message