diff --git a/src/docs/embedded-db-initialize.sql b/src/docs/embedded-db-initialize.sql index 09a7a22..b6fa4c4 100644 --- a/src/docs/embedded-db-initialize.sql +++ b/src/docs/embedded-db-initialize.sql @@ -7,11 +7,13 @@ CREATE TABLE TB_HOST ( CREATE TABLE TB_RECEIVE_MESSAGE ( SENDER_HOST_ID VARCHAR(512) NOT NULL + , SENDER_TIMESTAMP TIMESTAMP NOT NULL , MESSAGE_UUID VARCHAR(36) NOT NULL - , RECEIVED_TIMESTAMP TIMESTAMP NOT NULL , MESSAGE_TYPE VARCHAR(128) NOT NULL + , RECEIVED_TIMESTAMP TIMESTAMP NOT NULL + , RECIPIENT_HOST_ID VARCHAR(512) NOT NULL + , RECIPIENT_DROP_BOX VARCHAR(256) NOT NULL , DATA CLOB , PROCESS_STATUS VARCHAR(128) NOT NULL , PRIMARY KEY (SENDER_HOST_ID, MESSAGE_UUID) ); - diff --git a/src/docs/message-examples/insert-from-sam-to-defree.json b/src/docs/message-examples/insert-from-sam-to-defree.json index f404440..3cdb3f4 100644 --- a/src/docs/message-examples/insert-from-sam-to-defree.json +++ b/src/docs/message-examples/insert-from-sam-to-defree.json @@ -3,6 +3,8 @@ "host-id": "sam", "timestamp": 1740643945523 }, + "message-uuid": "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d", + "message-type": "SAVE_DB_DATA", "recipient": { "host-id": "defree", "drop-box-id": "save-violation-history" diff --git a/src/main/java/com/bsmlab/dfx/agent/config/constant/MessageType.java b/src/main/java/com/bsmlab/dfx/agent/config/constant/MessageType.java new file mode 100644 index 0000000..f2b2f67 --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/agent/config/constant/MessageType.java @@ -0,0 +1,6 @@ +package com.bsmlab.dfx.agent.config.constant; + +public enum MessageType { + SAVE_DB_DATA, + SAVE_FILE; +} diff --git a/src/main/java/com/bsmlab/dfx/agent/config/constant/ProcessStatusType.java b/src/main/java/com/bsmlab/dfx/agent/config/constant/ProcessStatusType.java new file mode 100644 index 0000000..e92035b --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/agent/config/constant/ProcessStatusType.java @@ -0,0 +1,5 @@ +package com.bsmlab.dfx.agent.config.constant; + +public enum ProcessStatusType { + READ; +} diff --git a/src/main/java/com/bsmlab/dfx/agent/listener/dto/AckDto.java b/src/main/java/com/bsmlab/dfx/agent/listener/dto/AckDto.java index db5e0e3..438cebf 100644 --- a/src/main/java/com/bsmlab/dfx/agent/listener/dto/AckDto.java +++ b/src/main/java/com/bsmlab/dfx/agent/listener/dto/AckDto.java @@ -8,6 +8,8 @@ import lombok.Setter; @Setter @Builder public class AckDto { + public static final String RESULT_SUCCESS = "SUCCESS"; + public static final String RESULT_FAIL = "FAIL"; private String result; private String message; } diff --git a/src/main/java/com/bsmlab/dfx/agent/listener/dto/ListenerMapper.java b/src/main/java/com/bsmlab/dfx/agent/listener/dto/ListenerMapper.java index 754dc69..e68a602 100644 --- a/src/main/java/com/bsmlab/dfx/agent/listener/dto/ListenerMapper.java +++ b/src/main/java/com/bsmlab/dfx/agent/listener/dto/ListenerMapper.java @@ -2,9 +2,8 @@ package com.bsmlab.dfx.agent.listener.dto; import org.apache.ibatis.annotations.Mapper; -import java.util.Map; - @Mapper public interface ListenerMapper { - void insertReceiveMessage(Map param); + int selectReceiveMessageCountByPk(ReceiveMessageDto receiveMessageDto); + void insertReceiveMessage(ReceiveMessageDto receiveMessageDto); } diff --git a/src/main/java/com/bsmlab/dfx/agent/listener/dto/ReceiveMessageDto.java b/src/main/java/com/bsmlab/dfx/agent/listener/dto/ReceiveMessageDto.java index 1edec24..ae555c8 100644 --- a/src/main/java/com/bsmlab/dfx/agent/listener/dto/ReceiveMessageDto.java +++ b/src/main/java/com/bsmlab/dfx/agent/listener/dto/ReceiveMessageDto.java @@ -9,9 +9,12 @@ import lombok.*; @NoArgsConstructor public class ReceiveMessageDto { private String senderHostId; + private long senderTimestamp; private String messageUuid; - private long receivedTimestamp; private String messageType; + private long receivedTimestamp; + private String recipientHostId; + private String recipientDropBox; private String data; private String processStatus; } diff --git a/src/main/java/com/bsmlab/dfx/agent/listener/service/ListenerService.java b/src/main/java/com/bsmlab/dfx/agent/listener/service/ListenerService.java index 5bd4b08..559637e 100644 --- a/src/main/java/com/bsmlab/dfx/agent/listener/service/ListenerService.java +++ b/src/main/java/com/bsmlab/dfx/agent/listener/service/ListenerService.java @@ -3,16 +3,14 @@ package com.bsmlab.dfx.agent.listener.service; import com.bsmlab.dfx.agent.listener.dto.AckDto; import com.bsmlab.dfx.agent.listener.dto.ListenerMapper; import com.bsmlab.dfx.agent.listener.dto.ReceiveMessageDto; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; +import com.bsmlab.dfx.agent.support.MessageUtils; +import com.bsmlab.dfx.agent.support.exception.IllegalMessageException; +import com.bsmlab.dfx.agent.support.exception.InCompleteMessageException; +import com.bsmlab.dfx.agent.support.exception.NullMessageException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import java.util.HashMap; -import java.util.Map; - @RequiredArgsConstructor @Slf4j @Service @@ -20,16 +18,21 @@ public class ListenerService { private final ListenerMapper listenerMapper; public AckDto saveNewMessage(String messageJsonString) { - return null; - } - - private ReceiveMessageDto toReceiveMessageDto(String messageJsonString) throws JsonProcessingException { - ObjectMapper objectMapper = new ObjectMapper(); - Map map = null; - map = objectMapper.readValue(messageJsonString, new TypeReference>() {}); - ReceiveMessageDto receiveMessageDto = ReceiveMessageDto.builder() - .senderHostId(map.get("")) - .build(); - return null; + AckDto ackDto = null; + try { + ReceiveMessageDto receiveMessageDto = MessageUtils.toReceiveMessageDto(messageJsonString); + int counter = listenerMapper.selectReceiveMessageCountByPk(receiveMessageDto); + if(counter > 0) { + ackDto = AckDto.builder().result(AckDto.RESULT_FAIL).message("이전 전송한 메시지 중 중복된 UUID가 존재합니다.").build(); + } + else { + listenerMapper.insertReceiveMessage(receiveMessageDto); + ackDto = AckDto.builder().result(AckDto.RESULT_SUCCESS).build(); + } + } catch (IllegalMessageException | NullMessageException | InCompleteMessageException e) { + log.error("{}", e, e); + ackDto = AckDto.builder().result(AckDto.RESULT_FAIL).message(e.getLocalizedMessage()).build(); + } + return ackDto; } } diff --git a/src/main/java/com/bsmlab/dfx/agent/support/MessageUtils.java b/src/main/java/com/bsmlab/dfx/agent/support/MessageUtils.java new file mode 100644 index 0000000..3e65176 --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/agent/support/MessageUtils.java @@ -0,0 +1,133 @@ +package com.bsmlab.dfx.agent.support; + +import com.bsmlab.dfx.agent.config.constant.MessageType; +import com.bsmlab.dfx.agent.config.constant.ProcessStatusType; +import com.bsmlab.dfx.agent.listener.dto.ReceiveMessageDto; +import com.bsmlab.dfx.agent.support.exception.IllegalMessageException; +import com.bsmlab.dfx.agent.support.exception.InCompleteMessageException; +import com.bsmlab.dfx.agent.support.exception.NullMessageException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class MessageUtils { + private MessageUtils() {}; + + public static ReceiveMessageDto toReceiveMessageDto(String messageJsonString) throws IllegalMessageException, NullMessageException, InCompleteMessageException { + ReceiveMessageDto receiveMessageDto = null; + ObjectMapper objectMapper = new ObjectMapper(); + Map map = null; + try { + map = objectMapper.readValue(messageJsonString, new TypeReference>() {}); + if(map == null) { + throw new NullMessageException(""); + } + if(map.get("sender") == null) { + throw new InCompleteMessageException("sender 엘리먼트를 찾을 수 없습니다."); + } + else if(map.get("sender") instanceof Map){ + throw new InCompleteMessageException("sender 엘리먼트의 데이터가 객체타입이 아닙니다."); + } + Map senderMap = (Map)map.get("sender"); + String senderHostId; + if(senderMap.get("host-id") == null) { + throw new InCompleteMessageException("sender.host-id 엘리먼트를 찾을 수 없습니다."); + } + else if(StringUtils.isBlank(String.valueOf(senderMap.get("host-id")))) { + throw new InCompleteMessageException("sender.host-id 값을 찾을 수 없습니다."); + } + else { + senderHostId = String.valueOf(senderMap.get("host-id")); + } + long senderTimestamp = 0; + if(senderMap.get("timestamp") == null) { + throw new InCompleteMessageException("sender.timestamp 엘리먼트를 찾을 수 없습니다."); + } + else if(StringUtils.isBlank(String.valueOf(senderMap.get("timestamp")))) { + throw new InCompleteMessageException("sender.timestamp 값을 찾을 수 없습니다."); + } + else { + String senderTimeStampString = String.valueOf(senderMap.get("host-id")); + try { + senderTimestamp = Long.parseLong(senderTimeStampString); + } catch (NumberFormatException e) { + throw new InCompleteMessageException("sender.timestamp 값의 형식이 숫자형식이 아닙니다. " + senderTimeStampString); + } + } + String messageUuid; + if(map.get("message-uuid") == null) { + throw new InCompleteMessageException("message-uuid 엘리먼트를 찾을 수 없습니다."); + } + else if(StringUtils.isBlank(String.valueOf(map.get("message-uuid")))) { + throw new InCompleteMessageException("message-uuid 값을 찾을 수 없습니다."); + } + else { + messageUuid = String.valueOf(map.get("message-uuid")); + try { + UUID.fromString(messageUuid); + } + catch (IllegalArgumentException e) { + throw new InCompleteMessageException("message-uuid 값의 형식이 숫자형식이 아닙니다. " + messageUuid); + } + } + long receivedTimestamp = System.currentTimeMillis(); + if(map.get("recipient") == null) { + throw new InCompleteMessageException("recipient 엘리먼트를 찾을 수 없습니다."); + } + else if(map.get("recipient") instanceof Map){ + throw new InCompleteMessageException("recipient 엘리먼트의 데이터가 객체타입이 아닙니다."); + } + Map recipientMap = (Map)map.get("recipient"); + //TODO recipient + String recipientHostId; + if(recipientMap.get("host-id") == null) { + throw new InCompleteMessageException("recipient.host-id 엘리먼트를 찾을 수 없습니다."); + } + else if(StringUtils.isBlank(String.valueOf(recipientMap.get("host-id")))) { + throw new InCompleteMessageException("recipient.host-id 값을 찾을 수 없습니다."); + } + else { + recipientHostId = String.valueOf(recipientMap.get("host-id")); + } + String recipientDropBoxId; + if(recipientMap.get("drop-box-id") == null) { + throw new InCompleteMessageException("recipient.drop-box-id 엘리먼트를 찾을 수 없습니다."); + } + else if(StringUtils.isBlank(String.valueOf(recipientMap.get("drop-box-id")))) { + throw new InCompleteMessageException("recipient.drop-box-id 값을 찾을 수 없습니다."); + } + else { + recipientDropBoxId = String.valueOf(recipientMap.get("drop-box-id")); + } + String messageType; + if(map.get("message-type") == null) { + throw new InCompleteMessageException("message-type 엘리먼트를 찾을 수 없습니다."); + } + else if(StringUtils.isBlank(String.valueOf(map.get("message-type")))) { + throw new InCompleteMessageException("message-type 값을 찾을 수 없습니다."); + } + else { + messageType = String.valueOf(map.get("message-type")); + if(!EnumUtils.isValidEnum(MessageType.class, messageType)) { + throw new InCompleteMessageException("message-type 값이 옳바르지 않습니다. " + messageType); + } + } + receiveMessageDto = ReceiveMessageDto.builder() + .senderHostId(senderHostId).senderTimestamp(senderTimestamp) + .messageUuid(messageUuid).messageType(messageType).receivedTimestamp(receivedTimestamp) + .recipientHostId(recipientHostId).recipientDropBox(recipientDropBoxId) + .data(messageJsonString).processStatus(ProcessStatusType.READ.name()) + .build(); + } + catch(JsonProcessingException e) { + throw new IllegalMessageException(e.getMessage()); + } + return receiveMessageDto; + } +} diff --git a/src/main/java/com/bsmlab/dfx/agent/support/exception/DfxException.java b/src/main/java/com/bsmlab/dfx/agent/support/exception/DfxException.java new file mode 100644 index 0000000..974ef19 --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/agent/support/exception/DfxException.java @@ -0,0 +1,19 @@ +package com.bsmlab.dfx.agent.support.exception; + +public class DfxException extends Exception { + protected String additionalMessage; + + public DfxException(String additionalMessage) { + this.additionalMessage = additionalMessage; + } + + @Override + public String getMessage() { + return "cannot find message contents. " + this.additionalMessage; + } + + @Override + public String getLocalizedMessage() { + return "파싱한 메시지의 내용을 찾을 수 없습니다. " + this.additionalMessage; + } +} diff --git a/src/main/java/com/bsmlab/dfx/agent/support/exception/IllegalMessageException.java b/src/main/java/com/bsmlab/dfx/agent/support/exception/IllegalMessageException.java new file mode 100644 index 0000000..ef9404c --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/agent/support/exception/IllegalMessageException.java @@ -0,0 +1,17 @@ +package com.bsmlab.dfx.agent.support.exception; + +public class IllegalMessageException extends DfxException { + public IllegalMessageException(String additionalMessage) { + super(additionalMessage); + } + + @Override + public String getMessage() { + return "cannot parse json message." + this.additionalMessage; + } + + @Override + public String getLocalizedMessage() { + return "json 메시지를 파싱할 수 없습니다." + this.additionalMessage; + } +} diff --git a/src/main/java/com/bsmlab/dfx/agent/support/exception/InCompleteMessageException.java b/src/main/java/com/bsmlab/dfx/agent/support/exception/InCompleteMessageException.java new file mode 100644 index 0000000..cb0e8ab --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/agent/support/exception/InCompleteMessageException.java @@ -0,0 +1,17 @@ +package com.bsmlab.dfx.agent.support.exception; + +public class InCompleteMessageException extends DfxException { + public InCompleteMessageException(String additionalMessage) { + super(additionalMessage); + } + + @Override + public String getMessage() { + return "incomplete message." + this.additionalMessage; + } + + @Override + public String getLocalizedMessage() { + return "메시지가 불완전합니다." + this.additionalMessage; + } +} diff --git a/src/main/java/com/bsmlab/dfx/agent/support/exception/NullMessageException.java b/src/main/java/com/bsmlab/dfx/agent/support/exception/NullMessageException.java new file mode 100644 index 0000000..a23e3a4 --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/agent/support/exception/NullMessageException.java @@ -0,0 +1,17 @@ +package com.bsmlab.dfx.agent.support.exception; + +public class NullMessageException extends DfxException { + public NullMessageException(String addtitionalMessage) { + super(addtitionalMessage); + } + + @Override + public String getMessage() { + return "cannot find message contents." + this.additionalMessage; + } + + @Override + public String getLocalizedMessage() { + return "파싱한 메시지의 내용을 찾을 수 없습니다." + this.additionalMessage; + } +} diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 0000000..aa334af --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,10 @@ + +ooo. ooooo o o .oo o +8 `8. 8 `b d' .P 8 8 +8 `8 o8oo `bd' .P 8 .oPYo. .oPYo. odYo. o8P +8 8 8 .PY. oPooo8 8 8 8oooo8 8' `8 8 +8 .P 8 .P Y. .P 8 8 8 8. 8 8 8 +8ooo' 8 .P Y. .P 8 `YooP8 `Yooo' 8 8 8 +.....:::..::::..::::..:::..:::::..:....8 :.....:..::..::..: +::::::::::::::::::::::::::::::::::::ooP'.:::::::::::::::::: +::::::::::::::::::::::::::::::::::::...:::::::::::::::::::: \ No newline at end of file diff --git a/src/main/resources/mapper/listener/message.xml b/src/main/resources/mapper/listener/message.xml index 100e09f..13d0281 100644 --- a/src/main/resources/mapper/listener/message.xml +++ b/src/main/resources/mapper/listener/message.xml @@ -1,12 +1,18 @@ - - + + + INSERT INTO TB_RECEIVE_MESSAGE ( - SENDER_HOST_ID, MESSAGE_UUID, RECEIVED_TIMESTAMP, MESSAGE_TYPE, DATA, PROCESS_STATUS + SENDER_HOST_ID, SENDER_TIMESTAMP, MESSAGE_UUID, MESSAGE_TYPE, RECEIVED_TIMESTAMP, RECIPIENT_HOST_ID, RECIPIENT_DROP_BOX, DATA, PROCESS_STATUS ) VALUES ( - #{SENDER_HOST_ID}, #{MESSAGE_UUID}, #{RECEIVED_TIMESTAMP}, #{MESSAGE_TYPE}, #{DATA}, #{PROCESS_STATUS} + #{senderHostId}, #{senderTimestamp}, #{messageUuid}, #{messageType}, #{receivedTimestamp}, #{recipientHostId}, #{recipientDropBox}, #{data: CLOB}, #{processStatus} ); - \ No newline at end of file +