2021-08-05

This commit is contained in:
2021-08-05 11:46:27 +09:00
parent 2a56dff104
commit 438bf4bd5c
58 changed files with 2418 additions and 33 deletions

8
README.md Normal file
View File

@@ -0,0 +1,8 @@
# Java Examples
-----
programmed by Elex
https://www.elex-project.com/

View File

@@ -1,3 +1,10 @@
/*
* Examples for Java
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
buildscript {
repositories {
maven {

View File

@@ -1,3 +1,10 @@
/*
* Examples for Java
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins{
`kotlin-dsl`
}

View File

@@ -1,3 +1,10 @@
/*
* Examples for Java
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins {
id("elex-base")

View File

@@ -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")
}

View File

@@ -1,3 +1,10 @@
/*
* Examples for Java
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins {
id ("elex-base")
}

View File

@@ -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`

View 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 {
}

View File

@@ -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
View File

@@ -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/
#
##############################################################################

View File

@@ -1,7 +0,0 @@
plugins {
id("elex-library")
}
dependencies {
}

View File

@@ -1 +0,0 @@
package kr.pe.elex.examples;

View 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")
}

View File

@@ -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);
}
}

View 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;

View File

@@ -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());
}
}

View File

@@ -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" />

View File

@@ -1,3 +1,10 @@
/*
* Examples for Java
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
plugins {
id("elex-java")
}

View File

@@ -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;

View File

@@ -1 +1,8 @@
/*
* Examples for Java
*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
View 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/

View File

@@ -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")
}

View 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();
}
}

View 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();
}
}

View File

@@ -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();
}
}

View 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;

62
rabbit-mq/README.md Normal file
View 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/

View File

@@ -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")
}

View File

@@ -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
*/
}
}

View File

@@ -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
*/
}
}

View File

@@ -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();
}
}

View File

@@ -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
*/
}
}

View File

@@ -0,0 +1,6 @@
/*
* Copyright (c) 2021. Elex. All Rights Reserved.
* https://www.elex-project.com/
*/
package kr.pe.elex.examples;

View 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();
}
}

View File

@@ -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);
}
}

View 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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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.
*/
}
}

View File

@@ -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")

View File

@@ -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")
}

View 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);
}
}

View 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;

View 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
View 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 {
}

View 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);
}
}

View 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 {
}

View 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")
}

View File

@@ -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");
}
}

View File

@@ -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);
}

View 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;

View 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")
}

View File

@@ -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);
}
});
}
}

View 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;

View File

@@ -1,7 +0,0 @@
plugins {
id("elex-java")
}
dependencies {
}