first successful ws impl
This commit is contained in:
parent
74700bfdc2
commit
4329c5067f
14 changed files with 175 additions and 154 deletions
|
@ -1,13 +1,16 @@
|
||||||
package me.imsonmia.epqapi;
|
package me.imsonmia.epqapi;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
public class EpqapiApplication {
|
public class EpqapiApplication extends Thread {
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(EpqapiApplication.class);
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
logger.info("Main Spring Boot process running in thread!");
|
||||||
SpringApplication.run(EpqapiApplication.class, args);
|
SpringApplication.run(EpqapiApplication.class, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
21
src/main/java/me/imsonmia/epqapi/config/WebSocketConfig.java
Normal file
21
src/main/java/me/imsonmia/epqapi/config/WebSocketConfig.java
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package me.imsonmia.epqapi.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 registry) {
|
||||||
|
registry.enableSimpleBroker("/sub");
|
||||||
|
registry.setApplicationDestinationPrefixes("/app");
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||||
|
registry.addEndpoint("/ws").setAllowedOrigins("*");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,21 @@
|
||||||
package me.imsonmia.epqapi.controller;
|
package me.imsonmia.epqapi.controller;
|
||||||
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.messaging.handler.annotation.SendTo;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import me.imsonmia.epqapi.model.ChatMessage;
|
import me.imsonmia.epqapi.model.Message;
|
||||||
import me.imsonmia.epqapi.repository.ChatMessageRepository;
|
|
||||||
|
|
||||||
@RestController
|
@Controller
|
||||||
@RequestMapping("/api/v1")
|
// @RequestMapping("/api/v1")
|
||||||
public class ChatMessageController {
|
public class ChatMessageController {
|
||||||
private ChatMessageRepository chatMessageRepository;
|
@MessageMapping("/chat")
|
||||||
@GetMapping("/msg/{id}")
|
@SendTo("/sub/chat")
|
||||||
public ChatMessage getMessageById(@PathVariable(value = "id") Long id) {
|
public Message messageHandler(Message message) throws Exception {
|
||||||
return chatMessageRepository.findById(id).get();
|
return message;
|
||||||
}
|
}
|
||||||
|
// @GetMapping("/msg/{id}")
|
||||||
|
// public ChatMessage getMessageById(@PathVariable(value = "id") Long id) {
|
||||||
|
// return chatMessageRepository.findById(id).get();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
17
src/main/java/me/imsonmia/epqapi/controller/TextMessage.java
Normal file
17
src/main/java/me/imsonmia/epqapi/controller/TextMessage.java
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package me.imsonmia.epqapi.controller;
|
||||||
|
|
||||||
|
public class TextMessage {
|
||||||
|
private String text;
|
||||||
|
public TextMessage() {
|
||||||
|
|
||||||
|
}
|
||||||
|
public TextMessage(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +0,0 @@
|
||||||
package me.imsonmia.epqapi.messaging;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
|
|
||||||
import jakarta.websocket.DecodeException;
|
|
||||||
import jakarta.websocket.Decoder;
|
|
||||||
import jakarta.websocket.EndpointConfig;
|
|
||||||
|
|
||||||
public class MessageDecoder implements Decoder.Text<Message> {
|
|
||||||
private static Gson gson = new Gson();
|
|
||||||
@Override
|
|
||||||
public Message decode(String message) throws DecodeException {
|
|
||||||
return gson.fromJson(message, Message.class);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void destroy() {
|
|
||||||
Text.super.destroy();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void init(EndpointConfig endpointConfig) {
|
|
||||||
Text.super.init(endpointConfig);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public boolean willDecode(String s) {
|
|
||||||
return (s != null);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package me.imsonmia.epqapi.messaging;
|
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
|
||||||
|
|
||||||
import jakarta.websocket.EncodeException;
|
|
||||||
import jakarta.websocket.Encoder;
|
|
||||||
import jakarta.websocket.EndpointConfig;
|
|
||||||
|
|
||||||
public class MessageEncoder implements Encoder.Text<Message> {
|
|
||||||
private static Gson gson = new Gson();
|
|
||||||
@Override
|
|
||||||
public String encode(Message message) throws EncodeException {
|
|
||||||
return gson.toJson(message);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void destroy() {
|
|
||||||
Text.super.destroy();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void init(EndpointConfig endpointConfig) {
|
|
||||||
Text.super.init(endpointConfig);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,71 +0,0 @@
|
||||||
package me.imsonmia.epqapi.messaging;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
|
|
||||||
import jakarta.websocket.EncodeException;
|
|
||||||
import jakarta.websocket.OnClose;
|
|
||||||
import jakarta.websocket.OnMessage;
|
|
||||||
import jakarta.websocket.OnOpen;
|
|
||||||
import jakarta.websocket.Session;
|
|
||||||
import jakarta.websocket.server.ServerEndpoint;
|
|
||||||
import me.imsonmia.epqapi.model.ChatConvert;
|
|
||||||
import me.imsonmia.epqapi.model.ChatMessage;
|
|
||||||
import me.imsonmia.epqapi.repository.ChatMessageRepository;
|
|
||||||
|
|
||||||
// https://www.baeldung.com/java-websockets
|
|
||||||
@ServerEndpoint(
|
|
||||||
value = "/chat/{username}",
|
|
||||||
encoders = MessageEncoder.class,
|
|
||||||
decoders = MessageDecoder.class
|
|
||||||
)
|
|
||||||
public class WebSocketHandler {
|
|
||||||
private Logger logger;
|
|
||||||
private ChatMessageRepository chatMessageRepository;
|
|
||||||
private Session session;
|
|
||||||
private Set<WebSocketHandler> chatEndpoints = new CopyOnWriteArraySet<>();
|
|
||||||
private HashMap<String, String> users = new HashMap<>();
|
|
||||||
@OnOpen
|
|
||||||
public void onOpen(Session session, String username) throws IOException {
|
|
||||||
this.session = session;
|
|
||||||
chatEndpoints.add(this);
|
|
||||||
users.put(session.getId(), username);
|
|
||||||
Message message = new Message();
|
|
||||||
message.setFrom(username);
|
|
||||||
message.setContent("Connected, hello world!");
|
|
||||||
broadcast(message);
|
|
||||||
ChatMessage cmessage = new ChatConvert().fromMessage(message);
|
|
||||||
chatMessageRepository.save(cmessage);
|
|
||||||
}
|
|
||||||
@OnMessage
|
|
||||||
public void onMessage(Session session, Message message) throws IOException {
|
|
||||||
message.setFrom(users.get(session.getId()));
|
|
||||||
broadcast(message);
|
|
||||||
}
|
|
||||||
@OnClose
|
|
||||||
public void onClose(Session session) throws IOException {
|
|
||||||
chatEndpoints.remove(this);
|
|
||||||
Message message = new Message();
|
|
||||||
message.setFrom(users.get(session.getId()));
|
|
||||||
message.setContent("Disconnected!");
|
|
||||||
broadcast(message);
|
|
||||||
}
|
|
||||||
public void broadcast(Message message) {
|
|
||||||
chatEndpoints.forEach(endpoint -> {
|
|
||||||
try {
|
|
||||||
endpoint.session.getBasicRemote().sendObject(message);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
logger.info("Error sending message! IOException");
|
|
||||||
logger.error(null, e);
|
|
||||||
}
|
|
||||||
catch (EncodeException e) {
|
|
||||||
logger.info("Error sending message! EncodeException");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,10 @@
|
||||||
package me.imsonmia.epqapi.model;
|
package me.imsonmia.epqapi.model;
|
||||||
|
|
||||||
import me.imsonmia.epqapi.messaging.Message;
|
|
||||||
import me.imsonmia.epqapi.repository.UserRepository;
|
import me.imsonmia.epqapi.repository.UserRepository;
|
||||||
|
|
||||||
public class ChatConvert {
|
public class ChatConvert {
|
||||||
private UserRepository r;
|
private UserRepository r;
|
||||||
public ChatMessage fromMessage(Message s) {
|
public ChatMessage fromMessage(Message s) {
|
||||||
return new ChatMessage(r.findByFirstName(s.getFrom()).getId(), s.getContent());
|
return new ChatMessage(r.findByUserName(s.getFrom()).getId(), s.getContent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package me.imsonmia.epqapi.messaging;
|
package me.imsonmia.epqapi.model;
|
||||||
|
|
||||||
public class Message {
|
public class Message {
|
||||||
private String from;
|
private String from;
|
|
@ -5,5 +5,5 @@ import org.springframework.data.repository.CrudRepository;
|
||||||
import me.imsonmia.epqapi.model.User;
|
import me.imsonmia.epqapi.model.User;
|
||||||
|
|
||||||
public interface UserRepository extends CrudRepository<User, Long> {
|
public interface UserRepository extends CrudRepository<User, Long> {
|
||||||
User findByFirstName(String userName);
|
User findByUserName(String userName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
spring.datasource.url=jdbc:mariadb://localhost:3306/test
|
spring.datasource.url=jdbc:mariadb://127.0.0.1:3306/test
|
||||||
spring.datasource.username=dbuser
|
spring.datasource.username=dbuser
|
||||||
spring.datasource.password=dbpasswd
|
spring.datasource.password=dbpasswd
|
||||||
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
|
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
|
||||||
|
spring.jpa.show-sql: true
|
||||||
spring.jpa.hibernate.ddl-auto=create-drop
|
spring.jpa.hibernate.ddl-auto=create-drop
|
60
src/main/resources/static/app.js
Normal file
60
src/main/resources/static/app.js
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
const stompClient = new StompJs.Client({
|
||||||
|
brokerURL: 'ws://localhost:8080/ws'
|
||||||
|
});
|
||||||
|
|
||||||
|
stompClient.onConnect = (frame) => {
|
||||||
|
setConnected(true);
|
||||||
|
console.log('Connected: ' + frame);
|
||||||
|
stompClient.subscribe('/sub/chat', (greeting) => {
|
||||||
|
showGreeting(JSON.parse(greeting.body).content);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
stompClient.onWebSocketError = (error) => {
|
||||||
|
console.error('Error with websocket', error);
|
||||||
|
};
|
||||||
|
|
||||||
|
stompClient.onStompError = (frame) => {
|
||||||
|
console.error('Broker reported error: ' + frame.headers['message']);
|
||||||
|
console.error('Additional details: ' + frame.body);
|
||||||
|
};
|
||||||
|
|
||||||
|
function setConnected(connected) {
|
||||||
|
$("#connect").prop("disabled", connected);
|
||||||
|
$("#disconnect").prop("disabled", !connected);
|
||||||
|
if (connected) {
|
||||||
|
$("#conversation").show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$("#conversation").hide();
|
||||||
|
}
|
||||||
|
$("#greetings").html("");
|
||||||
|
}
|
||||||
|
|
||||||
|
function connect() {
|
||||||
|
stompClient.activate();
|
||||||
|
}
|
||||||
|
|
||||||
|
function disconnect() {
|
||||||
|
stompClient.deactivate();
|
||||||
|
setConnected(false);
|
||||||
|
console.log("Disconnected");
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendName() {
|
||||||
|
stompClient.publish({
|
||||||
|
destination: "/app/chat",
|
||||||
|
body: JSON.stringify({'text': $("#name").val()})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showGreeting(message) {
|
||||||
|
$("#greetings").append("<tr><td>" + message + "</td></tr>");
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
$("form").on('submit', (e) => e.preventDefault());
|
||||||
|
$( "#connect" ).click(() => connect());
|
||||||
|
$( "#disconnect" ).click(() => disconnect());
|
||||||
|
$( "#send" ).click(() => sendName());
|
||||||
|
});
|
52
src/main/resources/static/index.html
Normal file
52
src/main/resources/static/index.html
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Hello WebSocket</title>
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||||
|
<link href="/main.css" rel="stylesheet">
|
||||||
|
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/@stomp/stompjs@7.0.0/bundles/stomp.umd.min.js"></script>
|
||||||
|
<script src="/app.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
|
||||||
|
enabled. Please enable
|
||||||
|
Javascript and reload this page!</h2></noscript>
|
||||||
|
<div id="main-content" class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<form class="form-inline">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="connect">WebSocket connection:</label>
|
||||||
|
<button id="connect" class="btn btn-default" type="submit">Connect</button>
|
||||||
|
<button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<form class="form-inline">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">What is your name?</label>
|
||||||
|
<input type="text" id="name" class="form-control" placeholder="Your name here...">
|
||||||
|
</div>
|
||||||
|
<button id="send" class="btn btn-default" type="submit">Send</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<table id="conversation" class="table table-striped">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Greetings</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="greetings">
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -1,13 +0,0 @@
|
||||||
package me.imsonmia.epqapi;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
|
|
||||||
@SpringBootTest
|
|
||||||
class EpqapiApplicationTests {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void contextLoads() {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Loading…
Add table
Add a link
Reference in a new issue