Skip to content

Commit

Permalink
feat($STOMP): support STOMP over WebSocket
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnymillergh committed Jul 15, 2021
1 parent 3976af1 commit be76707
Show file tree
Hide file tree
Showing 17 changed files with 656 additions and 14 deletions.
7 changes: 6 additions & 1 deletion auth-center/pom.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

Expand Down Expand Up @@ -118,6 +118,11 @@
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>com.jmsoftware.maf</groupId>
<artifactId>universal-ui</artifactId>
</dependency>

<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.jmsoftware.maf.common.websocket;

import lombok.Data;

/**
* Description: WebSocketMessagePayload, change description here.
*
* @author Johnny Miller (锺俊), email: johnnysviva@outlook.com, date: 7/15/2021 5:35 PM
**/
@Data
public class WebSocketMessagePayload {
private String sender;
private String receiver;
private Object content;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.jmsoftware.maf.common.websocket;

import lombok.Data;

import java.time.LocalDateTime;

/**
* Description: WebSocketMessagePayload, change description here.
*
* @author Johnny Miller (锺俊), email: johnnysviva@outlook.com, date: 7/15/2021 5:35 PM
**/
@Data
public class WebSocketMessageResponse {
private final LocalDateTime timestamp = LocalDateTime.now();
private String sender;
private String receiver;
private Object content;
}
10 changes: 9 additions & 1 deletion spring-boot-admin/pom.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

Expand Down Expand Up @@ -98,6 +98,14 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-reactor-netty</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-sftp</artifactId>
Expand Down
10 changes: 9 additions & 1 deletion spring-cloud-starter/pom.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

Expand Down Expand Up @@ -54,6 +54,14 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-reactor-netty</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.jmsoftware.maf.springcloudstarter.service.CommonService;
import com.jmsoftware.maf.springcloudstarter.service.impl.CommonServiceImpl;
import com.jmsoftware.maf.springcloudstarter.sftp.SftpConfiguration;
import com.jmsoftware.maf.springcloudstarter.websocket.WebSocketConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.exceptions.PersistenceException;
import org.mybatis.spring.MyBatisSystemException;
Expand Down Expand Up @@ -66,7 +67,8 @@
MinioConfiguration.class,
JacksonConfiguration.class,
TypeConversionConfiguration.class,
QuartzConfiguration.class
QuartzConfiguration.class,
WebSocketConfiguration.class
})
public class MafAutoConfiguration {
@PostConstruct
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
*
* @param registry CORS registry
*/
@SuppressWarnings("BroadCORSAllowOrigin")
@Override
public void addCorsMappings(CorsRegistry registry) {
log.info("Configuring CORS allowedOrigins: {}, allowedMethods: {}, allowedHeaders: {}", ALL, ALL, ALL);
registry.addMapping("/**")
.allowedOrigins(ALL)
.allowCredentials(true)
.allowedOriginPatterns(ALL)
.allowedMethods(ALL)
.allowedHeaders(ALL)
.maxAge(MAX_AGE_SECS);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.jmsoftware.maf.springcloudstarter.websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.jmsoftware.maf.common.websocket.WebSocketMessagePayload;
import com.jmsoftware.maf.common.websocket.WebSocketMessageResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;

import java.util.Map;

/**
* Description: GreetingController, change description here.
*
* @author Johnny Miller (锺俊), email: johnnysviva@outlook.com, date: 7/15/2021 5:24 PM
**/
@Slf4j
@Controller
@RequiredArgsConstructor
public class GreetingController {
private final SimpMessagingTemplate simpMessagingTemplate;
private final ObjectMapper objectMapper;

@SneakyThrows
@MessageMapping("/send-message")
public void sendMessage(@Payload WebSocketMessagePayload payload, @Headers Map<String, Object> headers) {
val response = new WebSocketMessageResponse();
response.setSender(payload.getSender());
response.setReceiver(payload.getReceiver());
response.setContent(headers.get("simpSessionId").toString() + "---" + payload.getContent());
this.simpMessagingTemplate.convertAndSend("/topic/public/broadcast",
this.objectMapper.writeValueAsString(response));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.jmsoftware.maf.springcloudstarter.websocket;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.simp.SimpMessagingTemplate;
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;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

import static org.springframework.web.cors.CorsConfiguration.ALL;

/**
* Description: WebSocketConfiguration, change description here.
*
* @author Johnny Miller (锺俊), email: johnnysviva@outlook.com, date: 7/15/2021 12:15 AM
* @see
* <a href='https://www.toptal.com/java/stomp-spring-boot-websocket'>Using Spring Boot for WebSocket Implementation with STOMP</a>
**/
@Slf4j
@EnableWebSocketMessageBroker
@ConditionalOnClass({ServerEndpointExporter.class})
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
log.warn("Initial bean: '{}'", ServerEndpointExporter.class.getSimpleName());
return new ServerEndpointExporter();
}

@Bean
public GreetingController greetingController(SimpMessagingTemplate simpMessagingTemplate,
ObjectMapper objectMapper) {
log.warn("Initial bean: '{}'", GreetingController.class.getSimpleName());
return new GreetingController(simpMessagingTemplate, objectMapper);
}

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/web-socket", "/ws")
.setAllowedOriginPatterns(ALL)
.withSockJS();
}

/**
* {@inheritDoc}
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
// In-memory message broker with one or more destinations for sending and receiving messages

// There are two destination prefixes defined: topic and queue. They follow the convention that destinations
// for messages to be carried on to all subscribed clients via the pub-sub model should be prefixed with
// "topic".
// On the other hand, destinations for private messages are typically prefixed by "queue".
registry.enableSimpleBroker("/topic/", "/queue/");
// Defines the prefix app that is used to filter destinations handled by methods annotated with
// @MessageMapping which you will implement in a controller. The controller, after processing the message,
// will send it to the broker.
registry.setApplicationDestinationPrefixes("/app");

// Use this for enabling a Full featured broker like RabbitMQ
/*
registry.enableStompBrokerRelay("/topic")
.setRelayHost("localhost")
.setRelayPort(61613)
.setClientLogin("guest")
.setClientPasscode("guest");
*/
}
}
12 changes: 6 additions & 6 deletions universal-ui/src/main/resources/static/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<head>
<meta charset="UTF-8">
<title>UNSET</title>
<link rel="icon" href="icon/favicon.ico">
<link rel="stylesheet" href="styles/home.css">
<link href="icon/favicon.ico" rel="icon">
<link href="style/home.css" rel="stylesheet">
</head>
<body>
<div id="app">
Expand All @@ -14,11 +14,11 @@
<strong class="greeting-text">
Welcome to <span class="server-name">{{ projectArtifactId }}@{{ version }}</span>!
</strong>
<button class="go-to" @click="handleClickSwaggerApiDocumentation">Go to Swagger API Documentation</button>
<button class="go-to" @click="handleClickVideoDemo">Go to Video Demo</button>
<button @click="handleClickSwaggerApiDocumentation" class="go-to">Go to Swagger API Documentation</button>
<button @click="handleClickVideoDemo" class="go-to">Go to Video Demo</button>
<span class="application-name">{{ applicationName }}</span>
<img class="social-image" src="image/muscle-and-fitness-server-social-image.png"
alt="Muscle And Fitness Server Social Image">
<img alt="Muscle And Fitness Server Social Image" class="social-image"
src="image/muscle-and-fitness-server-social-image.png">
</div>
<fieldset>
<legend>Create New Person</legend>
Expand Down
119 changes: 119 additions & 0 deletions universal-ui/src/main/resources/static/script/websocket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
'use strict';

var usernamePage = document.querySelector('#username-page');
var chatPage = document.querySelector('#chat-page');
var usernameForm = document.querySelector('#usernameForm');
var messageForm = document.querySelector('#messageForm');
var messageInput = document.querySelector('#message');
var messageArea = document.querySelector('#messageArea');
var connectingElement = document.querySelector('.connecting');

var stompClient = null;
var username = null;

var colors = [
'#2196F3', '#32c787', '#00BCD4', '#ff5652',
'#ffc107', '#ff85af', '#FF9800', '#39bbb0'
];

function connect(event) {
username = document.querySelector('#name').value.trim();

if (username) {
usernamePage.classList.add('hidden');
chatPage.classList.remove('hidden');

var socket = new SockJS('/web-socket');
stompClient = Stomp.over(socket);

stompClient.connect({}, onConnected, onError);
}
event.preventDefault();
}


function onConnected() {
// Subscribe to the Public Topic
stompClient.subscribe('/topic/public/broadcast', onMessageReceived);

// Tell your username to the server
// stompClient.send(`/app/join/${username}`, {},)

connectingElement.classList.add('hidden');
}


function onError(error) {
connectingElement.textContent = 'Could not connect to WebSocket server. Please refresh this page to try again!';
connectingElement.style.color = 'red';
}


function sendMessage(event) {
var messageContent = messageInput.value.trim();

if (messageContent && stompClient) {
var chatMessage = {
sender: username,
receiver: null,
content: messageInput.value
};

stompClient.send("/app/send-message", {}, JSON.stringify(chatMessage));
messageInput.value = '';
}
event.preventDefault();
}


function onMessageReceived(payload) {
var body = JSON.parse(payload.body);
console.info('body', body);

var messageElement = document.createElement('li');

if (body.type === 'JOIN') {
messageElement.classList.add('event-message');
body.content = body.sender + ' joined!';
} else if (body.type === 'LEAVE') {
messageElement.classList.add('event-message');
body.content = body.sender + ' left!';
} else {
messageElement.classList.add('chat-message');

var avatarElement = document.createElement('i');
var avatarText = document.createTextNode(body.sender[0]);
avatarElement.appendChild(avatarText);
avatarElement.style['background-color'] = getAvatarColor(body.sender);

messageElement.appendChild(avatarElement);

var usernameElement = document.createElement('span');
var usernameText = document.createTextNode(body.sender);
usernameElement.appendChild(usernameText);
messageElement.appendChild(usernameElement);
}

var textElement = document.createElement('p');
var messageText = document.createTextNode(body.content);
textElement.appendChild(messageText);

messageElement.appendChild(textElement);

messageArea.appendChild(messageElement);
messageArea.scrollTop = messageArea.scrollHeight;
}


function getAvatarColor(messageSender) {
var hash = 0;
for (var i = 0; i < messageSender.length; i++) {
hash = 31 * hash + messageSender.charCodeAt(i);
}

var index = Math.abs(hash % colors.length);
return colors[index];
}

usernameForm.addEventListener('submit', connect, true)
messageForm.addEventListener('submit', sendMessage, true)
Loading

0 comments on commit be76707

Please sign in to comment.