Merge remote-tracking branch 'origin/jenkins_with_weblogic' into jenkins_with_weblogic
# Conflicts: # src/test/java/com/eactive/ext/kjb/ums/test/KjbEmailSendModuleTests.java
This commit is contained in:
@@ -1 +1,4 @@
|
|||||||
build/
|
build/
|
||||||
|
.settings
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
|||||||
@@ -7,3 +7,13 @@ eapim-admin 에서 포함하여 테스트 이용시 느린 부팅 속도 떄문
|
|||||||
## 주요 기능
|
## 주요 기능
|
||||||
- EAI 배치 연계
|
- EAI 배치 연계
|
||||||
- 고객이메일발송 시스템 연계
|
- 고객이메일발송 시스템 연계
|
||||||
|
|
||||||
|
## 테스트
|
||||||
|
```bash
|
||||||
|
# git pull
|
||||||
|
GIT_SSH_COMMAND='ssh -i ~/.ssh/id_rsa-jenkins' git pull origin jenkins_with_weblogic
|
||||||
|
|
||||||
|
# test run
|
||||||
|
KJB_EAPIM_TEST_ENABLED=TRUE kjb-gradle.sh :eapim-admin-kjb:test --tests "com.eactive.ext.kjb.ums.test.UmsTest.testSms" -Psafedb
|
||||||
|
KJB_EAPIM_TEST_ENABLED=TRUE kjb-gradle.sh :eapim-admin-kjb:test --tests "com.eactive.ext.kjb.ums.test.UmsTest.testEmail" -Psafedb
|
||||||
|
```
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/main/
|
||||||
|
/test/
|
||||||
+1
-1
@@ -82,7 +82,7 @@ jar {
|
|||||||
test {
|
test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
|
|
||||||
systemProperty "kjb_safedb.mode", project.findProperty("kjb_safedb.mode") ?: ""
|
systemProperty "kjb_safedb.mode", System.getProperty("kjb_safedb.mode")
|
||||||
|
|
||||||
if (project.hasProperty("safedb")) {
|
if (project.hasProperty("safedb")) {
|
||||||
systemProperty "kjb_safedb.mode", "real"
|
systemProperty "kjb_safedb.mode", "real"
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.eactive.ext.kjb.common;
|
||||||
|
|
||||||
|
public class KjbEaiBatchException extends Exception {
|
||||||
|
public KjbEaiBatchException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KjbEaiBatchException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,24 +3,70 @@ package com.eactive.ext.kjb.common;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.hibernate.validator.constraints.NotEmpty;
|
import org.hibernate.validator.constraints.NotEmpty;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Max;
|
||||||
|
import javax.validation.constraints.Min;
|
||||||
import javax.validation.constraints.Pattern;
|
import javax.validation.constraints.Pattern;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class KjbProperty {
|
public class KjbProperty {
|
||||||
@Pattern(regexp = "^(https?://[^\\s/$.?#][^\\s]*)|\\s*$", message = "kjb.ums.eai_batch.url 는 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api")
|
@KjbPropertyValue(key = "kjb.eai.batch.url")
|
||||||
|
@Pattern(regexp = "^(https?://[^\\s/$.?#][^\\s]*)|\\s*$", message = "kjb.eai_batch.url 는 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api")
|
||||||
private String eaiBatchUrl;
|
private String eaiBatchUrl;
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.eai.batch.connection_timeout", defaultValue = "5")
|
||||||
|
@Min(1) @Max(10)
|
||||||
|
private int eaiBatchConnectionTimeout = 5;
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.eai.batch.response_timeout", defaultValue = "5")
|
||||||
|
@Min(1) @Max(30)
|
||||||
|
private int eaiBatchResponseTimeout = 5;
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.eai.batch.timeout", defaultValue = "10")
|
||||||
|
@Min(1) @Max(30)
|
||||||
|
private int eaiBatchTimeout = 10;
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.eai.batch.charset", defaultValue = "euc-kr")
|
||||||
|
@NotEmpty
|
||||||
|
private String eaiBatchCharset = "euc-kr";
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.ums.eai_batch.send_dir")
|
||||||
@NotEmpty(message = "kjb.ums.eai_batch.send_dir 는 필수값입니다.")
|
@NotEmpty(message = "kjb.ums.eai_batch.send_dir 는 필수값입니다.")
|
||||||
@Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$",
|
private String umsEaiBatchSendDir;
|
||||||
message = "kjb.ums.eai_batch.send_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/snd 또는 C:\\Data\\eapim\\portal\\sendmail\\snd")
|
|
||||||
private String eaiBatchSendDir;
|
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.ums.eai_batch.backup_dir")
|
||||||
@NotEmpty(message = "kjb.ums.eai_batch.backup_dir 는 필수값입니다.")
|
@NotEmpty(message = "kjb.ums.eai_batch.backup_dir 는 필수값입니다.")
|
||||||
@Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$",
|
private String umsEaiBatchBackupDir;
|
||||||
message = "kjb.ums.eai_batch.backup_dir 은 절대 경로여야 합니다. 예: /Data/eapim/portal/sendmail/bak 또는 C:\\Data\\eapim\\portal\\sendmail\\bak")
|
|
||||||
private String eaiBatchBackupDir;
|
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.ums.eai_batch.error_dir")
|
||||||
|
@NotEmpty(message = "kjb.ums.eai_batch.error_dir 는 필수값입니다.")
|
||||||
|
private String umsEaiBatchErrorDir;
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.ums.eai_batch.interface_id")
|
||||||
@Pattern(regexp = "^[a-zA-Z0-9]{13}$", message = "kjb.ums.eai_batch.interface_id 는 12자리 영문대소문자 및 숫자 조합이어야 합니다.")
|
@Pattern(regexp = "^[a-zA-Z0-9]{13}$", message = "kjb.ums.eai_batch.interface_id 는 12자리 영문대소문자 및 숫자 조합이어야 합니다.")
|
||||||
private String eaiBatchInterfaceId;
|
private String umsEaiBatchInterfaceId;
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.ums.eai_batch.retention_date", defaultValue = "60")
|
||||||
|
@Min(1) @Max(365)
|
||||||
|
private int umsEaiBatchRetentionDate = 60;
|
||||||
|
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.ums.host")
|
||||||
|
@Pattern(regexp = "^(https?://[^\\s/$.?#][^\\s]*)|\\s*$", message = "kjb.ums.host_url 는 유효한 URL 형식이거나 공백이어야 합니다. 예: http://example.com/api")
|
||||||
|
private String umsHostUrl;
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.ums.api_key")
|
||||||
|
@Pattern(regexp = "^[a-zA-Z0-9_-]{36}$", message = "kjb.ums.api_key 입력 형식이 맞지 않습니다.")
|
||||||
|
private String umsApiKey;
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.ums.connection_timeout", defaultValue = "5")
|
||||||
|
@Min(1) @Max(10)
|
||||||
|
private int umsConnectionTimeout = 5;
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.ums.response_timeout", defaultValue = "5")
|
||||||
|
@Min(1) @Max(30)
|
||||||
|
private int umsResponseTimeout = 5;
|
||||||
|
|
||||||
|
@KjbPropertyValue(key = "kjb.ums.timeout", defaultValue = "10")
|
||||||
|
@Min(1) @Max(30)
|
||||||
|
private int umsTimeout = 10;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.eactive.ext.kjb.common;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target(ElementType.FIELD)
|
||||||
|
public @interface KjbPropertyValue {
|
||||||
|
String key();
|
||||||
|
String defaultValue() default "";
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package com.eactive.ext.kjb.common;
|
||||||
|
|
||||||
|
public class KjbUmsException extends Exception {
|
||||||
|
public KjbUmsException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KjbUmsException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.eactive.ext.kjb.eai;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.hibernate.validator.constraints.NotEmpty;
|
||||||
|
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
import java.io.File;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Slf4j
|
||||||
|
public class EaiBatchInterface {
|
||||||
|
@Pattern(regexp = "^[a-zA-Z0-9]{13}$", message = "id는 12자리 영문대소문자 및 숫자 조합이어야 합니다.")
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@NotEmpty
|
||||||
|
@Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", message = "sendDir은 절대경로로 지정되어야 합니다.")
|
||||||
|
private String sendDir;
|
||||||
|
|
||||||
|
@NotEmpty
|
||||||
|
@Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", message = "backupDir은 절대경로로 지정되어야 합니다.")
|
||||||
|
private String backupDir;
|
||||||
|
|
||||||
|
@NotEmpty
|
||||||
|
@Pattern(regexp = "^(?:[a-zA-Z]:\\\\|\\\\|\\/)(?:[\\w\\-\\.]+[\\\\\\/])*[\\w\\-\\.]+$", message = "errorDir은 절대경로로 지정되어야 합니다.")
|
||||||
|
private String errorDir;
|
||||||
|
|
||||||
|
|
||||||
|
public void moveError(String filename) {
|
||||||
|
move(filename, errorDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void moveBackup(String filename) {
|
||||||
|
move(filename, backupDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void move(String filename, String targetDir) {
|
||||||
|
File file = Paths.get(sendDir, filename).toFile();
|
||||||
|
File targetDirFile = new File(targetDir);
|
||||||
|
File targetFile = new File(targetDir, file.getName());
|
||||||
|
|
||||||
|
log.debug("송신파일 이동 시작 - {} -> {}", filename, targetDir);
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
log.warn("송신 파일이 존재 하지 않습니다. ({})", file.getAbsolutePath());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetDirFile.exists()) {
|
||||||
|
if (!targetDirFile.mkdirs()) {
|
||||||
|
log.warn("대상 디렉토리 생성 실패 ({})", file.getAbsolutePath());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.renameTo(targetFile)) {
|
||||||
|
log.warn("송신 파일 이동 실패 - {}", targetFile.getAbsolutePath());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import lombok.Getter;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -17,6 +18,9 @@ public class EaiBatchMessage {
|
|||||||
@Getter
|
@Getter
|
||||||
private String count = "";
|
private String count = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 요청시 사용 안함, 응답시 1: 성공, 2: 실패
|
||||||
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
private String resultCode = "";
|
private String resultCode = "";
|
||||||
|
|
||||||
@@ -28,14 +32,17 @@ public class EaiBatchMessage {
|
|||||||
|
|
||||||
private boolean isReadOnly = false;
|
private boolean isReadOnly = false;
|
||||||
|
|
||||||
|
private String charset;
|
||||||
|
|
||||||
|
|
||||||
private EaiBatchMessage() {}
|
private EaiBatchMessage() {}
|
||||||
|
|
||||||
|
|
||||||
public static EaiBatchMessage createRequest(String interfaceId, String filename) {
|
public static EaiBatchMessage createRequest(String interfaceId, String filename, String charset) {
|
||||||
EaiBatchMessage message = new EaiBatchMessage();
|
EaiBatchMessage message = new EaiBatchMessage();
|
||||||
message.interfaceId = interfaceId;
|
message.interfaceId = interfaceId;
|
||||||
message.filename = filename;
|
message.filename = filename;
|
||||||
|
message.charset = charset;
|
||||||
|
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
@@ -46,16 +53,16 @@ public class EaiBatchMessage {
|
|||||||
message.isReadOnly = true;
|
message.isReadOnly = true;
|
||||||
|
|
||||||
if (rawMessage.length() >= 40) {
|
if (rawMessage.length() >= 40) {
|
||||||
message.interfaceId = rawMessage.substring(0, 20).trim();
|
message.interfaceId = rawMessage.substring(0, 19).trim();
|
||||||
message.baseDate = rawMessage.substring(20, 28).trim();
|
message.baseDate = rawMessage.substring(19, 27).trim();
|
||||||
message.count = rawMessage.substring(28, 38).trim();
|
message.count = rawMessage.substring(27, 37).trim();
|
||||||
message.resultCode = rawMessage.substring(38, 39).trim();
|
message.resultCode = rawMessage.substring(37, 38).trim();
|
||||||
// filename 은 @@가 발견되면 이전 까지 trim / 발견되지 않으면 나머지 전체 그리고 trim
|
// filename 은 @@가 발견되면 이전 까지 trim / 발견되지 않으면 나머지 전체 그리고 trim
|
||||||
int endIdx = rawMessage.indexOf("@@", 39);
|
int endIdx = rawMessage.indexOf("@@", 38);
|
||||||
if (endIdx == -1) {
|
if (endIdx == -1) {
|
||||||
message.filename = rawMessage.substring(39).trim();
|
message.filename = rawMessage.substring(38).trim();
|
||||||
} else {
|
} else {
|
||||||
message.filename = rawMessage.substring(39, endIdx).trim();
|
message.filename = rawMessage.substring(38, endIdx).trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,16 +81,20 @@ public class EaiBatchMessage {
|
|||||||
return rawMessage;
|
return rawMessage;
|
||||||
} else {
|
} else {
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(1024);
|
ByteBuffer buffer = ByteBuffer.allocate(1024);
|
||||||
// 0~19 : 인터페이스ID (20)
|
try {
|
||||||
buffer.put(fixedString(interfaceId, 20));
|
// 인터페이스ID (19)
|
||||||
// 19-8: 기준일자 (8)
|
buffer.put(fixedString(interfaceId, 19));
|
||||||
|
// 기준일자 (8)
|
||||||
buffer.put(fixedString(baseDate, 8));
|
buffer.put(fixedString(baseDate, 8));
|
||||||
// 27-10 : 건수 (10)
|
// 건수 (10)
|
||||||
buffer.put(fixedString(count, 10));
|
buffer.put(fixedString(count, 10));
|
||||||
// 37-1 : 결과코드 (1)
|
// 결과코드 (1)
|
||||||
buffer.put(fixedString(resultCode, 1));
|
buffer.put(fixedString(resultCode, 1));
|
||||||
// 38-100 : 파일명 (100)
|
// 파일명 (100)
|
||||||
buffer.put(fixedString(filename, 100));
|
buffer.put(fixedString(filename, 100));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
// 종료 구분자 (2)
|
// 종료 구분자 (2)
|
||||||
buffer.put("@@".getBytes());
|
buffer.put("@@".getBytes());
|
||||||
@@ -93,11 +104,11 @@ public class EaiBatchMessage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] fixedString(String str, int length) {
|
private byte[] fixedString(String str, int length) throws UnsupportedEncodingException {
|
||||||
if (str == null) {
|
if (str == null) {
|
||||||
str = "";
|
str = "";
|
||||||
}
|
}
|
||||||
byte[] strBytes = str.getBytes();
|
byte[] strBytes = str.getBytes(this.charset);
|
||||||
byte[] result = new byte[length];
|
byte[] result = new byte[length];
|
||||||
|
|
||||||
int copyLength = Math.min(strBytes.length, length);
|
int copyLength = Math.min(strBytes.length, length);
|
||||||
|
|||||||
@@ -1,20 +1,41 @@
|
|||||||
package com.eactive.ext.kjb.eai;
|
package com.eactive.ext.kjb.eai;
|
||||||
|
|
||||||
|
import com.eactive.ext.kjb.common.KjbEaiBatchException;
|
||||||
import com.eactive.ext.kjb.common.KjbProperty;
|
import com.eactive.ext.kjb.common.KjbProperty;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
||||||
|
import org.apache.hc.client5.http.config.RequestConfig;
|
||||||
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
||||||
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
||||||
import org.apache.hc.core5.http.io.entity.EntityUtils;
|
import org.apache.hc.core5.http.io.entity.EntityUtils;
|
||||||
|
import org.apache.hc.core5.net.URIBuilder;
|
||||||
|
import org.apache.hc.core5.util.Timeout;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class KjbEaiBatchModule {
|
public class KjbEaiBatchModule {
|
||||||
private final KjbProperty property;
|
private final KjbProperty property;
|
||||||
private final CloseableHttpClient httpClient;
|
private final CloseableHttpClient httpClient;
|
||||||
|
private final RequestConfig requestConfig;
|
||||||
|
private LocalDateTime lastFileRetentionCheckTime;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public KjbEaiBatchModule(@NotNull KjbProperty property) {
|
public KjbEaiBatchModule(@NotNull KjbProperty property) {
|
||||||
@@ -23,6 +44,10 @@ public class KjbEaiBatchModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.property = property;
|
this.property = property;
|
||||||
|
requestConfig = RequestConfig.custom()
|
||||||
|
.setConnectTimeout(Timeout.ofSeconds(property.getEaiBatchConnectionTimeout()))
|
||||||
|
.setResponseTimeout(Timeout.ofSeconds(property.getEaiBatchResponseTimeout()))
|
||||||
|
.build();
|
||||||
|
|
||||||
if (StringUtils.isNotEmpty(property.getEaiBatchUrl())) {
|
if (StringUtils.isNotEmpty(property.getEaiBatchUrl())) {
|
||||||
this.httpClient = HttpClients.createDefault();
|
this.httpClient = HttpClients.createDefault();
|
||||||
@@ -32,26 +57,110 @@ public class KjbEaiBatchModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public EaiBatchMessage call(String interfaceId, String filename) throws IOException {
|
public EaiBatchMessage call(@Valid EaiBatchInterface intf, String filename) throws KjbEaiBatchException {
|
||||||
EaiBatchMessage reqMessage = EaiBatchMessage.createRequest(interfaceId, filename);
|
EaiBatchMessage reqMessage = EaiBatchMessage.createRequest(intf.getId(), filename, property.getEaiBatchCharset());
|
||||||
log.debug("EAI 배치 호출: {}", reqMessage);
|
log.debug("EAI 배치 호출: {}", reqMessage);
|
||||||
|
|
||||||
|
// 송신 파일 체크
|
||||||
|
File sendFile = Paths.get(intf.getSendDir(), filename).toFile();
|
||||||
|
if (!sendFile.exists() || !sendFile.isFile()) {
|
||||||
|
throw new KjbEaiBatchException("Not found send file - " + sendFile.toPath());
|
||||||
|
}
|
||||||
|
|
||||||
if (this.httpClient != null) {
|
if (this.httpClient != null) {
|
||||||
String url = property.getEaiBatchUrl() + "?eaibatMsg=" + reqMessage.toString();
|
URI uri = null;
|
||||||
final HttpGet httpGet = new HttpGet(url);
|
try {
|
||||||
|
uri = new URIBuilder(property.getEaiBatchUrl())
|
||||||
|
.addParameter("eaibatMsg", reqMessage.toString())
|
||||||
|
.build();
|
||||||
|
} catch (URISyntaxException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
log.debug("EAI Batch call - {}", uri.toASCIIString());
|
||||||
|
|
||||||
String message = httpClient.execute(httpGet, response -> EntityUtils.toString(response.getEntity(), "EUC-KR"));
|
final HttpGet httpGet = new HttpGet(uri);
|
||||||
|
httpGet.setConfig(requestConfig);
|
||||||
|
|
||||||
EaiBatchMessage resMessage = EaiBatchMessage.fromResponse(message);
|
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
Future<String> future = executor.submit(() ->
|
||||||
|
httpClient.execute(httpGet, response -> EntityUtils.toString(response.getEntity(), "EUC-KR")));
|
||||||
|
|
||||||
|
EaiBatchMessage resMessage = null;
|
||||||
|
try {
|
||||||
|
String message = future.get(property.getEaiBatchTimeout(), TimeUnit.SECONDS);
|
||||||
|
resMessage = EaiBatchMessage.fromResponse(message);
|
||||||
log.debug("EAI 배치 응답: {}", resMessage);
|
log.debug("EAI 배치 응답: {}", resMessage);
|
||||||
|
|
||||||
return resMessage;
|
return resMessage;
|
||||||
|
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||||
|
log.error("EAI Batch call exception", e);
|
||||||
|
throw new KjbEaiBatchException("EAI Batch call failed", e);
|
||||||
|
} finally {
|
||||||
|
executor.shutdown();
|
||||||
|
|
||||||
|
// EAI 송신 실패일 경우 에러 경로로 이동
|
||||||
|
if (resMessage != null && "1".equals(resMessage.getResultCode())) {
|
||||||
|
intf.moveBackup(filename);
|
||||||
|
} else {
|
||||||
|
intf.moveError(filename);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.info("EAI_BATCH_URL 이 설정되지 않아 실제 호출하지 않음. 요청 메시지: {}", reqMessage);
|
log.info("EAI_BATCH_URL 이 설정되지 않아 실제 호출하지 않음. 요청 메시지: {}", reqMessage);
|
||||||
|
|
||||||
// 정상응답 리턴
|
// 정상응답 리턴
|
||||||
EaiBatchMessage resMessage = EaiBatchMessage.fromResponse(String.format("%-20s%-8s%-10s%-1s%s@@", interfaceId, "", "0000000000", "1", "S:" + filename));
|
EaiBatchMessage resMessage = EaiBatchMessage.fromResponse(String.format("%-19s%-8s%-10s%-1s%s@@", intf.getId(), "", "0000000000", "1", "S:" + filename));
|
||||||
log.debug("EAI 배치 응답(모의): {}", resMessage);
|
log.debug("EAI 배치 응답(모의): {}", resMessage);
|
||||||
|
intf.moveBackup(filename);
|
||||||
return resMessage;
|
return resMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1시간 주기로 파일 유지 및 삭제 처리
|
||||||
|
*/
|
||||||
|
public void processFileRetentionDays() {
|
||||||
|
if (lastFileRetentionCheckTime == null) {
|
||||||
|
lastFileRetentionCheckTime = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 현재 시간과 1시간 이상 차이 날때 동작
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
boolean isOver = Duration.between(lastFileRetentionCheckTime, now).abs().toHours() == 1;
|
||||||
|
|
||||||
|
if (!isOver) return;
|
||||||
|
|
||||||
|
this.walkDeletePath(property.getUmsEaiBatchSendDir());
|
||||||
|
this.walkDeletePath(property.getUmsEaiBatchBackupDir());
|
||||||
|
this.walkDeletePath(property.getUmsEaiBatchErrorDir());
|
||||||
|
|
||||||
|
lastFileRetentionCheckTime = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void walkDeletePath(String path) {
|
||||||
|
final int retentionDays = property.getUmsEaiBatchRetentionDate();
|
||||||
|
log.info("WalkDelete - {} / Retention days : {}", path, retentionDays);
|
||||||
|
|
||||||
|
Path dir = Paths.get(path);
|
||||||
|
LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
|
try (Stream<Path> paths = Files.walk(dir)) {
|
||||||
|
paths.sorted(Comparator.reverseOrder()).forEach(p -> {
|
||||||
|
try {
|
||||||
|
BasicFileAttributes attrs = Files.readAttributes(p, BasicFileAttributes.class);
|
||||||
|
LocalDateTime created = attrs.creationTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||||
|
|
||||||
|
long over = Math.abs(Duration.between(created, now).toDays());
|
||||||
|
if (over >= retentionDays) {
|
||||||
|
Files.deleteIfExists(p);
|
||||||
|
log.info("Delete over {} days - {}", retentionDays, p);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Delete fail - {}", p.getFileName().toString(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("Not found path : {}", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,13 @@ package com.eactive.ext.kjb.ums;
|
|||||||
import com.eactive.apim.portal.portaluser.entity.PortalUser;
|
import com.eactive.apim.portal.portaluser.entity.PortalUser;
|
||||||
import com.eactive.apim.portal.template.entity.MessageRequest;
|
import com.eactive.apim.portal.template.entity.MessageRequest;
|
||||||
import com.eactive.apim.portal.template.repository.MessageRequestRepository;
|
import com.eactive.apim.portal.template.repository.MessageRequestRepository;
|
||||||
|
import com.eactive.ext.kjb.common.KjbEaiBatchException;
|
||||||
import com.eactive.ext.kjb.common.KjbProperty;
|
import com.eactive.ext.kjb.common.KjbProperty;
|
||||||
|
import com.eactive.ext.kjb.eai.EaiBatchInterface;
|
||||||
import com.eactive.ext.kjb.eai.EaiBatchMessage;
|
import com.eactive.ext.kjb.eai.EaiBatchMessage;
|
||||||
import com.eactive.ext.kjb.eai.KjbEaiBatchModule;
|
import com.eactive.ext.kjb.eai.KjbEaiBatchModule;
|
||||||
|
import com.eactive.ext.kjb.safedb.KjbSafedbWrapper;
|
||||||
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -28,24 +32,28 @@ public class EmailJob {
|
|||||||
private final KjbProperty prop;
|
private final KjbProperty prop;
|
||||||
private final MessageRequest messageRequest;
|
private final MessageRequest messageRequest;
|
||||||
|
|
||||||
private final Path tempFilePath;
|
private static final int READY = 1; // Temp 파일 생성 단계
|
||||||
|
private static final int BEFORE_SEND = 2;
|
||||||
|
private static final int SEND = 3;
|
||||||
|
|
||||||
private final Path eaiFilePath;
|
private final Path eaiFilePath;
|
||||||
private final Path backupFilePath;
|
private final Path backupFilePath;
|
||||||
private final Path failedFilePath;
|
private final Path failedFilePath;
|
||||||
private final MessageRequestRepository messageRequestRepository;
|
private final MessageRequestRepository messageRequestRepository;
|
||||||
private final LocalDateTime now = LocalDateTime.now();
|
private final LocalDateTime now = LocalDateTime.now();
|
||||||
|
|
||||||
|
private int eaiPhase = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private EmailJob(KjbProperty prop, MessageRequest messageRequest, MessageRequestRepository messageRequestRepository) {
|
private EmailJob(KjbProperty prop, MessageRequest messageRequest, MessageRequestRepository messageRequestRepository) {
|
||||||
this.messageRequest = messageRequest;
|
this.messageRequest = messageRequest;
|
||||||
this.prop = prop;
|
this.prop = prop;
|
||||||
|
|
||||||
String filename = messageRequest.getServiceId() + "_" + DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(now);
|
String filename = messageRequest.getServiceId() + "_" + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(now) + ".dat";
|
||||||
this.tempFilePath = Paths.get(prop.getEaiBatchSendDir(), filename + ".tmp");
|
this.eaiFilePath = Paths.get(prop.getUmsEaiBatchSendDir(), filename);
|
||||||
this.eaiFilePath = Paths.get(prop.getEaiBatchSendDir(), filename);
|
this.backupFilePath = Paths.get(prop.getUmsEaiBatchBackupDir(), filename);
|
||||||
this.backupFilePath = Paths.get(prop.getEaiBatchBackupDir(), filename);
|
this.failedFilePath = Paths.get(prop.getUmsEaiBatchBackupDir(), filename + ".failed");
|
||||||
this.failedFilePath = Paths.get(prop.getEaiBatchBackupDir(), filename + ".failed");
|
|
||||||
|
|
||||||
this.messageRequestRepository = messageRequestRepository;
|
this.messageRequestRepository = messageRequestRepository;
|
||||||
|
|
||||||
@@ -65,7 +73,8 @@ public class EmailJob {
|
|||||||
|
|
||||||
if (messageRequest.getUser() != null) {
|
if (messageRequest.getUser() != null) {
|
||||||
PortalUser user = messageRequest.getUser();
|
PortalUser user = messageRequest.getUser();
|
||||||
messageRequest.setUmsUid(UUIDBase62Util.uuidStringToBase62(user.getId()));
|
// messageRequest.setUmsUid(UUIDBase62Util.uuidStringToBase62(user.getId()));
|
||||||
|
messageRequest.setUmsUid(user.getId());
|
||||||
|
|
||||||
if (repository != null) {
|
if (repository != null) {
|
||||||
repository.save(messageRequest);
|
repository.save(messageRequest);
|
||||||
@@ -83,6 +92,7 @@ public class EmailJob {
|
|||||||
errors.put("id", "ID is required");
|
errors.put("id", "ID is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (forSend) {
|
||||||
if (StringUtils.isEmpty(messageRequest.getEmail())) {
|
if (StringUtils.isEmpty(messageRequest.getEmail())) {
|
||||||
errors.put("email", "Email is required");
|
errors.put("email", "Email is required");
|
||||||
}
|
}
|
||||||
@@ -91,9 +101,6 @@ public class EmailJob {
|
|||||||
errors.put("username", "Username is required");
|
errors.put("username", "Username is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (forSend) {
|
|
||||||
// TODO: 발송용 체크
|
|
||||||
if (StringUtils.isEmpty(messageRequest.getServiceId())) {
|
if (StringUtils.isEmpty(messageRequest.getServiceId())) {
|
||||||
errors.put("serviceId", "Service ID is required");
|
errors.put("serviceId", "Service ID is required");
|
||||||
}
|
}
|
||||||
@@ -114,11 +121,12 @@ public class EmailJob {
|
|||||||
|
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void sendEmail() {
|
public void sendEmail() throws KjbEaiBatchException {
|
||||||
if (messageRequestRepository != null) {
|
if (messageRequestRepository != null) {
|
||||||
PortalUser user = messageRequest.getUser();
|
PortalUser user = messageRequest.getUser();
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
messageRequest.setUmsUid(UUIDBase62Util.uuidStringToBase62(user.getId()));
|
// messageRequest.setUmsUid(UUIDBase62Util.uuidStringToBase62(user.getId()));
|
||||||
|
messageRequest.setUmsUid(user.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
messageRequestRepository.save(messageRequest);
|
messageRequestRepository.save(messageRequest);
|
||||||
@@ -132,9 +140,11 @@ public class EmailJob {
|
|||||||
|
|
||||||
log.debug("발송 성공 처리");
|
log.debug("발송 성공 처리");
|
||||||
doSuccess();
|
doSuccess();
|
||||||
|
} catch (KjbEaiBatchException e) {
|
||||||
|
doFail(e);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("이메일 발송 실패", e);
|
log.error(e.getMessage(), e);
|
||||||
doFail();
|
doFail(e);
|
||||||
} finally {
|
} finally {
|
||||||
doDeleteEaiFile();
|
doDeleteEaiFile();
|
||||||
}
|
}
|
||||||
@@ -144,13 +154,24 @@ public class EmailJob {
|
|||||||
// TODO: EAI 발송용 파일 삭제
|
// TODO: EAI 발송용 파일 삭제
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doFail() {
|
private void doFail(Exception e) throws KjbEaiBatchException {
|
||||||
// TODO: EAI 발송 실패 처리
|
// TODO: EAI 발송 실패 처리
|
||||||
|
if (e instanceof KjbEaiBatchException) {
|
||||||
|
throw (KjbEaiBatchException) e;
|
||||||
|
} else {
|
||||||
|
throw new KjbEaiBatchException("이메일 발송 실패", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void eaiBatchCall() throws IOException {
|
private void eaiBatchCall() throws KjbEaiBatchException {
|
||||||
KjbEaiBatchModule eaiBatchModule = new KjbEaiBatchModule(prop);
|
KjbEaiBatchModule eaiBatchModule = new KjbEaiBatchModule(prop);
|
||||||
EaiBatchMessage result = eaiBatchModule.call(prop.getEaiBatchInterfaceId(), eaiFilePath.getFileName().toString());
|
EaiBatchInterface eaiBatchInterface = EaiBatchInterface.builder()
|
||||||
|
.id(prop.getUmsEaiBatchInterfaceId())
|
||||||
|
.sendDir(prop.getUmsEaiBatchSendDir())
|
||||||
|
.backupDir(prop.getUmsEaiBatchBackupDir())
|
||||||
|
.errorDir(prop.getUmsEaiBatchErrorDir())
|
||||||
|
.build();
|
||||||
|
EaiBatchMessage result = eaiBatchModule.call(eaiBatchInterface, eaiFilePath.getFileName().toString());
|
||||||
|
|
||||||
if (!"1".equals(result.getResultCode())) {
|
if (!"1".equals(result.getResultCode())) {
|
||||||
log.error("EAI Batch call failed: {}", result);
|
log.error("EAI Batch call failed: {}", result);
|
||||||
@@ -161,25 +182,17 @@ public class EmailJob {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void doSuccess() {
|
private void doSuccess() {
|
||||||
// 백업 디렉토리로 복사
|
// 딱히 뭐 할 거 없음.
|
||||||
try {
|
|
||||||
Files.copy(eaiFilePath, backupFilePath);
|
|
||||||
log.debug("Copied EAI file to backup: {} -> {}", eaiFilePath.toString(), backupFilePath.toString());
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.error("Cannot copy EAI file to backup: {} -> {}", eaiFilePath.toString(), backupFilePath.toString(), e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void doCreateNewFile() {
|
public void doCreateNewFile() {
|
||||||
final String filename = "EMAIL_" + messageRequest.getId() + ".json";
|
|
||||||
|
|
||||||
if (eaiFilePath.toFile().exists()) {
|
if (eaiFilePath.toFile().exists()) {
|
||||||
log.error("Mail File exists for ID: {}", messageRequest.getId());
|
log.error("Mail File exists for ID: {}", messageRequest.getId());
|
||||||
throw new IllegalStateException("Mail File exists for ID: " + messageRequest.getId());
|
throw new IllegalStateException("Mail File exists for ID: " + messageRequest.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
File mailFile = tempFilePath.toFile();
|
File mailFile = eaiFilePath.toFile();
|
||||||
try {
|
try {
|
||||||
boolean created = mailFile.createNewFile();
|
boolean created = mailFile.createNewFile();
|
||||||
if (!created) {
|
if (!created) {
|
||||||
@@ -205,9 +218,7 @@ public class EmailJob {
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Files.write(tempFilePath, data);
|
Files.write(eaiFilePath, data);
|
||||||
Files.copy(tempFilePath, eaiFilePath);
|
|
||||||
Files.delete(tempFilePath);
|
|
||||||
log.debug("Wrote JSON to file: {}", eaiFilePath.toString());
|
log.debug("Wrote JSON to file: {}", eaiFilePath.toString());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("Cannot write JSON to file: {}", eaiFilePath.toString(), e);
|
log.error("Cannot write JSON to file: {}", eaiFilePath.toString(), e);
|
||||||
@@ -217,24 +228,34 @@ public class EmailJob {
|
|||||||
|
|
||||||
public String generateJSON(boolean isPretty) {
|
public String generateJSON(boolean isPretty) {
|
||||||
validateMessageRequest(messageRequest, true, true);
|
validateMessageRequest(messageRequest, true, true);
|
||||||
|
KjbSafedbWrapper safedb = KjbSafedbWrapper.getInstance();
|
||||||
|
|
||||||
JsonObject jsonObject = new JsonObject();
|
JsonObject jsonObject = new JsonObject();
|
||||||
jsonObject.addProperty("MAKE_DATE", DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDateTime.now()));
|
jsonObject.addProperty("MAKE_DATE", DateTimeFormatter.ofPattern("yyyyMMdd").format(LocalDateTime.now()));
|
||||||
jsonObject.addProperty("MAKE_SEQNO", "0001");
|
jsonObject.addProperty("MAKE_SEQNO", DateTimeFormatter.ofPattern("HHmmssSSS").format(LocalDateTime.now()));
|
||||||
jsonObject.addProperty("CAMP_ID", messageRequest.getServiceId()); // 전자금융 고객이메일 발송 업무ID (SERVICE_ID)
|
jsonObject.addProperty("CAMP_ID", messageRequest.getServiceId()); // 전자금융 고객이메일 발송 업무ID (SERVICE_ID)
|
||||||
jsonObject.addProperty("ID", messageRequest.getUmsUid()); // 고객 ID
|
jsonObject.addProperty("ID", messageRequest.getUmsUid()); // 고객 ID
|
||||||
jsonObject.addProperty("EMAIL", messageRequest.getEmail());
|
jsonObject.addProperty("EMAIL", safedb.encryptNotRnnoString(messageRequest.getEmail()));
|
||||||
jsonObject.addProperty("NAME", messageRequest.getUsername());
|
jsonObject.addProperty("NAME", messageRequest.getUsername());
|
||||||
jsonObject.addProperty("ENCKEY", ""); // 보안메일용으로 기입 불필요
|
jsonObject.addProperty("ENCKEY", ""); // 보안메일용으로 기입 불필요
|
||||||
|
|
||||||
|
Gson gson = new Gson();
|
||||||
|
log.debug("Template string to json: {}", messageRequest.getMessage());
|
||||||
|
JsonObject contentJSon = gson.fromJson(messageRequest.getMessage(), JsonObject.class);
|
||||||
|
contentJSon.entrySet().forEach(entry -> {
|
||||||
|
jsonObject.add(entry.getKey(), entry.getValue());
|
||||||
|
});
|
||||||
|
|
||||||
jsonObject.addProperty("SUBJECT", messageRequest.getSubject());
|
// jsonObject.addProperty("SUBJECT", messageRequest.getSubject());
|
||||||
jsonObject.addProperty("MESSAGE", messageRequest.getMessage());
|
// jsonObject.addProperty("MESSAGE", messageRequest.getMessage());
|
||||||
|
|
||||||
|
String jsonString;
|
||||||
if (isPretty) {
|
if (isPretty) {
|
||||||
return new GsonBuilder().setPrettyPrinting().create().toJson(jsonObject);
|
jsonString = new GsonBuilder().setPrettyPrinting().create().toJson(jsonObject);
|
||||||
} else {
|
} else {
|
||||||
return jsonObject.toString();
|
jsonString = jsonObject.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return jsonString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,128 +0,0 @@
|
|||||||
package com.eactive.ext.kjb.ums;
|
|
||||||
|
|
||||||
import com.eactive.apim.portal.template.entity.MessageRequest;
|
|
||||||
import com.eactive.apim.portal.template.repository.MessageRequestRepository;
|
|
||||||
import com.eactive.ext.kjb.common.KjbProperty;
|
|
||||||
import com.eactive.ext.kjb.utils.ValidationUtil;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
import java.util.function.BiConsumer;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
public class EmailSendModule {
|
|
||||||
private static EmailSendModule instance = null;
|
|
||||||
private static KjbProperty prop;
|
|
||||||
|
|
||||||
private final MessageRequestRepository repo;
|
|
||||||
|
|
||||||
|
|
||||||
public static synchronized EmailSendModule getInstance(KjbProperty property,
|
|
||||||
MessageRequestRepository messageRequestRepository) {
|
|
||||||
|
|
||||||
ValidationUtil.validateOrThrow(property);
|
|
||||||
|
|
||||||
if (instance == null) {
|
|
||||||
instance = new EmailSendModule(property, messageRequestRepository);
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static void environmentCheck() {
|
|
||||||
if (StringUtils.isEmpty(prop.getEaiBatchUrl())) {
|
|
||||||
log.warn("EAI_BATCH_URL 가 설정되지 않음. 실제 EAI 배치 연동 없이 동작함");
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("EAI Batch URL: {}", prop.getEaiBatchUrl());
|
|
||||||
log.info("EAI Batch Send Dir: {}", prop.getEaiBatchSendDir());
|
|
||||||
|
|
||||||
|
|
||||||
// 디렉토리 검사(쓰기 권한 포함)
|
|
||||||
Path sendDirPath = Paths.get(prop.getEaiBatchSendDir());
|
|
||||||
if (!sendDirPath.toFile().exists()) {
|
|
||||||
// 생성 시도
|
|
||||||
boolean created = sendDirPath.toFile().mkdirs();
|
|
||||||
if (!created) {
|
|
||||||
log.error("EAI_BATCH_SEND_DIR 디렉토리를 생성할 수 없음: {}", prop.getEaiBatchSendDir());
|
|
||||||
throw new IllegalStateException("EAI_BATCH_SEND_DIR 디렉토리를 생성할 수 없음: " + prop.getEaiBatchSendDir());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sendDirPath.toFile().isDirectory()) {
|
|
||||||
log.error("EAI_BATCH_SEND_DIR 가 디렉토리가 아님: {}", prop.getEaiBatchSendDir());
|
|
||||||
throw new IllegalStateException("EAI_BATCH_SEND_DIR 가 디렉토리가 아님: " + prop.getEaiBatchSendDir());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sendDirPath.toFile().canWrite()) {
|
|
||||||
log.error("EAI_BATCH_SEND_DIR 에 쓰기 권한이 없음: {}", prop.getEaiBatchSendDir());
|
|
||||||
throw new IllegalStateException("EAI_BATCH_SEND_DIR 에 쓰기 권한이 없음: " + prop.getEaiBatchSendDir());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 백업 디렉토리
|
|
||||||
Path backupDirPath = Paths.get(prop.getEaiBatchBackupDir());
|
|
||||||
if (!backupDirPath.toFile().exists()) {
|
|
||||||
// 생성 시도
|
|
||||||
boolean created = backupDirPath.toFile().mkdirs();
|
|
||||||
if (!created) {
|
|
||||||
log.error("EAI_BATCH_BACKUP_DIR 디렉토리를 생성할 수 없음: {}", prop.getEaiBatchBackupDir());
|
|
||||||
throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 디렉토리를 생성할 수 없음: " + prop.getEaiBatchBackupDir());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!backupDirPath.toFile().isDirectory()) {
|
|
||||||
log.error("EAI_BATCH_BACKUP_DIR 가 디렉토리가 아님: {}", prop.getEaiBatchBackupDir());
|
|
||||||
throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 가 디렉토리가 아님: " + prop.getEaiBatchBackupDir());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!backupDirPath.toFile().canWrite()) {
|
|
||||||
log.error("EAI_BATCH_BACKUP_DIR 에 쓰기 권한이 없음: {}", prop.getEaiBatchBackupDir());
|
|
||||||
throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 에 쓰기 권한이 없음: " + prop.getEaiBatchBackupDir());
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("KjbEmailSendModule - Environment check passed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private EmailSendModule(KjbProperty property,
|
|
||||||
MessageRequestRepository repo) {
|
|
||||||
this.repo = repo;
|
|
||||||
|
|
||||||
if (property != null && prop == null) {
|
|
||||||
prop = property;
|
|
||||||
}
|
|
||||||
|
|
||||||
environmentCheck();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompletableFuture<Boolean> sendEmail(MessageRequest messageRequest, BiConsumer<Boolean, Throwable> callback) {
|
|
||||||
return CompletableFuture
|
|
||||||
.supplyAsync(() -> {
|
|
||||||
String serviceId = "UNKNOWN";
|
|
||||||
|
|
||||||
serviceId = messageRequest.getMessageCode().getServiceId();
|
|
||||||
messageRequest.setServiceId(serviceId);
|
|
||||||
|
|
||||||
if (StringUtils.isEmpty(serviceId)) {
|
|
||||||
throw new IllegalArgumentException("MessageRequest MessageCode serviceId is required");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (repo != null) {
|
|
||||||
repo.save(messageRequest);
|
|
||||||
}
|
|
||||||
|
|
||||||
EmailJob job = EmailJob.create(prop, messageRequest, repo);
|
|
||||||
job.sendEmail();
|
|
||||||
|
|
||||||
log.debug("JSON : {}", job.generateJSON(true));
|
|
||||||
return true;
|
|
||||||
})
|
|
||||||
.whenComplete(callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
package com.eactive.ext.kjb.ums;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.template.entity.MessageCode;
|
||||||
|
import com.eactive.apim.portal.template.entity.MessageRequest;
|
||||||
|
import com.eactive.apim.portal.template.repository.MessageRequestRepository;
|
||||||
|
import com.eactive.ext.kjb.common.KjbEaiBatchException;
|
||||||
|
import com.eactive.ext.kjb.common.KjbProperty;
|
||||||
|
import com.eactive.ext.kjb.utils.ValidationUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class KjbEmailSendModule {
|
||||||
|
public final static int[] ALLOW_CHANNELS = { MessageCode.Channels.EMAIL.getValue() };
|
||||||
|
|
||||||
|
private static KjbEmailSendModule instance = null;
|
||||||
|
private static KjbProperty prop;
|
||||||
|
|
||||||
|
private final MessageRequestRepository repo;
|
||||||
|
|
||||||
|
|
||||||
|
public static synchronized KjbEmailSendModule getInstance(KjbProperty property,
|
||||||
|
MessageRequestRepository messageRequestRepository) {
|
||||||
|
|
||||||
|
ValidationUtil.validateOrThrow(property);
|
||||||
|
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new KjbEmailSendModule(property, messageRequestRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isAllowChannel(MessageRequest message) {
|
||||||
|
for (int channel : ALLOW_CHANNELS) {
|
||||||
|
if ((message.getMessageCode().getChannels() & channel) == channel) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void environmentCheck() {
|
||||||
|
if (StringUtils.isEmpty(prop.getEaiBatchUrl())) {
|
||||||
|
log.warn("EAI_BATCH_URL 가 설정되지 않음. 실제 EAI 배치 연동 없이 동작함");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("EAI Batch URL: {}", prop.getEaiBatchUrl());
|
||||||
|
log.info("EAI Batch Send Dir: {}", prop.getUmsEaiBatchSendDir());
|
||||||
|
|
||||||
|
|
||||||
|
// 디렉토리 검사(쓰기 권한 포함)
|
||||||
|
Path sendDirPath = Paths.get(prop.getUmsEaiBatchSendDir());
|
||||||
|
if (!sendDirPath.toFile().exists()) {
|
||||||
|
// 생성 시도
|
||||||
|
boolean created = sendDirPath.toFile().mkdirs();
|
||||||
|
if (!created) {
|
||||||
|
log.error("EAI_BATCH_SEND_DIR 디렉토리를 생성할 수 없음: {}", prop.getUmsEaiBatchSendDir());
|
||||||
|
throw new IllegalStateException("EAI_BATCH_SEND_DIR 디렉토리를 생성할 수 없음: " + prop.getUmsEaiBatchSendDir());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendDirPath.toFile().isDirectory()) {
|
||||||
|
log.error("EAI_BATCH_SEND_DIR 가 디렉토리가 아님: {}", prop.getUmsEaiBatchSendDir());
|
||||||
|
throw new IllegalStateException("EAI_BATCH_SEND_DIR 가 디렉토리가 아님: " + prop.getUmsEaiBatchSendDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sendDirPath.toFile().canWrite()) {
|
||||||
|
log.error("EAI_BATCH_SEND_DIR 에 쓰기 권한이 없음: {}", prop.getUmsEaiBatchSendDir());
|
||||||
|
throw new IllegalStateException("EAI_BATCH_SEND_DIR 에 쓰기 권한이 없음: " + prop.getUmsEaiBatchSendDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 백업 디렉토리
|
||||||
|
Path backupDirPath = Paths.get(prop.getUmsEaiBatchBackupDir());
|
||||||
|
if (!backupDirPath.toFile().exists()) {
|
||||||
|
// 생성 시도
|
||||||
|
boolean created = backupDirPath.toFile().mkdirs();
|
||||||
|
if (!created) {
|
||||||
|
log.error("EAI_BATCH_BACKUP_DIR 디렉토리를 생성할 수 없음: {}", prop.getUmsEaiBatchBackupDir());
|
||||||
|
throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 디렉토리를 생성할 수 없음: " + prop.getUmsEaiBatchBackupDir());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!backupDirPath.toFile().isDirectory()) {
|
||||||
|
log.error("EAI_BATCH_BACKUP_DIR 가 디렉토리가 아님: {}", prop.getUmsEaiBatchBackupDir());
|
||||||
|
throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 가 디렉토리가 아님: " + prop.getUmsEaiBatchBackupDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!backupDirPath.toFile().canWrite()) {
|
||||||
|
log.error("EAI_BATCH_BACKUP_DIR 에 쓰기 권한이 없음: {}", prop.getUmsEaiBatchBackupDir());
|
||||||
|
throw new IllegalStateException("EAI_BATCH_BACKUP_DIR 에 쓰기 권한이 없음: " + prop.getUmsEaiBatchBackupDir());
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("KjbEmailSendModule - Environment check passed.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private KjbEmailSendModule(KjbProperty property,
|
||||||
|
MessageRequestRepository repo) {
|
||||||
|
this.repo = repo;
|
||||||
|
|
||||||
|
if (property != null && prop == null) {
|
||||||
|
prop = property;
|
||||||
|
}
|
||||||
|
|
||||||
|
environmentCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendEmail(MessageRequest messageRequest) throws KjbEaiBatchException {
|
||||||
|
String serviceId = "UNKNOWN";
|
||||||
|
|
||||||
|
serviceId = messageRequest.getMessageCode().getServiceId();
|
||||||
|
messageRequest.setServiceId(serviceId);
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(serviceId)) {
|
||||||
|
throw new IllegalArgumentException("MessageRequest MessageCode serviceId is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MessageCode.Channels.EMAIL.getValue() != (messageRequest.getMessageCode().getChannels() & MessageCode.Channels.EMAIL.getValue())) {
|
||||||
|
log.error("지원하지 않는 발송 채널 - {}", messageRequest.getMessageCode());
|
||||||
|
throw new IllegalArgumentException("지원하지 않는 발송 채널 - " + messageRequest.getMessageCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (repo != null) {
|
||||||
|
repo.save(messageRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
EmailJob job = EmailJob.create(prop, messageRequest, repo);
|
||||||
|
job.sendEmail();
|
||||||
|
|
||||||
|
log.debug("JSON : {}", job.generateJSON(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
package com.eactive.ext.kjb.ums;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.template.entity.MessageCode;
|
||||||
|
import com.eactive.apim.portal.template.entity.MessageRequest;
|
||||||
|
import com.eactive.ext.kjb.common.KjbProperty;
|
||||||
|
import com.eactive.ext.kjb.common.KjbUmsException;
|
||||||
|
import com.eactive.ext.kjb.ums.gson.IUmsGsonObject;
|
||||||
|
import com.eactive.ext.kjb.ums.gson.UmsRequestPayload;
|
||||||
|
import com.eactive.ext.kjb.ums.gson.VerifyPhoneUmsTemplate;
|
||||||
|
import com.eactive.ext.kjb.utils.JsonUtil;
|
||||||
|
import com.eactive.ext.kjb.utils.ValidationUtil;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.JsonSyntaxException;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.hc.client5.http.classic.methods.HttpPost;
|
||||||
|
import org.apache.hc.client5.http.config.RequestConfig;
|
||||||
|
import org.apache.hc.client5.http.entity.EntityBuilder;
|
||||||
|
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
||||||
|
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
||||||
|
import org.apache.hc.core5.http.ContentType;
|
||||||
|
import org.apache.hc.core5.http.ProtocolException;
|
||||||
|
import org.apache.hc.core5.http.io.entity.EntityUtils;
|
||||||
|
import org.apache.hc.core5.util.Timeout;
|
||||||
|
import org.springframework.util.StreamUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class KjbUmsService {
|
||||||
|
public final static int[] ALLOW_CHANNELS = { MessageCode.Channels.KAKAO_ALIMTALK.getValue() };
|
||||||
|
|
||||||
|
private final static String SEND_URL = "/ums/openapi/send";
|
||||||
|
private final static String CONTENT_TYPE = "application/json; charset=utf-8";
|
||||||
|
private final static String AUTHORIZATION_FMT = "Bearer %s";
|
||||||
|
|
||||||
|
private final KjbProperty property;
|
||||||
|
private final boolean isRealMode;
|
||||||
|
private final CloseableHttpClient httpClient;
|
||||||
|
private final RequestConfig requestConfig;
|
||||||
|
|
||||||
|
|
||||||
|
public KjbUmsService(KjbProperty property) {
|
||||||
|
if (property == null) {
|
||||||
|
log.error("[KJB-UMS] 파라미터 설정 되지 않음");
|
||||||
|
throw new IllegalArgumentException("property is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
isRealMode = property.getUmsHostUrl() != null && StringUtils.isNotEmpty(property.getUmsHostUrl().trim());
|
||||||
|
|
||||||
|
if (isRealMode) {
|
||||||
|
ValidationUtil.validateOrThrow(property);
|
||||||
|
|
||||||
|
httpClient = HttpClients.createDefault();
|
||||||
|
requestConfig = RequestConfig.custom()
|
||||||
|
.setConnectTimeout(Timeout.ofSeconds(property.getEaiBatchConnectionTimeout()))
|
||||||
|
.setResponseTimeout(Timeout.ofSeconds(property.getEaiBatchResponseTimeout()))
|
||||||
|
.build();
|
||||||
|
} else {
|
||||||
|
httpClient = HttpClients.createDefault();
|
||||||
|
requestConfig = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.property = property;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static boolean isAllowChannel(MessageRequest message) {
|
||||||
|
for (int channel : ALLOW_CHANNELS) {
|
||||||
|
if ((message.getMessageCode().getChannels() & channel) == channel) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean send(MessageRequest messageRequest) throws KjbUmsException {
|
||||||
|
if (messageRequest == null) {
|
||||||
|
throw new IllegalArgumentException("messageRequest is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MessageCode.Channels.KAKAO_ALIMTALK.getValue() != (messageRequest.getMessageCode().getChannels() & MessageCode.Channels.KAKAO_ALIMTALK.getValue())) {
|
||||||
|
log.error("지원하지 않는 발송 채널 - {}", messageRequest.getMessageCode().toString());
|
||||||
|
throw new IllegalArgumentException("지원하지 않는 발송 채널 - " + messageRequest.getMessageCode().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
IUmsGsonObject content = null;
|
||||||
|
content = VerifyPhoneUmsTemplate.builder().authNumber(messageRequest.getMessage()).build();
|
||||||
|
|
||||||
|
String umsMessageId = this.sendKakaoAlimTalk(messageRequest.getId(),
|
||||||
|
UmsBizWorkCode.VERIFY_PHONE,
|
||||||
|
messageRequest.getPhone(),
|
||||||
|
content
|
||||||
|
);
|
||||||
|
|
||||||
|
log.info("MessageRequest sent - {}", umsMessageId);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String sendKakaoAlimTalk(String guid, UmsBizWorkCode bizWorkCode, String cpno, IUmsGsonObject content) throws KjbUmsException {
|
||||||
|
if (content == null) {
|
||||||
|
throw new IllegalArgumentException("content is null");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(guid)) {
|
||||||
|
guid = String.format("APM-%s", DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS").format(LocalDateTime.now()));
|
||||||
|
}
|
||||||
|
|
||||||
|
UmsRequestPayload payload = UmsRequestPayload.builder()
|
||||||
|
.messageIdentityNo(guid)
|
||||||
|
.msgBizWorkCode(bizWorkCode.getCode())
|
||||||
|
.content(content)
|
||||||
|
.cellphone(cpno)
|
||||||
|
.msgSndChnlCd(UmsRequestPayload.ChannelCode.KM)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
ValidationUtil.validateOrThrow(payload);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.debug("UMS Request payload validation failed - {}", JsonUtil.toPrettyString(payload));
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
UmsCallResult result = call(SEND_URL, payload);
|
||||||
|
if (!result.isSuccess()) {
|
||||||
|
throw new KjbUmsException(result.getThrowable().getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.getUmsMessageId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private UmsCallResult call(String url, UmsRequestPayload requestPayload) throws KjbUmsException {
|
||||||
|
ExecutorService executor = Executors.newSingleThreadExecutor();
|
||||||
|
Gson gson = new Gson();
|
||||||
|
String json = gson.toJson(requestPayload);
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost(property.getUmsHostUrl() + url);
|
||||||
|
httpPost.setHeader("Content-Type", CONTENT_TYPE);
|
||||||
|
httpPost.setHeader("Authorization", String.format(AUTHORIZATION_FMT, property.getUmsApiKey()));
|
||||||
|
httpPost.setEntity(EntityBuilder.create().setText(json).setContentType(ContentType.APPLICATION_JSON).build());
|
||||||
|
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
try {
|
||||||
|
log.debug("Header - {}", httpPost.getHeader("Content-Type"));
|
||||||
|
log.debug("Header - {}", httpPost.getHeader("Authorization"));
|
||||||
|
log.debug("Request Body : {}", StreamUtils.copyToString(httpPost.getEntity().getContent(), Charset.defaultCharset()));
|
||||||
|
} catch (ProtocolException | IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
httpPost.setConfig(requestConfig);
|
||||||
|
|
||||||
|
if (isRealMode) {
|
||||||
|
Future<UmsCallResult> future = executor.submit(() -> {
|
||||||
|
return httpClient.execute(httpPost, res -> {
|
||||||
|
try {
|
||||||
|
UmsCallResult result;
|
||||||
|
String body = EntityUtils.toString(res.getEntity());
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Response - {}", body);
|
||||||
|
}
|
||||||
|
result = gson.fromJson(body, UmsCallResult.class);
|
||||||
|
result.setUmsMessageId(requestPayload.getMessageIdentityNo());
|
||||||
|
result.setSuccess(true);
|
||||||
|
return result;
|
||||||
|
} catch (JsonSyntaxException e) {
|
||||||
|
log.error("UMS Call error(JSON Syntax)", e);
|
||||||
|
return UmsCallResult.builder()
|
||||||
|
.umsMessageId(requestPayload.getMessageIdentityNo())
|
||||||
|
.isSuccess(false)
|
||||||
|
.throwable(e)
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("UMS Call error", e);
|
||||||
|
return UmsCallResult.builder()
|
||||||
|
.umsMessageId(requestPayload.getMessageIdentityNo())
|
||||||
|
.isSuccess(false)
|
||||||
|
.throwable(e)
|
||||||
|
.build();
|
||||||
|
} finally {
|
||||||
|
log.debug("Response status - {}", res.getCode());
|
||||||
|
|
||||||
|
if (res.getCode() != 200) {
|
||||||
|
Arrays.asList(res.getHeaders()).forEach(o -> {
|
||||||
|
log.warn("[Header] {}: {}", o.getName(), o.getValue());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
return future.get(property.getUmsTimeout(), TimeUnit.SECONDS);
|
||||||
|
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
||||||
|
log.error("EAI Batch call exception", e);
|
||||||
|
throw new KjbUmsException("EAI Ums call failed", e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.warn("UMS URL 미지정으로 실제 호출을 진행하지 않음.");
|
||||||
|
return UmsCallResult.builder().isSuccess(true).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.eactive.ext.kjb.ums;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
public enum UmsBizWorkCode {
|
||||||
|
MONITORING("B_ELFN_09510", "OBP 모니터링"),
|
||||||
|
VERIFY_PHONE("S55_ICT_0006", "API Portal 인증번호"),
|
||||||
|
;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
private String code;
|
||||||
|
@Getter
|
||||||
|
private String displayName;
|
||||||
|
|
||||||
|
UmsBizWorkCode(String code, String displayName) {
|
||||||
|
this.code = code;
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.eactive.ext.kjb.ums;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class UmsCallResult {
|
||||||
|
private String umsMessageId;
|
||||||
|
private boolean isSuccess;
|
||||||
|
|
||||||
|
private String returnCode;
|
||||||
|
private String returnMessage;
|
||||||
|
private String returnPayload;
|
||||||
|
|
||||||
|
private Throwable throwable;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package com.eactive.ext.kjb.ums.gson;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GSON Serialize 용 Object
|
||||||
|
*/
|
||||||
|
public interface IUmsGsonObject {
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.eactive.ext.kjb.ums.gson;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
public class ObpMonitoringUmsTemplate implements IUmsGsonObject {
|
||||||
|
@SerializedName("AUTH_NO")
|
||||||
|
private String authNumber;
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package com.eactive.ext.kjb.ums.gson;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
import org.hibernate.validator.constraints.NotEmpty;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import javax.validation.constraints.Pattern;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
@Getter
|
||||||
|
public class UmsRequestPayload {
|
||||||
|
// MSG_IDNT_NO string 메시지식별번호 50 O "발송 구분을 위한 거래 번호 (GUID 권고)
|
||||||
|
//GUID로 입력이 어려울시
|
||||||
|
//어플리케이션코드(3자리)+난수 로 유니크한 ID로 채번 권고"
|
||||||
|
@Length(min = 1, max = 50)
|
||||||
|
@NotEmpty
|
||||||
|
@SerializedName("MSG_IDNT_NO")
|
||||||
|
private String messageIdentityNo;
|
||||||
|
|
||||||
|
// MSG_BZWK_CD string 메시지업무코드 30 O UMS에 등록된 메시지업무코드(AS-IS 템플릿ID)
|
||||||
|
@NotEmpty
|
||||||
|
@Length(min = 1, max = 30)
|
||||||
|
@SerializedName("MSG_BZWK_CD")
|
||||||
|
private String msgBizWorkCode;
|
||||||
|
|
||||||
|
// 메시지업무파라미터내용 / 4000 / JSONObject
|
||||||
|
// MSG_BZWK_PARAM_CTNT object 메시지업무파라미터내용 4000 메시지업무코드의 템플릿에서 사용하는 변수와 변수값
|
||||||
|
@NotNull
|
||||||
|
@SerializedName("MSG_BZWK_PARAM_CTNT")
|
||||||
|
private IUmsGsonObject content;
|
||||||
|
|
||||||
|
// 발송예약일시 / 14 / 실 발송 일자(yyyyMMddHHmmss)
|
||||||
|
// SND_RESR_DTTM string 발송예약일시 14 20240418111500 O 실 발송 일자(현재시간)
|
||||||
|
@Pattern(regexp = "^[0-9]{14}$")
|
||||||
|
@NotEmpty
|
||||||
|
@SerializedName("SND_RESR_DTTM")
|
||||||
|
@Builder.Default
|
||||||
|
private String sendReservationDatetime = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now());
|
||||||
|
|
||||||
|
// 휴대전화번호 / 44 / 휴대폰번호(평문, 암호화 둘다가능)
|
||||||
|
// CPNO string 휴대전화번호 44 "암호화 또는 평문 (safedb not rrno)
|
||||||
|
//문자일 시 암호문
|
||||||
|
//숫자일 시 전화번호
|
||||||
|
//고객번호 또는 휴대전화번호 필수"
|
||||||
|
@NotEmpty
|
||||||
|
@Pattern(regexp = "^[0-9]{10,11}$")
|
||||||
|
@SerializedName("CPNO")
|
||||||
|
private String cellphone;
|
||||||
|
|
||||||
|
// 발송부점코드 / 4 /
|
||||||
|
// SND_BRCD string 발송부점코드 4 O 0333
|
||||||
|
@NotEmpty
|
||||||
|
@SerializedName("SND_BRCD")
|
||||||
|
@Builder.Default
|
||||||
|
private String sendBranchCode = "0990";
|
||||||
|
|
||||||
|
// 발송직원번호 / 7 / ECEB057(텔러번호?)
|
||||||
|
// SND_EMPNO string 발송직원번호 7 O ? 넣긴해야함
|
||||||
|
@NotEmpty
|
||||||
|
@SerializedName("SND_EMPNO")
|
||||||
|
@Builder.Default
|
||||||
|
private String sendEmployeeNo = "ECEB057";
|
||||||
|
|
||||||
|
// SRVC_ID string 서비스ID 11 요청단 측 서비스ID
|
||||||
|
@NotEmpty
|
||||||
|
@SerializedName("SRVC_ID")
|
||||||
|
@Builder.Default
|
||||||
|
private String serviceId = "eAPIM";
|
||||||
|
|
||||||
|
// MSG_SND_CHNL_CD string 메시지발송채널코드 2
|
||||||
|
@SerializedName("MSG_SND_CHNL_CD")
|
||||||
|
@NotNull
|
||||||
|
@Builder.Default
|
||||||
|
private ChannelCode msgSndChnlCd = ChannelCode.KM;
|
||||||
|
|
||||||
|
// VALDTN_RSLT_CD string 검증결과코드 2 00으로 고정
|
||||||
|
@SerializedName("VALDTN_RSLT_CD")
|
||||||
|
@NotEmpty
|
||||||
|
@Builder.Default
|
||||||
|
private String validationResultCode = "00";
|
||||||
|
|
||||||
|
public enum ChannelCode {
|
||||||
|
SM, // SMS
|
||||||
|
LM, // LMS
|
||||||
|
MM, // MMS
|
||||||
|
KM, // 카카오-알림톡
|
||||||
|
FM, // 카카오-친구톡
|
||||||
|
SR, // RCS-SMS
|
||||||
|
LR, // RCS-LMS
|
||||||
|
MR, // RCS-MMS
|
||||||
|
TR, // RCS-TEMPLATE
|
||||||
|
IR, // RCS-IMAGE Template
|
||||||
|
PM // Push
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.eactive.ext.kjb.ums.gson;
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class VerifyPhoneUmsTemplate implements IUmsGsonObject {
|
||||||
|
@SerializedName("ATHN_NO")
|
||||||
|
private String authNumber;
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.eactive.ext.kjb.utils;
|
||||||
|
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
|
||||||
|
public class JsonUtil {
|
||||||
|
public static String toPrettyString(Object obj) {
|
||||||
|
if (obj == null) { return "null"; }
|
||||||
|
return new GsonBuilder().setPrettyPrinting().create().toJson(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package com.eactive.ext.kjb.utils;
|
||||||
|
|
||||||
|
import com.eactive.ext.kjb.common.KjbProperty;
|
||||||
|
import com.eactive.ext.kjb.common.KjbPropertyValue;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public class KjbPropertyUtil {
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Class<?> clazz = KjbProperty.class;
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
for (Field field : clazz.getDeclaredFields()) {
|
||||||
|
KjbPropertyValue anno = field.getAnnotation(KjbPropertyValue.class);
|
||||||
|
|
||||||
|
if (anno != null) {
|
||||||
|
sb.append("insert into EMSADM.TSEAIRM24 (PRPTYGROUPNAME, PRPTYNAME, PRPTY2VAL) values ('Monitoring', ");
|
||||||
|
|
||||||
|
String key = anno.key();
|
||||||
|
String defValue = anno.defaultValue();
|
||||||
|
if (StringUtils.isEmpty(defValue)) defValue = "";
|
||||||
|
|
||||||
|
sb.append("'").append(key).append("', '").append(addQuote(defValue)).append("');\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String addQuote(String str) {
|
||||||
|
return str.replace("'", "''");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.eactive.ext.kjb.utils;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
public class OsUtil {
|
||||||
|
public static boolean isWindows() {
|
||||||
|
return System.getProperty("os.name").toLowerCase().contains("win");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isLinux() {
|
||||||
|
String os = System.getProperty("os.name").toLowerCase();
|
||||||
|
return os.contains("nux") || os.contains("nix") || os.contains("aix") ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getHostName() {
|
||||||
|
try {
|
||||||
|
return InetAddress.getLocalHost().getHostName();
|
||||||
|
} catch (Exception e) {
|
||||||
|
String name = System.getProperty("COMPUTERNAME");
|
||||||
|
if (name == null) name = System.getenv("HOSTNAME");
|
||||||
|
return name != null ? name : "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,7 @@
|
|||||||
package com.eactive.ext.kjb.ums.test;
|
package com.eactive.ext.kjb.ums.test;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.CompletableFuture;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import com.eactive.apim.portal.template.entity.MessageCode;
|
|
||||||
import com.eactive.apim.portal.template.entity.MessageRequest;
|
|
||||||
import com.eactive.ext.kjb.common.KjbProperty;
|
|
||||||
import com.eactive.ext.kjb.ums.EmailSendModule;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -18,31 +9,32 @@ public class KjbEmailSendModuleTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() {
|
public void test() {
|
||||||
KjbProperty prop = new KjbProperty();
|
// KjbProperty prop = new KjbProperty();
|
||||||
prop.setEaiBatchUrl(null);
|
// prop.setEaiBatchUrl(null);
|
||||||
prop.setEaiBatchSendDir("D:\\kjb-logs\\sendmail\\snd");
|
// prop.setEaiBatchSendDir("D:\\kjb-logs\\sendmail\\snd");
|
||||||
prop.setEaiBatchBackupDir("D:\\kjb-logs\\sendmail\\bak");
|
// prop.setEaiBatchBackupDir("D:\\kjb-logs\\sendmail\\bak");
|
||||||
prop.setEaiBatchInterfaceId("UAGFF00001UIE");
|
// prop.setEaiBatchInterfaceId("UAGFF00001UIE");
|
||||||
|
//
|
||||||
EmailSendModule service = EmailSendModule.getInstance(prop, null);
|
// EmailSendModule service = EmailSendModule.getInstance(prop, null);
|
||||||
MessageRequest messageRequest = new MessageRequest();
|
// MessageRequest messageRequest = new MessageRequest();
|
||||||
messageRequest.setId(UUID.randomUUID().toString());
|
// messageRequest.setId(UUID.randomUUID().toString());
|
||||||
messageRequest.setMessageCode(MessageCode.USER_VERIFICATION_EMAIL);
|
// messageRequest.setEmail("dearmai@dearmai.net");
|
||||||
messageRequest.setEmail("dearmai@dearmai.net");
|
// messageRequest.setUsername("김광은");
|
||||||
messageRequest.setUsername("김광은");
|
// messageRequest.setSubject("제목 - test");
|
||||||
messageRequest.setSubject("제목 - test");
|
//
|
||||||
messageRequest.setMessage("내용 - test");
|
// messageRequest.setMessageCode(MessageCode.USER_ACCOUNT_LOCKED);
|
||||||
|
//// messageRequest.setMessage("내용 - test");
|
||||||
|
//
|
||||||
CompletableFuture<Boolean> future = service.sendEmail(messageRequest, (result, ex) -> {
|
//
|
||||||
if (ex != null) {
|
// CompletableFuture<Boolean> future = service.sendEmail(messageRequest, (result, ex) -> {
|
||||||
log.error("failed", ex);
|
// if (ex != null) {
|
||||||
} else {
|
// log.error("failed", ex);
|
||||||
log.info("result: {}", result);
|
// } else {
|
||||||
}
|
// log.info("result: {}", result);
|
||||||
});
|
// }
|
||||||
|
// });
|
||||||
future.join();
|
//
|
||||||
|
// future.join();
|
||||||
|
|
||||||
log.info("done");
|
log.info("done");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package com.eactive.ext.kjb.ums.test;
|
||||||
|
|
||||||
|
import com.eactive.apim.portal.template.entity.MessageCode;
|
||||||
|
import com.eactive.apim.portal.template.entity.MessageRequest;
|
||||||
|
import com.eactive.ext.kjb.common.KjbEaiBatchException;
|
||||||
|
import com.eactive.ext.kjb.common.KjbProperty;
|
||||||
|
import com.eactive.ext.kjb.common.KjbUmsException;
|
||||||
|
import com.eactive.ext.kjb.ums.KjbEmailSendModule;
|
||||||
|
import com.eactive.ext.kjb.ums.KjbUmsService;
|
||||||
|
import com.eactive.ext.kjb.utils.OsUtil;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Assumptions;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class UmsTest {
|
||||||
|
private static KjbProperty prop = null;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
public static void init() {
|
||||||
|
prop = new KjbProperty();
|
||||||
|
prop.setEaiBatchUrl("http://172.31.32.111:10230/BATAppWeb/EaiBatCall");
|
||||||
|
prop.setUmsEaiBatchInterfaceId("UAGFF00001UIE");
|
||||||
|
|
||||||
|
prop.setUmsHostUrl("http://172.31.35.144:9021");
|
||||||
|
prop.setUmsApiKey("7cca6d58-e4f7-474d-9edd-525806bc99ff");
|
||||||
|
|
||||||
|
if (OsUtil.isWindows()) {
|
||||||
|
prop.setUmsEaiBatchSendDir("c:\\kjb-logs\\sendmail\\snd");
|
||||||
|
prop.setUmsEaiBatchBackupDir("c:\\kjb-logs\\sendmail\\bak");
|
||||||
|
prop.setUmsEaiBatchErrorDir("c:\\kjb-logs\\sendmail\\error");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (OsUtil.isLinux()) {
|
||||||
|
prop.setUmsEaiBatchSendDir("/Data/eapim/portal/sendmail/snd");
|
||||||
|
prop.setUmsEaiBatchBackupDir("/Data/eapim/portal/sendmail/bak");
|
||||||
|
prop.setUmsEaiBatchErrorDir("/Data/eapim/portal/sendmail/error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSms() {
|
||||||
|
Assumptions.assumeTrue(
|
||||||
|
"true".equalsIgnoreCase(System.getenv("KJB_EAPIM_TEST_ENABLED")),
|
||||||
|
"OS 환경변수 내 KJB_EAPIM_TEST_ENABLED=TRUE 설정되어 있어야 동작함");
|
||||||
|
|
||||||
|
MessageRequest messageRequest = new MessageRequest();
|
||||||
|
messageRequest.setId(UUID.randomUUID().toString());
|
||||||
|
messageRequest.setMessageCode(MessageCode.USER_VERIFICATION_MOBILEPHONE);
|
||||||
|
messageRequest.setPhone("01099750188");
|
||||||
|
messageRequest.setEmail("dearmai@dearmai.net");
|
||||||
|
messageRequest.setUsername("김광은");
|
||||||
|
messageRequest.setSubject("제목 - test");
|
||||||
|
messageRequest.setMessage("{\"ATHN_NO\"; \"369369\"}");
|
||||||
|
|
||||||
|
KjbUmsService umsService = new KjbUmsService(prop);
|
||||||
|
try {
|
||||||
|
boolean result = umsService.send(messageRequest);
|
||||||
|
|
||||||
|
} catch (KjbUmsException e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
Assertions.fail(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("done");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmail() {
|
||||||
|
Assumptions.assumeTrue(
|
||||||
|
"true".equalsIgnoreCase(System.getenv("KJB_EAPIM_TEST_ENABLED")),
|
||||||
|
"OS 환경변수 내 KJB_EAPIM_TEST_ENABLED=TRUE 설정되어 있어야 동작함");
|
||||||
|
|
||||||
|
|
||||||
|
System.getProperties().keySet().forEach(key -> {
|
||||||
|
log.info("{}: {}", key, System.getProperty((String) key));
|
||||||
|
});
|
||||||
|
|
||||||
|
KjbEmailSendModule service = KjbEmailSendModule.getInstance(prop, null);
|
||||||
|
MessageRequest messageRequest = new MessageRequest();
|
||||||
|
messageRequest.setId(UUID.randomUUID().toString());
|
||||||
|
messageRequest.setMessageCode(MessageCode.USER_VERIFICATION_EMAIL);
|
||||||
|
messageRequest.setEmail("dearmai@dearmai.net");
|
||||||
|
messageRequest.setUmsUid(UUID.randomUUID().toString());
|
||||||
|
messageRequest.setUsername("김광은");
|
||||||
|
messageRequest.setSubject("제목 - test");
|
||||||
|
messageRequest.setMessage("{\"ATHN_NO\": \"543216\"}");
|
||||||
|
messageRequest.setServiceId("FM_APIM001");
|
||||||
|
|
||||||
|
log.info("MessageRequest - {}", messageRequest);
|
||||||
|
|
||||||
|
try {
|
||||||
|
service.sendEmail(messageRequest);
|
||||||
|
} catch (KjbEaiBatchException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("done");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<configuration>
|
<configuration scan="true" scanPeriod="5 seconds">
|
||||||
|
|
||||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||||
<encoder>
|
<encoder>
|
||||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
<!-- <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>-->
|
||||||
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %magenta([%thread]) %highlight([%-3level]) %logger - %msg %n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@
|
|||||||
<logger name="com.eactive.ext.kjb" level="DEBUG" additivity="false">
|
<logger name="com.eactive.ext.kjb" level="DEBUG" additivity="false">
|
||||||
<appender-ref ref="CONSOLE" />
|
<appender-ref ref="CONSOLE" />
|
||||||
</logger>
|
</logger>
|
||||||
|
<logger name="org.apache.hc" level="DEBUG" additivity="false" />
|
||||||
|
|
||||||
|
|
||||||
<root level="INFO">
|
<root level="INFO">
|
||||||
|
|||||||
Reference in New Issue
Block a user