2021-08-05
This commit is contained in:
8
README.md
Normal file
8
README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# Java Examples
|
||||
|
||||
|
||||
|
||||
-----
|
||||
programmed by Elex
|
||||
|
||||
https://www.elex-project.com/
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
maven {
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins{
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
|
||||
plugins {
|
||||
id("elex-base")
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins {
|
||||
java
|
||||
}
|
||||
@@ -61,14 +68,16 @@ tasks.javadoc {
|
||||
}
|
||||
dependencies {
|
||||
//implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
|
||||
implementation("org.slf4j:slf4j-api:1.7.30")
|
||||
implementation("org.slf4j:slf4j-api:1.7.32")
|
||||
implementation("org.jetbrains:annotations:21.0.1")
|
||||
|
||||
implementation("com.elex-project:abraxas:4.5.3")
|
||||
|
||||
compileOnly("org.projectlombok:lombok:1.18.20")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.20")
|
||||
testAnnotationProcessor("org.projectlombok:lombok:1.18.20")
|
||||
|
||||
testImplementation("ch.qos.logback:logback-classic:1.2.3")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.7.0")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.0")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter:5.7.2")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.7.2")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id ("elex-base")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id ("elex-base")
|
||||
`java-library`
|
||||
|
||||
15
buildSrc/src/main/kotlin/elex-war.gradle.kts
Normal file
15
buildSrc/src/main/kotlin/elex-war.gradle.kts
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id ("elex-base")
|
||||
war
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
}
|
||||
7
gradle/wrapper/gradle-wrapper.properties
vendored
7
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,3 +1,10 @@
|
||||
#
|
||||
# Examples for Java
|
||||
#
|
||||
# Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
# https://www.elex-project.com/
|
||||
#
|
||||
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-all.zip
|
||||
|
||||
15
gradlew
vendored
15
gradlew
vendored
@@ -1,19 +1,10 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
# Examples for Java
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
# https://www.elex-project.com/
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
plugins {
|
||||
id("elex-library")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
package kr.pe.elex.examples;
|
||||
19
json-web-token/build.gradle.kts
Normal file
19
json-web-token/build.gradle.kts
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("elex-java")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api
|
||||
implementation("io.jsonwebtoken:jjwt-api:0.11.2")
|
||||
|
||||
// https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl
|
||||
runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.2")
|
||||
runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.2")
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import io.jsonwebtoken.*;
|
||||
import io.jsonwebtoken.security.InvalidKeyException;
|
||||
import io.jsonwebtoken.security.Keys;
|
||||
import io.jsonwebtoken.security.SignatureException;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
|
||||
public class JwtSample {
|
||||
private static final byte[] key;
|
||||
|
||||
static {
|
||||
key = new byte[32];
|
||||
new Random().nextBytes(key);
|
||||
}
|
||||
|
||||
public static String generateToken() throws InvalidKeyException {
|
||||
return Jwts.builder()
|
||||
.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
|
||||
.setIssuer("Elex")
|
||||
.setExpiration(Date.from(Instant.now().plus(3, ChronoUnit.HOURS)))
|
||||
.claim("userId", 3)
|
||||
.signWith(Keys.hmacShaKeyFor(key))
|
||||
.compact();
|
||||
}
|
||||
|
||||
public static Jws<Claims> parseToken(final String token)
|
||||
throws UnsupportedJwtException, MalformedJwtException, SignatureException, ExpiredJwtException {
|
||||
|
||||
return Jwts.parserBuilder()
|
||||
.setSigningKey(key)
|
||||
.build()
|
||||
.parseClaimsJws(parseHeader(token));
|
||||
}
|
||||
|
||||
private static String parseHeader(final String authenticationHeader) {
|
||||
final String[] authentication = authenticationHeader.split(" ");
|
||||
if (authentication.length == 2 && authentication[0].matches("[bB]earer")) {
|
||||
return authentication[1];
|
||||
} else if (authentication.length == 1) {
|
||||
return authentication[0];
|
||||
} else {
|
||||
throw new MalformedJwtException("Authentication Header param must be started with 'Bearer ': " + authenticationHeader);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String... args) {
|
||||
String token = generateToken();
|
||||
System.out.println(token);
|
||||
|
||||
String authHeader = "Bearer " + token;
|
||||
Jws<Claims> claims = parseToken(authHeader);
|
||||
System.out.println(claims);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class SampleTest {
|
||||
|
||||
@Test
|
||||
void test(){
|
||||
System.out.println(new String());
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Examples for Java
|
||||
~
|
||||
~ Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
~ https://www.elex-project.com/
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
<property name="LOG_FILE" value="LogFile" />
|
||||
<property name="LOG_DIR" value="/var/log/application" />
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("elex-java")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
||||
33
mosquitto/README.md
Normal file
33
mosquitto/README.md
Normal file
@@ -0,0 +1,33 @@
|
||||
# Mosquitto Example
|
||||
|
||||
```kotlin
|
||||
implementation("org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5")
|
||||
```
|
||||
|
||||
```java
|
||||
MqttClient client = new MqttClient(BROKER_URI, clientId, persistence);
|
||||
MqttConnectOptions connOpts = new MqttConnectOptions();
|
||||
connOpts.setUserName("elex");
|
||||
connOpts.setPassword("test".toCharArray());
|
||||
connOpts.setAutomaticReconnect(true);
|
||||
connOpts.setCleanSession(true);
|
||||
|
||||
client.connect(connOpts);
|
||||
```
|
||||
|
||||
```java
|
||||
MqttMessage msg = new MqttMessage(message.getBytes(StandardCharsets.UTF_8));
|
||||
msg.setQos(qos);
|
||||
client.publish(topic, msg);
|
||||
```
|
||||
|
||||
```java
|
||||
client.subscribe(topic, qos, listener);
|
||||
```
|
||||
|
||||
------
|
||||
Copyright (c) 2021. Elex.
|
||||
|
||||
All Rights Reserved.
|
||||
|
||||
https://www.elex-project.com/
|
||||
@@ -1,7 +1,20 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("elex-java")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
//implementation("org.eclipse.paho:org.eclipse.paho.mqttv5.client:1.2.5")
|
||||
implementation("org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5")
|
||||
|
||||
// https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
|
||||
implementation("org.bouncycastle:bcprov-jdk15on:1.69")
|
||||
// https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on
|
||||
implementation("org.bouncycastle:bcpkix-jdk15on:1.69")
|
||||
}
|
||||
|
||||
180
mosquitto/src/main/java/kr/pe/elex/examples/HelloMosquitto.java
Normal file
180
mosquitto/src/main/java/kr/pe/elex/examples/HelloMosquitto.java
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.paho.client.mqttv3.*;
|
||||
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
|
||||
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
/**
|
||||
* Mosquitto MQTT Example
|
||||
*
|
||||
* @author Elex
|
||||
*/
|
||||
@Slf4j
|
||||
public final class HelloMosquitto {
|
||||
static final String BROKER_URI = "tcp://127.0.0.1:1883";
|
||||
static final String BROKER_URI_TLS = "ssl://127.0.0.1:8883";
|
||||
static final String USER_NAME = "elex";
|
||||
static final String PASSWORD = "test";
|
||||
|
||||
private MqttClient client;
|
||||
private MemoryPersistence persistence = new MemoryPersistence();
|
||||
|
||||
/**
|
||||
* MQTT 연결
|
||||
*
|
||||
* @param clientId 연결하는 클라이언트 마다 다르게 지정.
|
||||
* @throws MqttException
|
||||
*/
|
||||
public HelloMosquitto(String clientId) throws MqttException {
|
||||
client = new MqttClient(BROKER_URI, clientId, persistence);
|
||||
MqttConnectOptions connOpts = new MqttConnectOptions();
|
||||
connOpts.setUserName(USER_NAME);
|
||||
connOpts.setPassword(PASSWORD.toCharArray());
|
||||
connOpts.setAutomaticReconnect(true);
|
||||
connOpts.setCleanSession(true);
|
||||
|
||||
client.connect(connOpts);
|
||||
}
|
||||
|
||||
/**
|
||||
* MQTT over TLS 연결
|
||||
*
|
||||
* @param clientId 연결하는 클라이언트 마다 다르게 지정.
|
||||
* @param socketFactory tls socket factory
|
||||
* @throws MqttException
|
||||
*/
|
||||
public HelloMosquitto(String clientId, SSLSocketFactory socketFactory) throws MqttException {
|
||||
client = new MqttClient(BROKER_URI_TLS, clientId, persistence); // 주소는 ssl로 시작하고, 포트는 8883을 사용합니다.
|
||||
MqttConnectOptions connOpts = new MqttConnectOptions();
|
||||
connOpts.setUserName(USER_NAME);
|
||||
connOpts.setPassword(PASSWORD.toCharArray());
|
||||
connOpts.setAutomaticReconnect(true);
|
||||
connOpts.setCleanSession(true);
|
||||
connOpts.setSocketFactory(socketFactory);
|
||||
connOpts.setHttpsHostnameVerificationEnabled(false);
|
||||
|
||||
client.connect(connOpts);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 발행
|
||||
*
|
||||
* @param topic 토픽. Word separator: '/'
|
||||
* @param message 메시지
|
||||
* @param qos 최대 전송 메시지 개수
|
||||
* @throws MqttException
|
||||
*/
|
||||
public void publish(String topic, String message, int qos) throws MqttException {
|
||||
if (client.isConnected()) {
|
||||
MqttMessage msg = new MqttMessage(message.getBytes(StandardCharsets.UTF_8));
|
||||
msg.setQos(qos);
|
||||
|
||||
client.publish(topic, msg);
|
||||
log.info("Tx: {} = {}", topic, new String(msg.getPayload(), StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 구독 시작
|
||||
*
|
||||
* @param topic 토픽 패턴. Wildcard char : '?' for 1 word, '#' for words
|
||||
* @param qos 최대 전송 메시지 개수
|
||||
* @param listener 리스너
|
||||
* @throws MqttException
|
||||
*/
|
||||
public void subscribe(String topic, int qos, IMqttMessageListener listener) throws MqttException {
|
||||
if (client.isConnected()) {
|
||||
client.subscribe(topic, qos, listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 구독 중단
|
||||
*
|
||||
* @param topic 토픽 패턴
|
||||
* @throws MqttException
|
||||
*/
|
||||
public void unsubscribe(String topic) throws MqttException {
|
||||
if (client.isConnected()) {
|
||||
client.unsubscribe(topic);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() throws MqttException {
|
||||
client.disconnect();
|
||||
}
|
||||
|
||||
public static void main(String... args)
|
||||
throws MqttException, CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException,
|
||||
KeyStoreException, KeyManagementException, IOException {
|
||||
// TCP connection
|
||||
HelloMosquitto client1 = new HelloMosquitto("Client_1");
|
||||
client1.subscribe("hello/#", 1, new IMqttMessageListener() {
|
||||
@Override
|
||||
public void messageArrived(String topic, MqttMessage message) throws Exception {
|
||||
log.info("Rx: {} = {}", topic, new String(message.getPayload(), StandardCharsets.UTF_8));
|
||||
}
|
||||
});
|
||||
client1.publish(String.join("/", "hello", "tcp"), "Hi!", 1);
|
||||
|
||||
// TLS connection
|
||||
HelloMosquitto client2 = new HelloMosquitto("Client_2", TlsHelper.socketFactory(
|
||||
HelloMosquitto.class.getResourceAsStream("/clientstore.p12"),
|
||||
"test".toCharArray(),
|
||||
"test1".toCharArray(),
|
||||
HelloMosquitto.class.getResourceAsStream("/truststore.p12"),
|
||||
"test".toCharArray()
|
||||
));
|
||||
client2.publish(String.join("/", "hello", "tls"), "Hahaha, ...", 1);
|
||||
|
||||
// TLS with BC connection
|
||||
HelloMosquitto client3 = new HelloMosquitto("Client_3", TlsHelperWithBouncyCastle.socketFactory(
|
||||
HelloMosquitto.class.getResourceAsStream("/client.pem"),
|
||||
HelloMosquitto.class.getResourceAsStream("/client.key.pem"),
|
||||
"test1".toCharArray(),
|
||||
HelloMosquitto.class.getResourceAsStream("/ca.pem")
|
||||
));
|
||||
client3.publish(String.join("/", "hello", "tls", "bc"), "Oke, ...", 1);
|
||||
|
||||
// TLS with BC connection, without client cert.
|
||||
HelloMosquitto client4 = new HelloMosquitto("Client_4", TlsHelperWithBouncyCastle.socketFactory(
|
||||
HelloMosquitto.class.getResourceAsStream("/ca.pem")
|
||||
));
|
||||
client4.publish(String.join("/", "hello", "tls", "bc-ca-only"), "Oke, ...", 1);
|
||||
|
||||
// // TLS connection, without client key store.
|
||||
HelloMosquitto client5 = new HelloMosquitto("Client_4", TlsHelper.socketFactory(
|
||||
HelloMosquitto.class.getResourceAsStream("/truststore.p12"),
|
||||
"test".toCharArray())
|
||||
);
|
||||
client5.publish(String.join("/", "hello", "tls", "ca-only"), "Mmmm, ...", 1);
|
||||
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted ...", e);
|
||||
}
|
||||
client1.close();
|
||||
client2.close();
|
||||
client3.close();
|
||||
client4.close();
|
||||
client5.close();
|
||||
}
|
||||
}
|
||||
114
mosquitto/src/main/java/kr/pe/elex/examples/TlsHelper.java
Normal file
114
mosquitto/src/main/java/kr/pe/elex/examples/TlsHelper.java
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
/**
|
||||
* @author Elex
|
||||
*/
|
||||
public final class TlsHelper {
|
||||
private TlsHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* SSL Context
|
||||
*
|
||||
* @param keyStoreInputStream 키스토어
|
||||
* @param keyStorePassword 키스토어 비번
|
||||
* @param keyPassword 키 비번
|
||||
* @param trustStoreInputStream CA 인증서 키 스토어
|
||||
* @param trustStorePassword 트러스트 스토어 비번
|
||||
* @return
|
||||
* @throws KeyStoreException
|
||||
* @throws CertificateException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws IOException
|
||||
* @throws UnrecoverableKeyException
|
||||
* @throws KeyManagementException
|
||||
*/
|
||||
public static SSLContext context(InputStream keyStoreInputStream, char[] keyStorePassword, char[] keyPassword,
|
||||
InputStream trustStoreInputStream, char[] trustStorePassword)
|
||||
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException,
|
||||
KeyManagementException {
|
||||
// OpenSSL로 키와 인증서를 만들고, PKCS12 키 저장소에 넣었습니다.
|
||||
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||
// 클라이언트 키와 인증서가 들어있습니다.
|
||||
keyStore.load(keyStoreInputStream,
|
||||
keyStorePassword); // 키스토어 비번
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
kmf.init(keyStore, keyPassword); // 키 비번
|
||||
|
||||
KeyStore trustKeyStore = KeyStore.getInstance("PKCS12");
|
||||
// CA 인증서가 들어있습니다.
|
||||
trustKeyStore.load(trustStoreInputStream,
|
||||
trustStorePassword); // 키 스토어 비번
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
tmf.init(trustKeyStore);
|
||||
|
||||
SSLContext context = SSLContext.getInstance("TLSv1.3");
|
||||
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
public static SSLSocketFactory socketFactory(InputStream keyStoreInputStream, char[] keyStorePassword, char[] keyPassword,
|
||||
InputStream trustStoreInputStream, char[] trustStorePassword)
|
||||
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException,
|
||||
KeyManagementException {
|
||||
return context(keyStoreInputStream, keyStorePassword, keyPassword, trustStoreInputStream, trustStorePassword)
|
||||
.getSocketFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param trustStoreInputStream CA 인증서 키 스토어
|
||||
* @param trustStorePassword 트러스트 스토어 비번
|
||||
* @return
|
||||
* @throws KeyStoreException
|
||||
* @throws CertificateException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws IOException
|
||||
* @throws UnrecoverableKeyException
|
||||
* @throws KeyManagementException
|
||||
*/
|
||||
public static SSLContext context(InputStream trustStoreInputStream, char[] trustStorePassword)
|
||||
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException,
|
||||
KeyManagementException {
|
||||
// OpenSSL로 키와 인증서를 만들고, PKCS12 키 저장소에 넣었습니다.
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
// 클라이언트 키와 인증서가 들어있습니다.
|
||||
keyStore.load(null, null); // 키스토어 비번
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
kmf.init(keyStore, null); // 키 비번
|
||||
|
||||
KeyStore trustKeyStore = KeyStore.getInstance("PKCS12");
|
||||
// CA 인증서가 들어있습니다.
|
||||
trustKeyStore.load(trustStoreInputStream,
|
||||
trustStorePassword); // 키 스토어 비번
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
tmf.init(trustKeyStore);
|
||||
|
||||
SSLContext context = SSLContext.getInstance("TLSv1.3");
|
||||
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
public static SSLSocketFactory socketFactory(InputStream trustStoreInputStream, char[] trustStorePassword)
|
||||
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException,
|
||||
KeyManagementException {
|
||||
return context(trustStoreInputStream, trustStorePassword).getSocketFactory();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.openssl.PEMDecryptorProvider;
|
||||
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
|
||||
import org.bouncycastle.openssl.PEMKeyPair;
|
||||
import org.bouncycastle.openssl.PEMParser;
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
|
||||
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
/**
|
||||
* @author Elex
|
||||
*/
|
||||
public final class TlsHelperWithBouncyCastle {
|
||||
private TlsHelperWithBouncyCastle() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Bouncy Castle을 이용.
|
||||
*
|
||||
* @param clientCrtInputStream cert pem
|
||||
* @param clientKeyInputStream key pem
|
||||
* @param clientPassword key password
|
||||
* @param caCrtInputStream ca cert pem
|
||||
* @return
|
||||
*/
|
||||
public static SSLSocketFactory socketFactory(InputStream clientCrtInputStream, InputStream clientKeyInputStream, char[] clientPassword,
|
||||
InputStream caCrtInputStream)
|
||||
throws KeyStoreException, IOException, CertificateException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException {
|
||||
return context(clientCrtInputStream, clientKeyInputStream, clientPassword, caCrtInputStream).getSocketFactory();
|
||||
}
|
||||
|
||||
public static SSLContext context(InputStream clientCrtInputStream, InputStream clientKeyInputStream, char[] clientPassword,
|
||||
InputStream caCrtInputStream)
|
||||
throws KeyStoreException, IOException, CertificateException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
|
||||
// CA 인증서 불러오기
|
||||
CertificateFactory caCertFactory = CertificateFactory.getInstance("X.509");
|
||||
PEMParser parser = new PEMParser(new InputStreamReader(caCrtInputStream));
|
||||
X509Certificate caCert = (X509Certificate) caCertFactory
|
||||
.generateCertificate(new ByteArrayInputStream(parser.readPemObject().getContent()));
|
||||
parser.close();
|
||||
|
||||
// 클라이언트 인증서 불러오기
|
||||
CertificateFactory clientCertFactory = CertificateFactory.getInstance("X.509");
|
||||
parser = new PEMParser(new InputStreamReader(clientCrtInputStream));
|
||||
X509Certificate clientCert = (X509Certificate) clientCertFactory
|
||||
.generateCertificate(new ByteArrayInputStream(parser.readPemObject().getContent()));
|
||||
parser.close();
|
||||
|
||||
// 클라이언트 비밀키 불러오기
|
||||
parser = new PEMParser(new InputStreamReader(clientKeyInputStream));
|
||||
Object object = parser.readObject();
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
|
||||
KeyPair keyPair;
|
||||
if (object instanceof PEMEncryptedKeyPair) {
|
||||
// 암호화된 키라면 패스워드가 필요하다.
|
||||
PEMEncryptedKeyPair pemKeyPair = (PEMEncryptedKeyPair) object;
|
||||
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(clientPassword);
|
||||
keyPair = converter.getKeyPair(pemKeyPair.decryptKeyPair(decProv));
|
||||
} else {
|
||||
// 암호화되지 않은 키라면 비밀번호가 필요없다.
|
||||
PEMKeyPair pemKeyPair = (PEMKeyPair) object;
|
||||
keyPair = converter.getKeyPair(pemKeyPair);
|
||||
}
|
||||
parser.close();
|
||||
PrivateKey clientKey = keyPair.getPrivate();
|
||||
|
||||
// CA 인증서를 사용해서 트러스트 스토어를 만든다.
|
||||
KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
caKeyStore.load(null, null);
|
||||
caKeyStore.setCertificateEntry("ca-certificate", caCert);
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(caKeyStore);
|
||||
|
||||
// 클라이언트 인증서와 비밀키를 사용해서 키 스토어를 만든다.
|
||||
KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
clientKeyStore.load(null, null);
|
||||
clientKeyStore.setCertificateEntry("certificate", clientCert);
|
||||
clientKeyStore.setKeyEntry("private-key", clientKey, clientPassword, new java.security.cert.Certificate[]{clientCert});
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
keyManagerFactory.init(clientKeyStore, clientPassword);
|
||||
|
||||
SSLContext context = SSLContext.getInstance("TLSv1.3");
|
||||
context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* BouncyCastle을 이용.
|
||||
*
|
||||
* @param caCrtInputStream ca cert pem
|
||||
* @return
|
||||
* @throws KeyStoreException
|
||||
* @throws IOException
|
||||
* @throws CertificateException
|
||||
* @throws UnrecoverableKeyException
|
||||
* @throws KeyManagementException
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
public static SSLContext context(InputStream caCrtInputStream)
|
||||
throws KeyStoreException, IOException, CertificateException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
|
||||
// CA 인증서 불러오기
|
||||
CertificateFactory caCertFactory = CertificateFactory.getInstance("X.509");
|
||||
PEMParser parser = new PEMParser(new InputStreamReader(caCrtInputStream));
|
||||
X509Certificate caCert = (X509Certificate) caCertFactory
|
||||
.generateCertificate(new ByteArrayInputStream(parser.readPemObject().getContent()));
|
||||
parser.close();
|
||||
|
||||
// CA 인증서를 사용해서 트러스트 스토어를 만든다.
|
||||
KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
caKeyStore.load(null, null);
|
||||
caKeyStore.setCertificateEntry("ca-certificate", caCert);
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(caKeyStore);
|
||||
|
||||
// 클라이언트 인증서와 비밀키는 없으므로, 그냥 만든다.
|
||||
KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
clientKeyStore.load(null, null);
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
keyManagerFactory.init(clientKeyStore, null);
|
||||
|
||||
SSLContext context = SSLContext.getInstance("TLSv1.3");
|
||||
context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
|
||||
return context;
|
||||
}
|
||||
|
||||
public static SSLSocketFactory socketFactory(InputStream caCrtInputStream)
|
||||
throws KeyStoreException, IOException, CertificateException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException {
|
||||
return context(caCrtInputStream).getSocketFactory();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
62
rabbit-mq/README.md
Normal file
62
rabbit-mq/README.md
Normal file
@@ -0,0 +1,62 @@
|
||||
# RabbitMQ Client Example
|
||||
|
||||
## Docker에 설치
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
docker run -d --restart=no --name=rabbitmq \
|
||||
--hostname rabbit-mq \
|
||||
-p 5672:5672 \
|
||||
-p 15672:15672 \
|
||||
-v /docker/rabbitmq/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
|
||||
-v docker/rabbitmq/conf.d:/etc/rabbitmq/conf.d \
|
||||
-e TZ=Asia/Seoul -e LANG=ko_KR.UTF-8 \
|
||||
-e RABBITMQ_DEFAULT_USER=elex -e RABBITMQ_DEFAULT_PASS=test \
|
||||
rabbitmq:3.8.11-management
|
||||
```
|
||||
|
||||
## Gradle 디펜던시 추가
|
||||
```kotlin
|
||||
// https://mvnrepository.com/artifact/com.rabbitmq/amqp-client
|
||||
implementation("com.rabbitmq:amqp-client:5.10.0")
|
||||
```
|
||||
|
||||
## 다이렉트 익스체인지
|
||||
* 익스체인지는 브로커가 메시지를 받는 곳이다. 즉, 메시지를 보낼 때는 익스체인지로 보내야 한다.
|
||||
* 큐는 브로커가 메시지를 보내는 곳이다. 즉, 메시지를 받을 때는 큐로부터 받는다.
|
||||
* 익스체인지와 큐는 라우팅-키로 서로 바인딩된다. 여러 개의 라우팅-키로 여러 번 바인딩 할 수도 있다.
|
||||
* 익스체인지에 메시지가 도착하면 메시지의 라우팅-키와 일치하는 큐로 메시지를 보낸다.
|
||||
|
||||
## 팬아웃 익스체인지
|
||||
* 라우팅-키 규칙이 무시된다.
|
||||
|
||||
## 토픽 익스체인지
|
||||
* 라우팅-키를 패턴으로 사용한다.
|
||||
|
||||
## TLS
|
||||
```bash
|
||||
!/bin/bash
|
||||
docker run -d --restart=always --name=rabbitmq \
|
||||
--hostname rabbitmq \
|
||||
-p 5671:5671 \
|
||||
-p 5672:5672 \
|
||||
-p 15671:15671 \
|
||||
-p 15672:15672 \
|
||||
-v /media/rabbitmq/certs:/etc/certs \
|
||||
-e TZ=Asia/Seoul -e LANG=ko_KR.UTF-8 \
|
||||
-e RABBITMQ_DEFAULT_USER=elex \
|
||||
-e RABBITMQ_DEFAULT_PASS=test \
|
||||
-e RABBITMQ_SSL_CACERTFILE=/etc/certs/ca.pem \
|
||||
-e RABBITMQ_SSL_CERTFILE=/etc/certs/server.pem \
|
||||
-e RABBITMQ_SSL_KEYFILE=/etc/certs/server.key.pem \
|
||||
-e RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT=false \
|
||||
-e RABBITMQ_SSL_VERIFY=verify_none \
|
||||
rabbitmq:3.8.11-management
|
||||
```
|
||||
|
||||
-----
|
||||
Copyright (c) 2021 Elex.
|
||||
|
||||
All Rights Reserved.
|
||||
|
||||
https://www.elex-project.com/
|
||||
@@ -1,7 +1,20 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("elex-java")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// https://mvnrepository.com/artifact/com.rabbitmq/amqp-client
|
||||
implementation("com.rabbitmq:amqp-client:5.13.0")
|
||||
|
||||
// https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on
|
||||
implementation("org.bouncycastle:bcprov-jdk15on:1.69")
|
||||
// https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on
|
||||
implementation("org.bouncycastle:bcpkix-jdk15on:1.69")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples.fanout;
|
||||
|
||||
import com.rabbitmq.client.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* 팬 아웃 익스체인지는 라우팅-키 규칙에 무관하게 메시지를 전달한다.
|
||||
* @author Elex
|
||||
* @see "https://www.rabbitmq.com/tutorials/tutorial-two-java.html"
|
||||
*/
|
||||
@Slf4j
|
||||
public class RabbitClient {
|
||||
private static final String EXCHANGE = "elex.fanout.exchange";
|
||||
|
||||
private Connection connection;
|
||||
private Channel channel;
|
||||
private String queue;
|
||||
|
||||
RabbitClient() throws IOException, TimeoutException {
|
||||
ConnectionFactory connectionFactory = new ConnectionFactory();
|
||||
connectionFactory.setHost("localhost");
|
||||
connectionFactory.setPort(5672);
|
||||
connectionFactory.setUsername("elex");
|
||||
connectionFactory.setPassword("test");
|
||||
connectionFactory.setVirtualHost("/");
|
||||
|
||||
connection = connectionFactory.newConnection();
|
||||
channel = connection.createChannel();
|
||||
|
||||
// fanout은 routing-key 규칙을 무시하고, 모든 큐에 메시지를 전달합니다.
|
||||
channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.FANOUT, false);
|
||||
|
||||
// 큐 이름을 랜덤으로 생성합니다.
|
||||
queue = channel.queueDeclare().getQueue();
|
||||
|
||||
// 익스체인지와 큐를 묶습니다.
|
||||
channel.queueBind(queue, EXCHANGE, "");
|
||||
|
||||
}
|
||||
|
||||
public void consume(String consumerTag) throws IOException {
|
||||
// 큐로부터 메시지를 받습니다.
|
||||
channel.basicConsume(queue, true, consumerTag, new DefaultConsumer(channel) {
|
||||
@Override
|
||||
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
|
||||
log.info("Rx: [{}] {}", consumerTag, new String(body, StandardCharsets.UTF_8));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void publish(String routingKey, String message) throws IOException {
|
||||
// 익스체인지에 메시지를 보냅니다.
|
||||
channel.basicPublish(EXCHANGE, routingKey, null, message.getBytes(StandardCharsets.UTF_8));
|
||||
log.info("Tx: [{}] {}", routingKey, message);
|
||||
}
|
||||
|
||||
public void close() throws IOException, TimeoutException {
|
||||
channel.close();
|
||||
connection.close();
|
||||
}
|
||||
|
||||
public static void main(String... args) throws IOException, TimeoutException {
|
||||
RabbitClient producer = new RabbitClient();
|
||||
RabbitClient consumer1 = new RabbitClient();
|
||||
|
||||
consumer1.consume("");
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
producer.publish(String.valueOf(i), "Hello, " + i);
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted..", e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted..", e);
|
||||
}
|
||||
producer.close();
|
||||
consumer1.close();
|
||||
|
||||
/*
|
||||
11:12:40.969 [main] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Tx: [0] Hello, 0
|
||||
11:12:40.969 [pool-3-thread-4] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Rx: [amq.ctag-NJH49xhJFHvgAo90c1qEiA] Hello, 0
|
||||
11:12:41.074 [main] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Tx: [1] Hello, 1
|
||||
11:12:41.077 [pool-3-thread-5] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Rx: [amq.ctag-NJH49xhJFHvgAo90c1qEiA] Hello, 1
|
||||
11:12:41.175 [main] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Tx: [2] Hello, 2
|
||||
11:12:41.178 [pool-3-thread-6] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Rx: [amq.ctag-NJH49xhJFHvgAo90c1qEiA] Hello, 2
|
||||
11:12:41.276 [main] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Tx: [3] Hello, 3
|
||||
11:12:41.279 [pool-3-thread-7] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Rx: [amq.ctag-NJH49xhJFHvgAo90c1qEiA] Hello, 3
|
||||
11:12:41.377 [main] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Tx: [4] Hello, 4
|
||||
11:12:41.380 [pool-3-thread-8] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Rx: [amq.ctag-NJH49xhJFHvgAo90c1qEiA] Hello, 4
|
||||
11:12:41.478 [main] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Tx: [5] Hello, 5
|
||||
11:12:41.481 [pool-3-thread-9] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Rx: [amq.ctag-NJH49xhJFHvgAo90c1qEiA] Hello, 5
|
||||
11:12:41.579 [main] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Tx: [6] Hello, 6
|
||||
11:12:41.582 [pool-3-thread-10] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Rx: [amq.ctag-NJH49xhJFHvgAo90c1qEiA] Hello, 6
|
||||
11:12:41.681 [main] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Tx: [7] Hello, 7
|
||||
11:12:41.684 [pool-3-thread-11] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Rx: [amq.ctag-NJH49xhJFHvgAo90c1qEiA] Hello, 7
|
||||
11:12:41.782 [main] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Tx: [8] Hello, 8
|
||||
11:12:41.785 [pool-3-thread-12] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Rx: [amq.ctag-NJH49xhJFHvgAo90c1qEiA] Hello, 8
|
||||
11:12:41.883 [main] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Tx: [9] Hello, 9
|
||||
11:12:41.886 [pool-3-thread-13] INFO kr.pe.elex.rabbitmq.fanout.RabbitClient - Rx: [amq.ctag-NJH49xhJFHvgAo90c1qEiA] Hello, 9
|
||||
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples.hello;
|
||||
|
||||
import com.rabbitmq.client.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* 다이렉트 익스체인지 샘플
|
||||
* <p>
|
||||
* 익스체인지는 브로커가 메시지를 받는 곳이다.
|
||||
* 큐는 브로커가 메시지를 보내는 곳이다.
|
||||
* 익스체인지와 큐는 라우팅-키로 서로 바인딩된다.
|
||||
* <p>
|
||||
* 익스체인지에 메시지가 도착하면 메시지의 라우팅-키와 일치하는 큐로 메시지를 보낸다.
|
||||
*
|
||||
* @author Elex
|
||||
* @see "https://www.rabbitmq.com/tutorials/tutorial-one-java.html"
|
||||
*/
|
||||
@Slf4j
|
||||
public class HelloRabbit {
|
||||
private static final String EXCHANGE = "elex.direct.exchange";
|
||||
private static final String QUEUE = "elex.queue.01";
|
||||
private static final String ROUTING_KEY = "elex-routing-key";
|
||||
|
||||
private Connection connection;
|
||||
private Channel channel;
|
||||
|
||||
HelloRabbit() throws IOException, TimeoutException {
|
||||
ConnectionFactory connectionFactory = new ConnectionFactory();
|
||||
connectionFactory.setHost("localhost");
|
||||
connectionFactory.setPort(5672);
|
||||
connectionFactory.setUsername("elex");
|
||||
connectionFactory.setPassword("test");
|
||||
connectionFactory.setVirtualHost("/");
|
||||
|
||||
connection = connectionFactory.newConnection();
|
||||
channel = connection.createChannel();
|
||||
|
||||
// 익스체인지는 브로커가 메시지를 받는 곳입니다.
|
||||
channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.DIRECT, true);
|
||||
|
||||
// 큐는 브로커가 메시지를 보내는 곳입니다.
|
||||
channel.queueDeclare(QUEUE, false, false, false, null);
|
||||
|
||||
// 익스체인지와 큐를 묶습니다.
|
||||
channel.queueBind(QUEUE, EXCHANGE, ROUTING_KEY);
|
||||
|
||||
// 큐로부터 메시지를 받습니다.
|
||||
channel.basicConsume(QUEUE, true, ROUTING_KEY, new DefaultConsumer(channel) {
|
||||
@Override
|
||||
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
|
||||
log.info("Rx: {}", new String(body, StandardCharsets.UTF_8));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void publish(String message) throws IOException {
|
||||
// 익스체인지에 메시지를 보냅니다.
|
||||
channel.basicPublish(EXCHANGE, ROUTING_KEY, null, message.getBytes(StandardCharsets.UTF_8));
|
||||
log.info("Tx: {}", message);
|
||||
}
|
||||
|
||||
public void close() throws IOException, TimeoutException {
|
||||
channel.close();
|
||||
connection.close();
|
||||
}
|
||||
|
||||
public static void main(String... args) throws IOException, TimeoutException {
|
||||
HelloRabbit helloRabbit = new HelloRabbit();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
helloRabbit.publish("Hello, " + i);
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted..", e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted..", e);
|
||||
}
|
||||
helloRabbit.close();
|
||||
/*
|
||||
11:01:31.502 [main] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Tx: Hello, 0
|
||||
11:01:31.502 [pool-1-thread-4] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Rx: Hello, 0
|
||||
11:01:32.505 [main] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Tx: Hello, 1
|
||||
11:01:32.509 [pool-1-thread-5] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Rx: Hello, 1
|
||||
11:01:33.507 [main] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Tx: Hello, 2
|
||||
11:01:33.510 [pool-1-thread-6] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Rx: Hello, 2
|
||||
11:01:34.508 [main] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Tx: Hello, 3
|
||||
11:01:34.511 [pool-1-thread-7] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Rx: Hello, 3
|
||||
11:01:35.509 [main] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Tx: Hello, 4
|
||||
11:01:35.512 [pool-1-thread-8] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Rx: Hello, 4
|
||||
11:01:36.511 [main] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Tx: Hello, 5
|
||||
11:01:36.514 [pool-1-thread-9] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Rx: Hello, 5
|
||||
11:01:37.512 [main] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Tx: Hello, 6
|
||||
11:01:37.515 [pool-1-thread-10] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Rx: Hello, 6
|
||||
11:01:38.513 [main] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Tx: Hello, 7
|
||||
11:01:38.516 [pool-1-thread-11] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Rx: Hello, 7
|
||||
11:01:39.514 [main] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Tx: Hello, 8
|
||||
11:01:39.517 [pool-1-thread-12] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Rx: Hello, 8
|
||||
11:01:40.515 [main] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Tx: Hello, 9
|
||||
11:01:40.518 [pool-1-thread-13] INFO kr.pe.elex.rabbitmq.hello.HelloRabbit - Rx: Hello, 9
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples.hello;
|
||||
|
||||
import com.rabbitmq.client.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* 다이렉트 익스체인지 샘플
|
||||
* <p>
|
||||
* 메시지 소비자는 메시지 처리 후 ack를 보내야 한다.
|
||||
* QoS를 지정해서 소비자에 전달할 메시지의 최대 개수를 지정할 수 있다.
|
||||
*
|
||||
* @author Elex
|
||||
* @see "https://www.rabbitmq.com/tutorials/tutorial-two-java.html"
|
||||
*/
|
||||
@Slf4j
|
||||
public class HelloRabbit2 {
|
||||
private static final String EXCHANGE = "elex.direct.exchange";
|
||||
private static final String QUEUE = "elex.queue";
|
||||
private static final String ROUTING_KEY = "elex-routing-key";
|
||||
|
||||
private Connection connection;
|
||||
private Channel channel;
|
||||
|
||||
HelloRabbit2() throws IOException, TimeoutException {
|
||||
ConnectionFactory connectionFactory = new ConnectionFactory();
|
||||
connectionFactory.setHost("localhost");
|
||||
connectionFactory.setPort(5672);
|
||||
connectionFactory.setUsername("elex");
|
||||
connectionFactory.setPassword("test");
|
||||
connectionFactory.setVirtualHost("/");
|
||||
connectionFactory.setAutomaticRecoveryEnabled(true);
|
||||
|
||||
connection = connectionFactory.newConnection();
|
||||
channel = connection.createChannel();
|
||||
|
||||
// 익스체인지는 브로커가 메시지를 받는 곳입니다.
|
||||
channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.DIRECT, true);
|
||||
|
||||
// 큐는 브로커가 메시지를 보내는 곳입니다.
|
||||
channel.queueDeclare(QUEUE, false, false, false, null);
|
||||
|
||||
// 익스체인지와 큐를 묶습니다.
|
||||
channel.queueBind(QUEUE, EXCHANGE, ROUTING_KEY);
|
||||
|
||||
// 메시지 소비자에 전달할 메시지의 최대 개수입니다. ack를 받을 때까지 메시지 전송을 미룰 수 있습니다.
|
||||
channel.basicQos(1);
|
||||
|
||||
// 큐로부터 메시지를 받습니다.
|
||||
channel.basicConsume(QUEUE, false, ROUTING_KEY, new DefaultConsumer(channel) {
|
||||
@Override
|
||||
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
|
||||
log.info("Rx: {}", new String(body, StandardCharsets.UTF_8));
|
||||
|
||||
// 만일, 수신 확인을 하지 않으면, 브로커는 다시 전송을 시도할겁니다.
|
||||
channel.basicAck(envelope.getDeliveryTag(), false);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public void publish(String message) throws IOException, TimeoutException, InterruptedException {
|
||||
// 익스체인지에 메시지를 보냅니다.
|
||||
channel.basicPublish(EXCHANGE, ROUTING_KEY,
|
||||
// 브로커가 메시지를 디스크에 저장해둠으로써, 오류 등으로 브로커가 종료되었을 경우에
|
||||
// 미처 전달되지 못한 메시지가 사라지는 것을 예방합니다.
|
||||
MessageProperties.PERSISTENT_TEXT_PLAIN,
|
||||
message.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
// 메시지가 전달되지 않으면 예외가 발생합니다.
|
||||
channel.waitForConfirmsOrDie(1000);
|
||||
|
||||
log.info("Tx: {}", message);
|
||||
}
|
||||
|
||||
public void close() throws IOException, TimeoutException {
|
||||
channel.close();
|
||||
connection.close();
|
||||
}
|
||||
|
||||
public static void main(String... args) throws IOException, TimeoutException {
|
||||
HelloRabbit2 helloRabbit = new HelloRabbit2();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
try {
|
||||
helloRabbit.publish("Hello, " + i);
|
||||
} catch (TimeoutException | InterruptedException e) {
|
||||
log.error("Publish fail..", e);
|
||||
}
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted..", e);
|
||||
}
|
||||
}
|
||||
helloRabbit.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples.loadbalance;
|
||||
|
||||
import com.rabbitmq.client.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* 하나의 큐에 여러 개의 컨슈머를 할당합니다.
|
||||
*
|
||||
* @author Elex
|
||||
* @see "https://www.rabbitmq.com/tutorials/tutorial-two-java.html"
|
||||
*/
|
||||
@Slf4j
|
||||
public class RabbitClient {
|
||||
private static final String EXCHANGE = "elex.direct.exchange";
|
||||
private static final String QUEUE = "elex.queue";
|
||||
private static final String ROUTING_KEY = "elex-routing-key";
|
||||
|
||||
private String name;
|
||||
|
||||
private Connection connection;
|
||||
private Channel channel;
|
||||
|
||||
RabbitClient(String name) throws IOException, TimeoutException {
|
||||
this.name = name;
|
||||
|
||||
ConnectionFactory connectionFactory = new ConnectionFactory();
|
||||
connectionFactory.setHost("localhost");
|
||||
connectionFactory.setPort(5672);
|
||||
connectionFactory.setUsername("elex");
|
||||
connectionFactory.setPassword("test");
|
||||
connectionFactory.setVirtualHost("/");
|
||||
|
||||
connection = connectionFactory.newConnection();
|
||||
channel = connection.createChannel();
|
||||
|
||||
// 익스체인지는 브로커가 메시지를 받는 곳입니다.
|
||||
channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.DIRECT, true);
|
||||
|
||||
// 큐는 브로커가 메시지를 보내는 곳입니다.
|
||||
channel.queueDeclare(QUEUE, false, false, false, null);
|
||||
|
||||
// 익스체인지와 큐를 묶습니다.
|
||||
channel.queueBind(QUEUE, EXCHANGE, ROUTING_KEY);
|
||||
|
||||
// 메시지 소비자에 전달할 메시지의 최대 개수입니다. ack를 받을 때까지 메시지 전송을 미룰 수 있습니다.
|
||||
channel.basicQos(1);
|
||||
}
|
||||
|
||||
public void consume(String consumerTag) throws IOException {
|
||||
// 큐로부터 메시지를 받습니다.
|
||||
channel.basicConsume(QUEUE, false, consumerTag, new DefaultConsumer(channel) {
|
||||
@Override
|
||||
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
|
||||
log.info("Rx: [{}] {}", name, new String(body, StandardCharsets.UTF_8));
|
||||
try {
|
||||
// 메시지를 처리하는데 시간이 좀 걸린다고 가정합니다.
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted..", e);
|
||||
}
|
||||
// 메시지 처리 후 ack를 보냅니다.
|
||||
channel.basicAck(envelope.getDeliveryTag(), false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void publish(String message) throws IOException {
|
||||
// 익스체인지에 메시지를 보냅니다.
|
||||
channel.basicPublish(EXCHANGE, ROUTING_KEY, null, message.getBytes(StandardCharsets.UTF_8));
|
||||
log.info("Tx: [{}] {}", name, message);
|
||||
}
|
||||
|
||||
public void close() throws IOException, TimeoutException {
|
||||
channel.close();
|
||||
connection.close();
|
||||
}
|
||||
|
||||
public static void main(String... args) throws IOException, TimeoutException {
|
||||
RabbitClient producer = new RabbitClient("Producer");
|
||||
RabbitClient consumer1 = new RabbitClient("Consumer1");
|
||||
RabbitClient consumer2 = new RabbitClient("Consumer2");
|
||||
RabbitClient consumer3 = new RabbitClient("Consumer3");
|
||||
consumer1.consume(ROUTING_KEY);
|
||||
consumer2.consume(ROUTING_KEY);
|
||||
consumer3.consume(ROUTING_KEY);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
producer.publish("Hello, " + i);
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted..", e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted..", e);
|
||||
}
|
||||
producer.close();
|
||||
consumer1.close();
|
||||
consumer2.close();
|
||||
consumer3.close();
|
||||
/*
|
||||
10:59:25.659 [main] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Tx: [Producer] Hello, 0
|
||||
10:59:25.659 [pool-3-thread-4] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Rx: [Consumer1] Hello, 0
|
||||
10:59:25.762 [main] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Tx: [Producer] Hello, 1
|
||||
10:59:25.765 [pool-5-thread-4] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Rx: [Consumer2] Hello, 1
|
||||
10:59:25.863 [main] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Tx: [Producer] Hello, 2
|
||||
10:59:25.866 [pool-7-thread-4] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Rx: [Consumer3] Hello, 2
|
||||
10:59:25.965 [main] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Tx: [Producer] Hello, 3
|
||||
10:59:26.066 [main] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Tx: [Producer] Hello, 4
|
||||
10:59:26.167 [main] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Tx: [Producer] Hello, 5
|
||||
10:59:26.268 [main] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Tx: [Producer] Hello, 6
|
||||
10:59:26.369 [main] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Tx: [Producer] Hello, 7
|
||||
10:59:26.470 [main] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Tx: [Producer] Hello, 8
|
||||
10:59:26.571 [main] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Tx: [Producer] Hello, 9
|
||||
10:59:26.664 [pool-3-thread-5] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Rx: [Consumer1] Hello, 3
|
||||
10:59:26.767 [pool-5-thread-5] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Rx: [Consumer2] Hello, 4
|
||||
10:59:26.869 [pool-7-thread-5] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Rx: [Consumer3] Hello, 5
|
||||
10:59:27.665 [pool-3-thread-5] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Rx: [Consumer1] Hello, 6
|
||||
10:59:27.768 [pool-5-thread-5] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Rx: [Consumer2] Hello, 7
|
||||
10:59:27.870 [pool-7-thread-5] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Rx: [Consumer3] Hello, 8
|
||||
10:59:28.666 [pool-3-thread-5] INFO kr.pe.elex.rabbitmq.loadbalance.RabbitClient - Rx: [Consumer1] Hello, 9
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
120
rabbit-mq/src/main/java/kr/pe/elex/examples/rpc/HelloRabbit.java
Normal file
120
rabbit-mq/src/main/java/kr/pe/elex/examples/rpc/HelloRabbit.java
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples.rpc;
|
||||
|
||||
import com.rabbitmq.client.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import static kr.pe.elex.examples.rpc.HelloRabbitServer.EXCHANGE;
|
||||
import static kr.pe.elex.examples.rpc.HelloRabbitServer.ROUTING_KEY;
|
||||
|
||||
/**
|
||||
* {@link AMQP.BasicProperties.Builder#replyTo(String)}를 사용해서 응답받을 라우팅-키를 전달할 수 있다.
|
||||
*
|
||||
* @author Elex
|
||||
* @see "https://www.rabbitmq.com/tutorials/tutorial-six-java.html"
|
||||
*/
|
||||
@Slf4j
|
||||
public class HelloRabbit {
|
||||
private static final String CLIENT_ROUTING_KEY = "client-routing-key";
|
||||
private Connection connection;
|
||||
private Channel channel;
|
||||
private String queue;
|
||||
|
||||
private Map<UUID, Handler> handlers = new HashMap<>();
|
||||
|
||||
HelloRabbit() throws IOException, TimeoutException {
|
||||
ConnectionFactory connectionFactory = new ConnectionFactory();
|
||||
connectionFactory.setHost("localhost");
|
||||
connectionFactory.setPort(5672);
|
||||
connectionFactory.setUsername("elex");
|
||||
connectionFactory.setPassword("test");
|
||||
connectionFactory.setVirtualHost("/");
|
||||
|
||||
connection = connectionFactory.newConnection();
|
||||
channel = connection.createChannel();
|
||||
|
||||
// 익스체인지는 브로커가 메시지를 받는 곳입니다.
|
||||
channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.DIRECT, true);
|
||||
// 큐는 브로커가 메시지를 보내는 곳입니다.
|
||||
queue = channel.queueDeclare().getQueue();
|
||||
// 익스체인지와 큐를 묶습니다.
|
||||
channel.queueBind(queue, EXCHANGE, CLIENT_ROUTING_KEY);
|
||||
|
||||
// 큐로부터 메시지를 받습니다.
|
||||
channel.basicConsume(queue, false, queue, new DefaultConsumer(channel) {
|
||||
@Override
|
||||
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
|
||||
// 메시지 아이디로 핸들러를 가져옵니다.
|
||||
Handler handler = handlers.remove(UUID.fromString(properties.getCorrelationId()));
|
||||
if (null != handler) {
|
||||
handler.onResponse(new String(body, StandardCharsets.UTF_8));
|
||||
channel.basicAck(envelope.getDeliveryTag(), false);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void publish(String message, Handler handler) throws IOException {
|
||||
UUID uuid = UUID.randomUUID(); // 메시지 아이디로 사용됩니다.
|
||||
handlers.put(uuid, handler); // 응답 처리를 위해 핸들러를 저장해둡니다.
|
||||
|
||||
// 익스체인지에 메시지를 보냅니다.
|
||||
channel.basicPublish(EXCHANGE, ROUTING_KEY,
|
||||
new AMQP.BasicProperties.Builder()
|
||||
.replyTo(CLIENT_ROUTING_KEY)
|
||||
.contentEncoding(StandardCharsets.UTF_8.name())
|
||||
.contentType("text/plain")
|
||||
.correlationId(uuid.toString())
|
||||
.deliveryMode(MessageProperties.PERSISTENT_BASIC.getDeliveryMode())
|
||||
.build(),
|
||||
message.getBytes(StandardCharsets.UTF_8));
|
||||
log.info("Tx: {}", message);
|
||||
}
|
||||
|
||||
public void close() throws IOException, TimeoutException {
|
||||
channel.close();
|
||||
connection.close();
|
||||
handlers.clear();
|
||||
}
|
||||
|
||||
interface Handler {
|
||||
void onResponse(String message);
|
||||
}
|
||||
|
||||
public static void main(String... args) throws IOException, TimeoutException {
|
||||
HelloRabbitServer server = new HelloRabbitServer();
|
||||
HelloRabbit client = new HelloRabbit();
|
||||
client.publish("Hello, there", new Handler() {
|
||||
@Override
|
||||
public void onResponse(String message) {
|
||||
log.info("Rx: {}", message);
|
||||
}
|
||||
});
|
||||
client.publish("Lorem ipsum ...", new Handler() {
|
||||
@Override
|
||||
public void onResponse(String message) {
|
||||
log.info("Rx: {}", message);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted..", e);
|
||||
}
|
||||
client.close();
|
||||
server.close();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples.rpc;
|
||||
|
||||
import com.rabbitmq.client.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* @author Elex
|
||||
* @see "https://www.rabbitmq.com/tutorials/tutorial-six-java.html"
|
||||
*/
|
||||
@Slf4j
|
||||
public class HelloRabbitServer {
|
||||
static final String EXCHANGE = "elex.rpc.exchange";
|
||||
static final String QUEUE = "elex.rpc.queue";
|
||||
static final String ROUTING_KEY = "elex-routing-key";
|
||||
|
||||
private Connection connection;
|
||||
private Channel channel;
|
||||
|
||||
HelloRabbitServer() throws IOException, TimeoutException {
|
||||
ConnectionFactory connectionFactory = new ConnectionFactory();
|
||||
connectionFactory.setHost("localhost");
|
||||
connectionFactory.setPort(5672);
|
||||
connectionFactory.setUsername("elex");
|
||||
connectionFactory.setPassword("test");
|
||||
connectionFactory.setVirtualHost("/");
|
||||
|
||||
connection = connectionFactory.newConnection();
|
||||
channel = connection.createChannel();
|
||||
|
||||
// 익스체인지는 브로커가 메시지를 받는 곳입니다.
|
||||
channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.DIRECT, true);
|
||||
// 큐는 브로커가 메시지를 보내는 곳입니다.
|
||||
channel.queueDeclare(QUEUE, false, false, false, null);
|
||||
// 익스체인지와 큐를 묶습니다.
|
||||
channel.queueBind(QUEUE, EXCHANGE, ROUTING_KEY);
|
||||
|
||||
// 큐로부터 메시지를 받습니다.
|
||||
channel.basicConsume(QUEUE, true, ROUTING_KEY, new DefaultConsumer(channel) {
|
||||
@Override
|
||||
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
|
||||
log.info("Server Rx: {}", new String(body, StandardCharsets.UTF_8));
|
||||
|
||||
String queue = properties.getReplyTo();
|
||||
String messageId = properties.getCorrelationId();
|
||||
String message = new String(body, StandardCharsets.UTF_8).toUpperCase();
|
||||
|
||||
channel.basicPublish(EXCHANGE, queue,
|
||||
new AMQP.BasicProperties.Builder()
|
||||
.correlationId(messageId)
|
||||
.build(),
|
||||
message.getBytes(StandardCharsets.UTF_8));
|
||||
log.info("Server Tx: {}", message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void close() throws IOException, TimeoutException {
|
||||
channel.close();
|
||||
connection.close();
|
||||
}
|
||||
|
||||
interface Handler {
|
||||
void onResponse(String message);
|
||||
}
|
||||
|
||||
}
|
||||
106
rabbit-mq/src/main/java/kr/pe/elex/examples/tls/HelloRabbit.java
Normal file
106
rabbit-mq/src/main/java/kr/pe/elex/examples/tls/HelloRabbit.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples.tls;
|
||||
|
||||
import com.rabbitmq.client.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* TLS 샘플
|
||||
*
|
||||
* @author Elex
|
||||
* @see "https://www.rabbitmq.com/tutorials/tutorial-one-java.html"
|
||||
*/
|
||||
@Slf4j
|
||||
public class HelloRabbit {
|
||||
private static final String EXCHANGE = "elex.direct.exchange";
|
||||
private static final String QUEUE = "elex.queue.01";
|
||||
private static final String ROUTING_KEY = "elex-routing-key";
|
||||
|
||||
private Connection connection;
|
||||
private Channel channel;
|
||||
|
||||
private SSLContext sslContext()
|
||||
throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, KeyManagementException, IOException, CertificateException {
|
||||
return TlsHelper.context(getClass().getResourceAsStream("/clientstore.p12"),
|
||||
"test".toCharArray(),
|
||||
"test1".toCharArray(),
|
||||
getClass().getResourceAsStream("/truststore.p12"),
|
||||
"test".toCharArray());
|
||||
}
|
||||
|
||||
HelloRabbit() throws IOException, TimeoutException, KeyManagementException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException, CertificateException {
|
||||
ConnectionFactory connectionFactory = new ConnectionFactory();
|
||||
connectionFactory.setHost("localhost");
|
||||
connectionFactory.setPort(5671); // 포트 번호가 다릅니다.
|
||||
connectionFactory.setUsername("elex");
|
||||
connectionFactory.setPassword("test");
|
||||
connectionFactory.setVirtualHost("/");
|
||||
//connectionFactory.useSslProtocol(); // 테스트 환경에서 사용됩니다.
|
||||
connectionFactory.useSslProtocol(sslContext());
|
||||
//connectionFactory.enableHostnameVerification(); // 인증서 내용과 호스트네임을 검증합니다.
|
||||
|
||||
connection = connectionFactory.newConnection();
|
||||
channel = connection.createChannel();
|
||||
|
||||
// 익스체인지는 브로커가 메시지를 받는 곳입니다.
|
||||
channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.DIRECT, true);
|
||||
|
||||
// 큐는 브로커가 메시지를 보내는 곳입니다.
|
||||
channel.queueDeclare(QUEUE, false, false, false, null);
|
||||
|
||||
// 익스체인지와 큐를 묶습니다.
|
||||
channel.queueBind(QUEUE, EXCHANGE, ROUTING_KEY);
|
||||
|
||||
// 큐로부터 메시지를 받습니다.
|
||||
channel.basicConsume(QUEUE, true, ROUTING_KEY, new DefaultConsumer(channel) {
|
||||
@Override
|
||||
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
|
||||
log.info("Rx: {}", new String(body, StandardCharsets.UTF_8));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void publish(String message) throws IOException {
|
||||
// 익스체인지에 메시지를 보냅니다.
|
||||
channel.basicPublish(EXCHANGE, ROUTING_KEY, null, message.getBytes(StandardCharsets.UTF_8));
|
||||
log.info("Tx: {}", message);
|
||||
}
|
||||
|
||||
public void close() throws IOException, TimeoutException {
|
||||
channel.close();
|
||||
connection.close();
|
||||
}
|
||||
|
||||
public static void main(String... args) throws IOException, TimeoutException, UnrecoverableKeyException, CertificateException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
|
||||
HelloRabbit helloRabbit = new HelloRabbit();
|
||||
for (int i = 0; i < 10; i++) {
|
||||
helloRabbit.publish("Hello, " + i);
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted..", e);
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(10000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted..", e);
|
||||
}
|
||||
helloRabbit.close();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples.tls;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
public final class TlsHelper {
|
||||
private TlsHelper(){}
|
||||
|
||||
/**
|
||||
* SSL Context를 만든 다음, 소켓 팩토리를 가져옵니다.
|
||||
*
|
||||
* @param keyStoreInputStream 키스토어
|
||||
* @param keyStorePassword 키스토어 비번
|
||||
* @param keyPassword 키 비번
|
||||
* @param trustStoreInputStream CA 인증서 키 스토어
|
||||
* @param trustStorePassword 트러스트 스토어 비번
|
||||
* @return
|
||||
* @throws KeyStoreException
|
||||
* @throws CertificateException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws IOException
|
||||
* @throws UnrecoverableKeyException
|
||||
* @throws KeyManagementException
|
||||
*/
|
||||
public static SSLContext context(InputStream keyStoreInputStream, char[] keyStorePassword, char[] keyPassword,
|
||||
InputStream trustStoreInputStream, char[] trustStorePassword)
|
||||
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException,
|
||||
KeyManagementException {
|
||||
// OpenSSL로 키와 인증서를 만들고, PKCS12 키 저장소에 넣었습니다.
|
||||
KeyStore keyStore = KeyStore.getInstance("PKCS12");
|
||||
// 클라이언트 키와 인증서가 들어있습니다.
|
||||
keyStore.load(keyStoreInputStream,
|
||||
keyStorePassword); // 키스토어 비번
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
kmf.init(keyStore, keyPassword); // 키 비번
|
||||
|
||||
KeyStore trustKeyStore = KeyStore.getInstance("PKCS12");
|
||||
// CA 인증서가 들어있습니다.
|
||||
trustKeyStore.load(trustStoreInputStream,
|
||||
trustStorePassword); // 키 스토어 비번
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
tmf.init(trustKeyStore);
|
||||
|
||||
SSLContext context = SSLContext.getInstance("TLSv1.3");
|
||||
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param trustStoreInputStream CA 인증서 키 스토어
|
||||
* @param trustStorePassword 트러스트 스토어 비번
|
||||
* @return
|
||||
* @throws KeyStoreException
|
||||
* @throws CertificateException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws IOException
|
||||
* @throws UnrecoverableKeyException
|
||||
* @throws KeyManagementException
|
||||
*/
|
||||
public static SSLContext context(InputStream trustStoreInputStream, char[] trustStorePassword)
|
||||
throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, UnrecoverableKeyException,
|
||||
KeyManagementException {
|
||||
// OpenSSL로 키와 인증서를 만들고, PKCS12 키 저장소에 넣었습니다.
|
||||
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
// 클라이언트 키와 인증서가 들어있습니다.
|
||||
keyStore.load(null, null); // 키스토어 비번
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
kmf.init(keyStore, null); // 키 비번
|
||||
|
||||
KeyStore trustKeyStore = KeyStore.getInstance("PKCS12");
|
||||
// CA 인증서가 들어있습니다.
|
||||
trustKeyStore.load(trustStoreInputStream,
|
||||
trustStorePassword); // 키 스토어 비번
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
tmf.init(trustKeyStore);
|
||||
|
||||
SSLContext context = SSLContext.getInstance("TLSv1.3");
|
||||
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||
|
||||
return context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples.tls;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.openssl.PEMDecryptorProvider;
|
||||
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
|
||||
import org.bouncycastle.openssl.PEMKeyPair;
|
||||
import org.bouncycastle.openssl.PEMParser;
|
||||
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
|
||||
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
|
||||
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public final class TlsHelperWithBouncyCastle {
|
||||
private TlsHelperWithBouncyCastle(){}
|
||||
|
||||
public static SSLContext context(InputStream clientCrtInputStream, InputStream clientKeyInputStream, char[] clientPassword,
|
||||
InputStream caCrtInputStream)
|
||||
throws KeyStoreException, IOException, CertificateException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
|
||||
// CA 인증서 불러오기
|
||||
CertificateFactory caCertFactory = CertificateFactory.getInstance("X.509");
|
||||
PEMParser parser = new PEMParser(new InputStreamReader(caCrtInputStream));
|
||||
X509Certificate caCert = (X509Certificate) caCertFactory
|
||||
.generateCertificate(new ByteArrayInputStream(parser.readPemObject().getContent()));
|
||||
parser.close();
|
||||
|
||||
// 클라이언트 인증서 불러오기
|
||||
CertificateFactory clientCertFactory = CertificateFactory.getInstance("X.509");
|
||||
parser = new PEMParser(new InputStreamReader(clientCrtInputStream));
|
||||
X509Certificate clientCert = (X509Certificate) clientCertFactory
|
||||
.generateCertificate(new ByteArrayInputStream(parser.readPemObject().getContent()));
|
||||
parser.close();
|
||||
|
||||
// 클라이언트 비밀키 불러오기
|
||||
parser = new PEMParser(new InputStreamReader(clientKeyInputStream));
|
||||
Object object = parser.readObject();
|
||||
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
|
||||
KeyPair keyPair;
|
||||
if (object instanceof PEMEncryptedKeyPair) {
|
||||
// 암호화된 키라면 패스워드가 필요하다.
|
||||
PEMEncryptedKeyPair pemKeyPair = (PEMEncryptedKeyPair) object;
|
||||
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(clientPassword);
|
||||
keyPair = converter.getKeyPair(pemKeyPair.decryptKeyPair(decProv));
|
||||
} else {
|
||||
// 암호화되지 않은 키라면 비밀번호가 필요없다.
|
||||
PEMKeyPair pemKeyPair = (PEMKeyPair) object;
|
||||
keyPair = converter.getKeyPair(pemKeyPair);
|
||||
}
|
||||
parser.close();
|
||||
PrivateKey clientKey = keyPair.getPrivate();
|
||||
|
||||
// CA 인증서를 사용해서 트러스트 스토어를 만든다.
|
||||
KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
caKeyStore.load(null, null);
|
||||
caKeyStore.setCertificateEntry("ca-certificate", caCert);
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(caKeyStore);
|
||||
|
||||
// 클라이언트 인증서와 비밀키를 사용해서 키 스토어를 만든다.
|
||||
KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
clientKeyStore.load(null, null);
|
||||
clientKeyStore.setCertificateEntry("certificate", clientCert);
|
||||
clientKeyStore.setKeyEntry("private-key", clientKey, clientPassword, new java.security.cert.Certificate[]{clientCert});
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
keyManagerFactory.init(clientKeyStore, clientPassword);
|
||||
|
||||
SSLContext context = SSLContext.getInstance("TLSv1.3");
|
||||
context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* BouncyCastle을 이용.
|
||||
*
|
||||
* @param caCrtInputStream ca cert pem
|
||||
* @return
|
||||
* @throws KeyStoreException
|
||||
* @throws IOException
|
||||
* @throws CertificateException
|
||||
* @throws UnrecoverableKeyException
|
||||
* @throws KeyManagementException
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
public static SSLContext context(InputStream caCrtInputStream)
|
||||
throws KeyStoreException, IOException, CertificateException, UnrecoverableKeyException, KeyManagementException, NoSuchAlgorithmException {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
|
||||
// CA 인증서 불러오기
|
||||
CertificateFactory caCertFactory = CertificateFactory.getInstance("X.509");
|
||||
PEMParser parser = new PEMParser(new InputStreamReader(caCrtInputStream));
|
||||
X509Certificate caCert = (X509Certificate) caCertFactory
|
||||
.generateCertificate(new ByteArrayInputStream(parser.readPemObject().getContent()));
|
||||
parser.close();
|
||||
|
||||
// CA 인증서를 사용해서 트러스트 스토어를 만든다.
|
||||
KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
caKeyStore.load(null, null);
|
||||
caKeyStore.setCertificateEntry("ca-certificate", caCert);
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
trustManagerFactory.init(caKeyStore);
|
||||
|
||||
// 클라이언트 인증서와 비밀키는 없으므로, 그냥 만든다.
|
||||
KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
clientKeyStore.load(null, null);
|
||||
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
keyManagerFactory.init(clientKeyStore, null);
|
||||
|
||||
SSLContext context = SSLContext.getInstance("TLSv1.3");
|
||||
context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
|
||||
return context;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples.topic;
|
||||
|
||||
import com.rabbitmq.client.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* 토픽 익스체인지는 라우팅-키를 패턴으로 사용한다.
|
||||
*
|
||||
* @author Elex
|
||||
* @see "https://www.rabbitmq.com/tutorials/tutorial-five-java.html"
|
||||
*/
|
||||
@Slf4j
|
||||
public class RabbitClient {
|
||||
private static final String EXCHANGE = "elex.topic.exchange";
|
||||
|
||||
private String name;
|
||||
|
||||
private Connection connection;
|
||||
private Channel channel;
|
||||
private String queue;
|
||||
|
||||
RabbitClient(String name) throws IOException, TimeoutException {
|
||||
this.name = name;
|
||||
|
||||
ConnectionFactory connectionFactory = new ConnectionFactory();
|
||||
connectionFactory.setHost("localhost");
|
||||
connectionFactory.setPort(5672);
|
||||
connectionFactory.setUsername("elex");
|
||||
connectionFactory.setPassword("test");
|
||||
connectionFactory.setVirtualHost("/");
|
||||
|
||||
connection = connectionFactory.newConnection();
|
||||
channel = connection.createChannel();
|
||||
|
||||
// topic은 routing-key를 패턴으로 사용합니다.
|
||||
channel.exchangeDeclare(EXCHANGE, BuiltinExchangeType.TOPIC, false);
|
||||
|
||||
// 큐 이름을 랜덤으로 생성합니다.
|
||||
queue = channel.queueDeclare().getQueue();
|
||||
|
||||
}
|
||||
|
||||
public void consume(String topic) throws IOException {
|
||||
// 익스체인지와 큐를 묶습니다.
|
||||
channel.queueBind(queue, EXCHANGE, topic);
|
||||
// 큐로부터 메시지를 받습니다.
|
||||
channel.basicConsume(queue, true, queue, new DefaultConsumer(channel) {
|
||||
@Override
|
||||
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
|
||||
log.info("Rx: [{}] {} : {}", name, envelope.getRoutingKey(), new String(body, StandardCharsets.UTF_8));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void publish(String topic, String message) throws IOException {
|
||||
// 익스체인지에 메시지를 보냅니다.
|
||||
channel.basicPublish(EXCHANGE, topic, null, message.getBytes(StandardCharsets.UTF_8));
|
||||
log.info("Tx: [{}] {} : {}", name, topic, message);
|
||||
}
|
||||
|
||||
public void close() throws IOException, TimeoutException {
|
||||
channel.close();
|
||||
connection.close();
|
||||
}
|
||||
|
||||
public static void main(String... args) throws IOException, TimeoutException {
|
||||
RabbitClient producer = new RabbitClient("Producer");
|
||||
RabbitClient consumer1 = new RabbitClient("Consumer1");
|
||||
RabbitClient consumer2 = new RabbitClient("Consumer2");
|
||||
consumer1.consume("message.apple.#");
|
||||
consumer2.consume("message.#");
|
||||
|
||||
producer.publish("message.hello", "Hello, there.");
|
||||
producer.publish("message.apple", "Hello, apple.");
|
||||
producer.publish("message.banana", "Hello, banana.");
|
||||
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("Interrupted..", e);
|
||||
}
|
||||
producer.close();
|
||||
consumer1.close();
|
||||
consumer2.close();
|
||||
/*
|
||||
11:29:47.699 [main] INFO kr.pe.elex.rabbitmq.topic.RabbitClient - Tx: [Producer] message.hello : Hello, there.
|
||||
11:29:47.700 [pool-5-thread-4] INFO kr.pe.elex.rabbitmq.topic.RabbitClient - Rx: [Consumer2] message.hello : Hello, there.
|
||||
11:29:47.703 [main] INFO kr.pe.elex.rabbitmq.topic.RabbitClient - Tx: [Producer] message.apple : Hello, apple.
|
||||
11:29:47.703 [main] INFO kr.pe.elex.rabbitmq.topic.RabbitClient - Tx: [Producer] message.banana : Hello, banana.
|
||||
11:29:47.704 [pool-5-thread-5] INFO kr.pe.elex.rabbitmq.topic.RabbitClient - Rx: [Consumer2] message.apple : Hello, apple.
|
||||
11:29:47.704 [pool-5-thread-5] INFO kr.pe.elex.rabbitmq.topic.RabbitClient - Rx: [Consumer2] message.banana : Hello, banana.
|
||||
11:29:47.704 [pool-3-thread-4] INFO kr.pe.elex.rabbitmq.topic.RabbitClient - Rx: [Consumer1] message.apple : Hello, apple.
|
||||
*/
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,13 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
rootProject.name = "java-examples"
|
||||
include("java-web-token", "mockito",
|
||||
"mosquitto", "rabbit-mq", "ssh", "web-socket")
|
||||
include("json-web-token", "mockito",
|
||||
"mosquitto", "rabbit-mq",
|
||||
"ssh",
|
||||
"web-socket-servlet","web-socket-client",
|
||||
"thread")
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("elex-java")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
// https://mvnrepository.com/artifact/com.jcraft/jsch
|
||||
implementation("com.jcraft:jsch:0.1.55")
|
||||
}
|
||||
|
||||
89
ssh/src/main/java/kr/pe/elex/examples/SecureShell.java
Normal file
89
ssh/src/main/java/kr/pe/elex/examples/SecureShell.java
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import com.jcraft.jsch.*;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class SecureShell implements Closeable {
|
||||
|
||||
private final Session session;
|
||||
|
||||
public SecureShell(String host, int port, String user, String password) throws JSchException {
|
||||
JSch jSch = new JSch();
|
||||
session = jSch.getSession(user, host, port);
|
||||
session.setPassword(password);
|
||||
session.setUserInfo(new UserInfo() {
|
||||
@Override
|
||||
public String getPassphrase() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean promptPassword(String message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean promptPassphrase(String message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean promptYesNo(String message) {
|
||||
System.out.println(message);
|
||||
// user type 'yes'
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showMessage(String message) {
|
||||
System.out.println(message);
|
||||
}
|
||||
});
|
||||
session.connect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
session.disconnect();
|
||||
}
|
||||
|
||||
public ChannelShell shell(InputStream is, OutputStream os) throws JSchException {
|
||||
ChannelShell channel = (ChannelShell) session.openChannel("shell");
|
||||
channel.setInputStream(is);
|
||||
channel.setOutputStream(os);
|
||||
channel.setAgentForwarding(true);
|
||||
channel.setPtyType("vt100");
|
||||
channel.connect();
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
public ChannelExec exec(String command) throws JSchException {
|
||||
ChannelExec channel = (ChannelExec) session.openChannel("exec");
|
||||
channel.setCommand(command);
|
||||
channel.setInputStream(null);
|
||||
channel.setErrStream(System.err);
|
||||
channel.connect();
|
||||
return channel;
|
||||
}
|
||||
|
||||
public void portForwardingL(int portL, String hostR, int portR) throws JSchException {
|
||||
session.setPortForwardingL(portL, hostR, portR);
|
||||
}
|
||||
|
||||
}
|
||||
8
ssh/src/main/java/kr/pe/elex/examples/package-info.java
Normal file
8
ssh/src/main/java/kr/pe/elex/examples/package-info.java
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
67
ssh/src/test/java/kr/pe/elex/examples/SecureShellTest.java
Normal file
67
ssh/src/test/java/kr/pe/elex/examples/SecureShellTest.java
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import com.elex_project.abraxas.IOz;
|
||||
import com.jcraft.jsch.ChannelExec;
|
||||
import com.jcraft.jsch.ChannelShell;
|
||||
import com.jcraft.jsch.JSchException;
|
||||
import jdk.nashorn.internal.ir.annotations.Ignore;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@Slf4j
|
||||
class SecureShellTest {
|
||||
|
||||
private static final String host = "192.168.0.1";
|
||||
private static final String user = "elex";
|
||||
private static final String password = "test";
|
||||
private static final int port = 22;
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
void shell() throws JSchException {
|
||||
SecureShell ssh = new SecureShell(host, port, user, password);
|
||||
|
||||
ChannelShell shell = ssh.shell(System.in, System.out);
|
||||
|
||||
// ???
|
||||
}
|
||||
|
||||
@Test
|
||||
void exec() throws JSchException, IOException {
|
||||
SecureShell ssh = new SecureShell(host, port, user, password);
|
||||
|
||||
ChannelExec channel = ssh.exec("ls -al");
|
||||
String out = IOz.readStringFrom(channel.getInputStream(),"\n");
|
||||
System.out.println(out);
|
||||
|
||||
if (channel.isClosed()){
|
||||
channel.disconnect();
|
||||
}
|
||||
ssh.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void scpTo() throws JSchException, IOException {
|
||||
String filename = "";
|
||||
SecureShell ssh = new SecureShell(host, port, user, password);
|
||||
|
||||
ChannelExec channel = ssh.exec("scp -p -t "+ filename);
|
||||
InputStream is = channel.getInputStream();
|
||||
OutputStream os = channel.getOutputStream();
|
||||
|
||||
// ???
|
||||
}
|
||||
}
|
||||
14
thread/build.gradle.kts
Normal file
14
thread/build.gradle.kts
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("elex-java")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
}
|
||||
38
thread/src/main/java/kr/pe/elex/examples/Sample.java
Normal file
38
thread/src/main/java/kr/pe/elex/examples/Sample.java
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.Period;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
public class Sample {
|
||||
public static void main(String... args) throws ExecutionException, InterruptedException {
|
||||
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
LocalDateTime startTime = LocalDateTime.of(2021, 12, 25, 0, 0);
|
||||
executor.scheduleAtFixedRate(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
}
|
||||
},
|
||||
Duration.between(LocalDateTime.now(), startTime).abs().get(ChronoUnit.MILLIS),
|
||||
Period.ofDays(1).get(ChronoUnit.MILLIS),
|
||||
TimeUnit.MILLISECONDS);
|
||||
|
||||
}
|
||||
}
|
||||
14
thread/src/test/java/kr/pe/elex/examples/ExecutorTest.java
Normal file
14
thread/src/test/java/kr/pe/elex/examples/ExecutorTest.java
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class ExecutorTest {
|
||||
}
|
||||
16
web-socket-client/build.gradle.kts
Normal file
16
web-socket-client/build.gradle.kts
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("elex-java")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("javax.websocket:javax.websocket-client-api:1.1")
|
||||
implementation("org.eclipse.jetty.websocket:javax-websocket-client-impl:9.4.43.v20210629")
|
||||
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.websocket.*;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
@Slf4j
|
||||
@ClientEndpoint
|
||||
public class WebSocketClient {
|
||||
private Session session = null;
|
||||
private WebSocketMessageHandler messageHandler;
|
||||
|
||||
public WebSocketClient(URI endpointURI) {
|
||||
try {
|
||||
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
|
||||
container.connectToServer(this, endpointURI);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback hook for Connection open events.
|
||||
*
|
||||
* @param session the userSession which is opened.
|
||||
*/
|
||||
@OnOpen
|
||||
public void onOpen(@NotNull Session session) throws IOException {
|
||||
log.info("opening websocket");
|
||||
this.session = session;
|
||||
//session.getBasicRemote().sendText("Hi~~");
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback hook for Connection close events.
|
||||
*
|
||||
* @param session the userSession which is getting closed.
|
||||
* @param reason the reason for connection close
|
||||
*/
|
||||
@OnClose
|
||||
public void onClose(Session session, CloseReason reason) {
|
||||
log.info("closing websocket");
|
||||
this.session = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback hook for Message Events. This method will be invoked when a client send a message.
|
||||
*
|
||||
* @param message The text message
|
||||
*/
|
||||
@OnMessage
|
||||
public void onMessage(String message) {
|
||||
if (this.messageHandler != null) {
|
||||
this.messageHandler.handleMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
@OnMessage
|
||||
public void onMessage(PongMessage message) {
|
||||
if (this.messageHandler != null) {
|
||||
this.messageHandler.handleMessage("pong: " + message.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* register message handler
|
||||
*
|
||||
* @param msgHandler
|
||||
*/
|
||||
public void setMessageHandler(WebSocketMessageHandler msgHandler) {
|
||||
this.messageHandler = msgHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message.
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
public void sendMessage(String message) throws IOException {
|
||||
this.session.getBasicRemote().sendText(message);
|
||||
}
|
||||
|
||||
public void sendMessage(ByteBuffer message) throws IOException {
|
||||
this.session.getBasicRemote().sendBinary(message);
|
||||
}
|
||||
|
||||
public void sendMessageAsync(String message) {
|
||||
this.session.getAsyncRemote().sendText(message);
|
||||
}
|
||||
|
||||
public void sendMessageAsync(ByteBuffer message) {
|
||||
this.session.getAsyncRemote().sendBinary(message);
|
||||
}
|
||||
|
||||
public static void main(String... args) throws URISyntaxException, IOException {
|
||||
WebSocketClient client = new WebSocketClient(new URI("ws://localhost:8080/websocket"));
|
||||
client.setMessageHandler(new WebSocketMessageHandler() {
|
||||
@Override
|
||||
public void handleMessage(final String message) {
|
||||
log.info("Rx: {}", message);
|
||||
}
|
||||
});
|
||||
client.sendMessage("Hello");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
/**
|
||||
* Message handler.
|
||||
*
|
||||
*/
|
||||
public interface WebSocketMessageHandler {
|
||||
public void handleMessage(String message);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
15
web-socket-servlet/build.gradle.kts
Normal file
15
web-socket-servlet/build.gradle.kts
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
plugins {
|
||||
id("elex-war")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
providedCompile ("javax.servlet:javax.servlet-api:4.0.1")
|
||||
implementation("javax.websocket:javax.websocket-api:1.1")
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.websocket.*;
|
||||
import javax.websocket.server.ServerEndpoint;
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@ServerEndpoint("/")
|
||||
public class WebSocketServlet {
|
||||
private static final Set<Session> sessions;
|
||||
|
||||
static {
|
||||
sessions = new HashSet<>();
|
||||
}
|
||||
|
||||
@OnOpen
|
||||
public void onOpen(@NotNull Session session) throws IOException {
|
||||
sessions.add(session);//session.
|
||||
session.getBasicRemote().sendText("Hello");
|
||||
session.getUserProperties().put("createdOn", LocalDateTime.now());
|
||||
}
|
||||
|
||||
@OnClose
|
||||
public void onClose(Session session) {
|
||||
sessions.remove(session);
|
||||
session = null;
|
||||
}
|
||||
|
||||
@OnMessage
|
||||
public void onMessage(@NotNull Session session, String message) throws IOException {
|
||||
session.getBasicRemote().sendText("you said, " + message);
|
||||
}
|
||||
|
||||
@OnError
|
||||
public void onError(Session session, Throwable e) {
|
||||
|
||||
}
|
||||
|
||||
public static void broadcast(final String message){
|
||||
sessions.forEach(session -> {
|
||||
if (null!=session && session.isOpen()) {
|
||||
session.getAsyncRemote().sendText(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
* Examples for Java
|
||||
*
|
||||
* Copyright (c) 2021. Elex. All Rights Reserved.
|
||||
* https://www.elex-project.com/
|
||||
*/
|
||||
|
||||
package kr.pe.elex.examples;
|
||||
@@ -1,7 +0,0 @@
|
||||
plugins {
|
||||
id("elex-java")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user