2021-08-05
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* Spring-boot Examples
|
* Examples for Spring-boot
|
||||||
*
|
*
|
||||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
* Copyright (c) 2021-2021. Elex. All Rights Reserved.
|
||||||
* https://www.elex-project.com/
|
* https://www.elex-project.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
implementation("org.jetbrains:annotations:21.0.1")
|
//implementation("org.jetbrains:annotations:21.0.1")
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
27
mqtt/build.gradle.kts
Normal file
27
mqtt/build.gradle.kts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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-integration")
|
||||||
|
implementation ("org.springframework.integration:spring-integration-mqtt")
|
||||||
|
|
||||||
|
compileOnly ("org.projectlombok:lombok")
|
||||||
|
developmentOnly ("org.springframework.boot:spring-boot-devtools")
|
||||||
|
annotationProcessor ("org.projectlombok:lombok")
|
||||||
|
testImplementation ("org.springframework.boot:spring-boot-starter-test")
|
||||||
|
|
||||||
|
}
|
||||||
18
mqtt/src/main/java/kr/pe/elex/examples/Application.java
Normal file
18
mqtt/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
mqtt/src/main/java/kr/pe/elex/examples/Config.java
Normal file
31
mqtt/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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
35
mqtt/src/main/java/kr/pe/elex/examples/HttpController.java
Normal file
35
mqtt/src/main/java/kr/pe/elex/examples/HttpController.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/*
|
||||||
|
* 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.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
public class HttpController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MqttPublisher mqtt;
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
public ModelAndView home() {
|
||||||
|
final Map<String, Object> map = new HashMap<>();
|
||||||
|
return new ModelAndView("home", map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/publish")
|
||||||
|
public void publish(@RequestParam String topic, @RequestParam String message) {
|
||||||
|
log.info("I'll publish a message: {} {}", topic, message);
|
||||||
|
|
||||||
|
mqtt.publish(topic, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
92
mqtt/src/main/java/kr/pe/elex/examples/MqttConfig.java
Normal file
92
mqtt/src/main/java/kr/pe/elex/examples/MqttConfig.java
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||||
|
* https://www.elex-project.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kr.pe.elex.examples;
|
||||||
|
|
||||||
|
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
|
||||||
|
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.integration.annotation.ServiceActivator;
|
||||||
|
import org.springframework.integration.channel.DirectChannel;
|
||||||
|
import org.springframework.integration.core.MessageProducer;
|
||||||
|
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
|
||||||
|
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
|
||||||
|
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
|
||||||
|
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
|
||||||
|
import org.springframework.messaging.MessageChannel;
|
||||||
|
import org.springframework.messaging.MessageHandler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see "https://docs.spring.io/spring-integration/reference/html/mqtt.html#mqtt"
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class MqttConfig {
|
||||||
|
private static final String HOST = "tcp://localhost:1883";
|
||||||
|
private static final String USERNAME = "elex";
|
||||||
|
private static final String PASSWORD = "test";
|
||||||
|
private static final String CLIENT_ID = "i-am-a-server";
|
||||||
|
|
||||||
|
private static final String TOPIC_FILTER = "#";
|
||||||
|
private static final String TOPIC_FILTER_2 = "$SYS/#";
|
||||||
|
|
||||||
|
private MqttConnectOptions connectOptions() {
|
||||||
|
MqttConnectOptions options = new MqttConnectOptions();
|
||||||
|
options.setCleanSession(true);
|
||||||
|
options.setServerURIs(new String[]{HOST});
|
||||||
|
options.setKeepAliveInterval(10);
|
||||||
|
options.setAutomaticReconnect(true);
|
||||||
|
options.setUserName(USERNAME);
|
||||||
|
options.setPassword(PASSWORD.toCharArray());
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DefaultMqttPahoClientFactory defaultMqttPahoClientFactory() {
|
||||||
|
DefaultMqttPahoClientFactory clientFactory = new DefaultMqttPahoClientFactory();
|
||||||
|
clientFactory.setConnectionOptions(connectOptions());
|
||||||
|
return clientFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MessageChannel mqttInputChannel() {
|
||||||
|
return new DirectChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MessageProducer inboundChannel(DefaultMqttPahoClientFactory clientFactory) {
|
||||||
|
MqttPahoMessageDrivenChannelAdapter adapter =
|
||||||
|
new MqttPahoMessageDrivenChannelAdapter(HOST, CLIENT_ID, clientFactory,
|
||||||
|
TOPIC_FILTER, TOPIC_FILTER_2);
|
||||||
|
adapter.setCompletionTimeout(5000);
|
||||||
|
adapter.setConverter(new DefaultPahoMessageConverter());
|
||||||
|
adapter.setQos(1);
|
||||||
|
adapter.setOutputChannel(mqttInputChannel());
|
||||||
|
return adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ServiceActivator(inputChannel = "mqttInputChannel")
|
||||||
|
public MessageHandler messageHandler() {
|
||||||
|
return new MqttMessageHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public MessageChannel mqttOutboundChannel() {
|
||||||
|
return new DirectChannel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ServiceActivator(inputChannel = "mqttOutboundChannel")
|
||||||
|
public MessageHandler mqttOutbound(DefaultMqttPahoClientFactory clientFactory) {
|
||||||
|
MqttPahoMessageHandler messageHandler =
|
||||||
|
new MqttPahoMessageHandler(CLIENT_ID + MqttAsyncClient.generateClientId(),
|
||||||
|
clientFactory);
|
||||||
|
messageHandler.setAsync(true);
|
||||||
|
messageHandler.setDefaultQos(1);
|
||||||
|
return messageHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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.integration.mqtt.support.MqttHeaders;
|
||||||
|
import org.springframework.messaging.Message;
|
||||||
|
import org.springframework.messaging.MessageHandler;
|
||||||
|
import org.springframework.messaging.MessagingException;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class MqttMessageHandler implements MessageHandler {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 구독 중인 MQTT 메시지는 여기서 받는다.
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* @throws MessagingException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message<?> message) throws MessagingException {
|
||||||
|
String topic = (String) message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC);
|
||||||
|
log.info("MQTT Rx: {} {}", topic, message.getPayload());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
23
mqtt/src/main/java/kr/pe/elex/examples/MqttPublisher.java
Normal file
23
mqtt/src/main/java/kr/pe/elex/examples/MqttPublisher.java
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||||
|
* https://www.elex-project.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kr.pe.elex.examples;
|
||||||
|
|
||||||
|
import org.springframework.integration.annotation.MessagingGateway;
|
||||||
|
import org.springframework.integration.mqtt.support.MqttHeaders;
|
||||||
|
import org.springframework.messaging.handler.annotation.Header;
|
||||||
|
|
||||||
|
@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
|
||||||
|
public interface MqttPublisher {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MQTT 메시지는 여기서 발행한다.
|
||||||
|
*
|
||||||
|
* @param topic
|
||||||
|
* @param payload
|
||||||
|
*/
|
||||||
|
void publish(@Header(MqttHeaders.TOPIC) String topic, String payload);
|
||||||
|
|
||||||
|
}
|
||||||
6
mqtt/src/main/java/kr/pe/elex/examples/package-info.java
Normal file
6
mqtt/src/main/java/kr/pe/elex/examples/package-info.java
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||||
|
* https://www.elex-project.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
package kr.pe.elex.examples;
|
||||||
4
mqtt/src/main/resources/application.properties
Normal file
4
mqtt/src/main/resources/application.properties
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021. Elex. All Rights Reserved.
|
||||||
|
# https://www.elex-project.com/
|
||||||
|
#
|
||||||
19
mqtt/src/main/resources/logback-spring.xml
Normal file
19
mqtt/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>
|
||||||
15
mqtt/src/main/resources/templates/home.mustache
Normal file
15
mqtt/src/main/resources/templates/home.mustache
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>MQTT Spring-boot Integration Examples</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>MQTT Spring-boot Integration Examples</h1>
|
||||||
|
<dl>
|
||||||
|
<dt>Usage</dt>
|
||||||
|
<dd><code>/publish?topic=TOPIC&message=MESSAGE</code></dd>
|
||||||
|
</dl>
|
||||||
|
<p>Copyright $copy; 2021 Elex Project. All Rights Reserved.</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
18
mqtt/src/test/java/kr/pe/elex/examples/ApplicationTests.java
Normal file
18
mqtt/src/test/java/kr/pe/elex/examples/ApplicationTests.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.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class ApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
27
restful/build.gradle.kts
Normal file
27
restful/build.gradle.kts
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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")
|
||||||
|
|
||||||
|
compileOnly("org.projectlombok:lombok")
|
||||||
|
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||||
|
|
||||||
|
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
|
||||||
|
annotationProcessor("org.projectlombok:lombok")
|
||||||
|
|
||||||
|
testImplementation("org.springframework.boot:spring-boot-starter-test")
|
||||||
|
|
||||||
|
}
|
||||||
13
restful/src/main/java/kr/pe/elex/examples/Application.java
Normal file
13
restful/src/main/java/kr/pe/elex/examples/Application.java
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package kr.pe.elex.examples;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.util.MimeType;
|
||||||
|
import org.springframework.util.MimeTypeUtils;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
public class MainController {
|
||||||
|
|
||||||
|
@GetMapping(path = "/{name}", produces = {MimeTypeUtils.APPLICATION_JSON_VALUE})
|
||||||
|
public ResponseEntity<Person> home(@PathVariable String name) {
|
||||||
|
|
||||||
|
return ResponseEntity
|
||||||
|
.ok(new Person(name, new Random().nextInt()));
|
||||||
|
}
|
||||||
|
}
|
||||||
14
restful/src/main/java/kr/pe/elex/examples/Person.java
Normal file
14
restful/src/main/java/kr/pe/elex/examples/Person.java
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package kr.pe.elex.examples;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Person {
|
||||||
|
@JsonProperty
|
||||||
|
private String name;
|
||||||
|
@JsonProperty
|
||||||
|
private int age;
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
package kr.pe.elex.examples;
|
||||||
1
restful/src/main/resources/application.properties
Normal file
1
restful/src/main/resources/application.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -6,4 +6,5 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
rootProject.name = "spring-boot-examples"
|
rootProject.name = "spring-boot-examples"
|
||||||
include("file-upload", "security", "security-with-jpa", "validation", "testing")
|
include("file-upload", "security", "security-with-jpa", "validation", "testing",
|
||||||
|
"mqtt", "websocket", "restful")
|
||||||
|
|||||||
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