2021-08-05
This commit is contained in:
25
websocket/build.gradle.kts
Normal file
25
websocket/build.gradle.kts
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Spring-boot Examples
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("elex-spring-boot")
|
||||
|
||||
id("org.springframework.boot") version "2.5.3"
|
||||
id("io.spring.dependency-management") version "1.0.11.RELEASE"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation ("org.springframework.boot:spring-boot-starter-web")
|
||||
implementation("org.springframework.boot:spring-boot-starter-mustache")
|
||||
|
||||
implementation("org.springframework.boot:spring-boot-starter-websocket")
|
||||
|
||||
compileOnly ("org.projectlombok:lombok")
|
||||
developmentOnly ("org.springframework.boot:spring-boot-devtools")
|
||||
annotationProcessor ("org.projectlombok:lombok")
|
||||
testImplementation ("org.springframework.boot:spring-boot-starter-test")
|
||||
}
|
||||
18
websocket/src/main/java/kr/pe/elex/examples/Application.java
Normal file
18
websocket/src/main/java/kr/pe/elex/examples/Application.java
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
31
websocket/src/main/java/kr/pe/elex/examples/Config.java
Normal file
31
websocket/src/main/java/kr/pe/elex/examples/Config.java
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import com.samskivert.mustache.Mustache;
|
||||
import org.springframework.boot.autoconfigure.mustache.MustacheEnvironmentCollector;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
@Configuration
|
||||
public class Config {
|
||||
|
||||
@Bean
|
||||
public Mustache.Compiler mustacheCompiler(
|
||||
Mustache.TemplateLoader templateLoader,
|
||||
Environment environment) {
|
||||
|
||||
MustacheEnvironmentCollector collector
|
||||
= new MustacheEnvironmentCollector();
|
||||
collector.setEnvironment(environment);
|
||||
|
||||
return Mustache.compiler()
|
||||
.withLoader(templateLoader)
|
||||
.withCollector(collector);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
public class HttpController {
|
||||
|
||||
@GetMapping("/")
|
||||
public ModelAndView home() {
|
||||
final Map<String, Object> map = new HashMap<>();
|
||||
map.put("title", "Websocket Test");
|
||||
return new ModelAndView("home", map);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
|
||||
|
||||
@Configuration
|
||||
@EnableWebSocket
|
||||
@RequiredArgsConstructor
|
||||
public class WebSocketConfig implements WebSocketConfigurer {
|
||||
private final WebSocketHandler webSocketHandler;
|
||||
|
||||
@Override
|
||||
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
|
||||
registry.addHandler(webSocketHandler, "/websocket");
|
||||
//.setAllowedOrigins("*")
|
||||
// .withSockJS();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.socket.CloseStatus;
|
||||
import org.springframework.web.socket.TextMessage;
|
||||
import org.springframework.web.socket.WebSocketSession;
|
||||
import org.springframework.web.socket.handler.TextWebSocketHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class WebSocketHandler extends TextWebSocketHandler {
|
||||
private static Set<WebSocketSession> sessions = new ConcurrentHashMap().newKeySet();
|
||||
|
||||
public static void publish(String message) throws IOException {
|
||||
for (WebSocketSession item : sessions) {
|
||||
try {
|
||||
item.sendMessage(new TextMessage(message));
|
||||
} catch (Throwable e) {
|
||||
log.error("Couldn't send message over websocket.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||
super.afterConnectionEstablished(session);
|
||||
sessions.add(session);
|
||||
log.info("Client({}) connected.", session.getRemoteAddress());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
|
||||
log.info("Message from client({}):{}", session.getRemoteAddress(), message.getPayload());
|
||||
for (WebSocketSession webSocketSession : sessions) {
|
||||
//if (session == webSocketSession) continue;
|
||||
String msg = message.getPayload().toUpperCase();
|
||||
webSocketSession.sendMessage(new TextMessage(msg));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
|
||||
super.afterConnectionClosed(session, status);
|
||||
sessions.remove(session);
|
||||
log.info("Client({}) disconnect.", session.getRemoteAddress());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
4
websocket/src/main/resources/application.properties
Normal file
4
websocket/src/main/resources/application.properties
Normal file
@@ -0,0 +1,4 @@
|
||||
#
|
||||
# Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
# https://www.elex-project.com/
|
||||
#
|
||||
19
websocket/src/main/resources/logback-spring.xml
Normal file
19
websocket/src/main/resources/logback-spring.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
~ https://www.elex-project.com/
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{15} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
108
websocket/src/main/resources/templates/home.mustache
Normal file
108
websocket/src/main/resources/templates/home.mustache
Normal file
@@ -0,0 +1,108 @@
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{{title}}</title>
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
margin:0; padding:0; border:0;
|
||||
}
|
||||
.layout {
|
||||
display: grid;
|
||||
grid-template-rows: 64px 1fr;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-areas: "controls" "textpane";
|
||||
width: 100%; height: 100%;
|
||||
}
|
||||
.row{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
grid-area: controls;
|
||||
background-color: orange;
|
||||
padding: 0 16px;
|
||||
}
|
||||
.row h1{
|
||||
flex-grow: 1;
|
||||
}
|
||||
.text-pane {
|
||||
grid-area: textpane;
|
||||
width: 100%; height: 100%;
|
||||
}
|
||||
textarea {
|
||||
width: 100%; height: 100%;
|
||||
overflow: auto;
|
||||
resize: none;
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
const log = function(msg){
|
||||
const ta = document.querySelector("#text-area");
|
||||
ta.append(new Date().toLocaleString());
|
||||
ta.append("\t");
|
||||
ta.append(msg);
|
||||
ta.append("\r\n");
|
||||
ta.scrollTop = ta.scrollHeight;
|
||||
};
|
||||
const WS = {
|
||||
websocket: null,
|
||||
connect: function(){
|
||||
this.websocket = new WebSocket("ws://localhost:8080/websocket");
|
||||
this.websocket.onopen = function(ev){
|
||||
console.log("Connected.");
|
||||
log("Connected.");
|
||||
};
|
||||
this.websocket.onclose = function(ev){
|
||||
console.log("Closed.");
|
||||
log("Closed.");
|
||||
};
|
||||
this.websocket.onmessage = function(ev){
|
||||
console.log("Rx: " + ev.data);
|
||||
log("Rx: " + ev.data);
|
||||
};
|
||||
this.websocket.onerror = function(ev){
|
||||
console.log("Error! " + ev.data);
|
||||
log("Error! " + ev.data);
|
||||
}
|
||||
},
|
||||
disconnect: function(){
|
||||
if (!this.websocket) websocket.close();
|
||||
console.log("Disconnected.");
|
||||
log("Disconnected.");
|
||||
},
|
||||
sendMessage: function(message){
|
||||
this.websocket.send(message);
|
||||
console.log("Tx: " + message);
|
||||
log("Tx: " + message);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('DOMContentLoaded', (event) => {
|
||||
WS.connect();
|
||||
});
|
||||
|
||||
const send = function(){
|
||||
const msg = document.querySelector("#text-to-send").value;
|
||||
WS.sendMessage(msg);
|
||||
};
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="layout">
|
||||
<div class="row">
|
||||
<h1>{{title}}</h1>
|
||||
<input id="text-to-send" type="text" />
|
||||
<button onclick="send();">Send</button>
|
||||
<button onclick="WS.connect();">Connect</button>
|
||||
<button onclick="WS.disconnect();">Disconnect</button>
|
||||
</div>
|
||||
<div class="text-pane">
|
||||
<textarea id="text-area"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class ApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user