diff --git a/src/database/create-database.sql b/src/database/create-database.sql index 790e3ae..370b83c 100644 --- a/src/database/create-database.sql +++ b/src/database/create-database.sql @@ -99,7 +99,7 @@ COMMENT ON COLUMN TB_DFX_POSTMAN.RECIPIENT_DROPBOX_ID IS '수신자 DROPBOX ID'; COMMENT ON COLUMN TB_DFX_POSTMAN.DESCRIPTION IS '설명'; -CREATE TABLE TB_DFX_AGENT_MESSAGE_HISTORY ( +CREATE TABLE TB_DFX_AGENT_MESSAGE ( MESSAGE_UUID VARCHAR(36) NOT NULL , SENDER_AGENT_ID VARCHAR(256) NOT NULL , SENDER_POSTMAN_ID VARCHAR(256) NOT NULL @@ -113,11 +113,50 @@ CREATE TABLE TB_DFX_AGENT_MESSAGE_HISTORY ( , MESSAGE_DATA TEXT , MESSAGE_DATA_COUNT DECIMAL(9) DEFAULT 0 , PROCESS_ACK_TS TIMESTAMPTZ(3) - , CONSTRAINT PK_DFX_AGENT_MESSAGE_HISTORY PRIMARY KEY (SENDER_AGENT_ID, MESSAGE_UUID) + , CONSTRAINT PK_DFX_AGENT_MESSAGE PRIMARY KEY (MESSAGE_UUID, SENDER_AGENT_ID) +); +CREATE INDEX IX_DFX_AGENT_MESSAGE_1 ON TB_DFX_AGENT_MESSAGE (RECIPIENT_AGENT_ID); +CREATE INDEX IX_DFX_AGENT_MESSAGE_2 ON TB_DFX_AGENT_MESSAGE (CONSOLE_RECEIVE_TS); +COMMENT ON TABLE TB_DFX_AGENT_MESSAGE IS '에이전트간 메시지'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE.MESSAGE_UUID IS 'MESSAGE UUID'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE.SENDER_AGENT_ID IS '송신 에이전트 ID'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE.SENDER_POSTMAN_ID IS '송신 에이전트 POSTMAN ID'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE.SENDER_TS IS '송신 시간'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE.RECIPIENT_AGENT_ID IS '수신 에이전트 ID'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE.RECIPIENT_DROPBOX_ID IS '수신 DROPBOX ID'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE.RECIPIENT_TS IS '수신 시간'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE.MESSAGE_TYPE_CODE IS '메시지 타입 코드 CODE_GROUP: MESSAGE_TYPE'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE.CONSOLE_RECEIVE_TS IS '콘솔 수신 시간'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE.PROCESS_STATUS_CODE IS '프로세스 상태 코드 CODE_GROUP: PROCESS_STATUS'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE.MESSAGE_DATA IS '메시지 데이터'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE.MESSAGE_DATA_COUNT IS '메시지 데이터 갯수'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE.PROCESS_ACK_TS IS '처리결과 수신 시간'; + + +CREATE SEQUENCE SQ_DFX_AGENT_MESSAGE_HISTORY START WITH 1 MINVALUE 1 MAXVALUE 999999999999999999 INCREMENT BY 1 CACHE 1; +CREATE TABLE TB_DFX_AGENT_MESSAGE_HISTORY ( + AGENT_MESSAGE_HISTORY_SEQ DECIMAL(18) NOT NULL + , MESSAGE_UUID VARCHAR(36) NOT NULL + , SENDER_AGENT_ID VARCHAR(256) NOT NULL + , SENDER_POSTMAN_ID VARCHAR(256) NOT NULL + , SENDER_TS TIMESTAMPTZ(3) + , RECIPIENT_AGENT_ID VARCHAR(256) NOT NULL + , RECIPIENT_DROPBOX_ID VARCHAR(256) NOT NULL + , RECIPIENT_TS TIMESTAMPTZ(3) + , MESSAGE_TYPE_CODE VARCHAR(64) + , CONSOLE_RECEIVE_TS TIMESTAMPTZ(3) + , PROCESS_STATUS_CODE VARCHAR(64) + , MESSAGE_DATA TEXT + , MESSAGE_DATA_COUNT DECIMAL(9) DEFAULT 0 + , PROCESS_ACK_TS TIMESTAMPTZ(3) + , CONSTRAINT PK_DFX_AGENT_MESSAGE_HISTORY PRIMARY KEY (AGENT_MESSAGE_HISTORY_SEQ) ); -CREATE INDEX IX_DFX_AGENT_MESSAGE_HISTORY_1 ON TB_DFX_AGENT_MESSAGE_HISTORY (RECIPIENT_AGENT_ID); -CREATE INDEX IX_DFX_AGENT_MESSAGE_HISTORY_2 ON TB_DFX_AGENT_MESSAGE_HISTORY (CONSOLE_RECEIVE_TS); -COMMENT ON TABLE TB_DFX_AGENT_MESSAGE_HISTORY IS '에이전트간 메시지 송신 이력'; +CREATE INDEX IX_DFX_AGENT_MESSAGE_HISTORY_1 ON TB_DFX_AGENT_MESSAGE_HISTORY (SENDER_AGENT_ID); +CREATE INDEX IX_DFX_AGENT_MESSAGE_HISTORY_2 ON TB_DFX_AGENT_MESSAGE_HISTORY (MESSAGE_UUID); +CREATE INDEX IX_DFX_AGENT_MESSAGE_HISTORY_3 ON TB_DFX_AGENT_MESSAGE_HISTORY (RECIPIENT_AGENT_ID); +CREATE INDEX IX_DFX_AGENT_MESSAGE_HISTORY_4 ON TB_DFX_AGENT_MESSAGE_HISTORY (CONSOLE_RECEIVE_TS); +COMMENT ON TABLE TB_DFX_AGENT_MESSAGE_HISTORY IS '에이전트간 메시지 송수신 이력'; +COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE_HISTORY.AGENT_MESSAGE_HISTORY_SEQ IS '메시지 송신 이력 순번 SQ_DFX_AGENT_MESSAGE_HISTORY'; COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE_HISTORY.MESSAGE_UUID IS 'MESSAGE UUID'; COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE_HISTORY.SENDER_AGENT_ID IS '송신 에이전트 ID'; COMMENT ON COLUMN TB_DFX_AGENT_MESSAGE_HISTORY.SENDER_POSTMAN_ID IS '송신 에이전트 POSTMAN ID'; diff --git a/src/main/front/src/router/index.js b/src/main/front/src/router/index.js index 5c9b0ff..dc5f632 100644 --- a/src/main/front/src/router/index.js +++ b/src/main/front/src/router/index.js @@ -26,6 +26,13 @@ const router = createRouter({ props: { contentId: 'agent-manage-view' }, meta: { isRequiredAuth: true }, }, + { + path: '/message-history.html', + name: 'message-history', + component: MainView, + props: { contentId: 'message-history-view' }, + meta: { isRequiredAuth: true }, + }, ], }) diff --git a/src/main/front/src/views/AgentManageView.vue b/src/main/front/src/views/AgentManageView.vue index 10cbfd0..19f1f1f 100644 --- a/src/main/front/src/views/AgentManageView.vue +++ b/src/main/front/src/views/AgentManageView.vue @@ -54,7 +54,7 @@ onMounted(async () => { - no data. + no datas. diff --git a/src/main/front/src/views/MainView.vue b/src/main/front/src/views/MainView.vue index 895857c..abf8bf5 100644 --- a/src/main/front/src/views/MainView.vue +++ b/src/main/front/src/views/MainView.vue @@ -4,6 +4,7 @@ import { userApi } from '@/components/userInfo' import { useRouter, RouterLink } from 'vue-router' import DashboardView from './DashboardView.vue' import AgentManageView from './AgentManageView.vue' +import MessageHistoryView from './MessageHistoryView.vue' import { computed } from 'vue' const props = defineProps({ @@ -24,6 +25,8 @@ const currentContent = computed(() => { return DashboardView } else if (props.contentId == 'agent-manage-view') { return AgentManageView + } else if (props.contentId == 'message-history-view') { + return MessageHistoryView } else { return DashboardView } @@ -63,7 +66,7 @@ const currentContent = computed(() => { Agents
  • - History + Messages
  • diff --git a/src/main/front/src/views/MessageHistoryView.vue b/src/main/front/src/views/MessageHistoryView.vue new file mode 100644 index 0000000..0a090f3 --- /dev/null +++ b/src/main/front/src/views/MessageHistoryView.vue @@ -0,0 +1,68 @@ + + + + + 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 3350d2c..0b1a647 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 @@ -27,6 +27,8 @@ public class AckDto { RECEIVE_SUCCESS, RECEIVE_FAIL, PROCESS_SUCCESS, - PROCESS_FAIL + PROCESS_FAIL, + TRANSFER_SUCCESS, + TRANSFER_FAIL } } 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 36ff060..1851e0e 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 @@ -28,6 +28,7 @@ public class ReceiveMessageDto implements Serializable { private ProcessStatus processStatus; public static enum ProcessStatus { + PROCESS_SEND, PROCESS_RECEIVED, PROCESS_DONE, PROCESS_NOT_POSSIBLE, diff --git a/src/main/java/com/bsmlab/dfx/agent/support/MessageUtils.java b/src/main/java/com/bsmlab/dfx/agent/support/MessageUtils.java index 75cb43d..d991030 100644 --- a/src/main/java/com/bsmlab/dfx/agent/support/MessageUtils.java +++ b/src/main/java/com/bsmlab/dfx/agent/support/MessageUtils.java @@ -14,19 +14,16 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.StringUtils; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; +import java.util.*; @Slf4j public class MessageUtils { private MessageUtils() {}; + private static final ObjectMapper objectMapper = new ObjectMapper(); @SuppressWarnings("unchecked") 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>() {}); @@ -58,6 +55,16 @@ public class MessageUtils { throw new InCompleteMessageException("senderTimestamp 값의 형식이 숫자형식이 아닙니다. " + senderTimeStampString); } } + String senderPostmanId; + if(map.get("senderPostmanId") == null) { + throw new InCompleteMessageException("senderPostmanId 엘리먼트를 찾을 수 없습니다."); + } + else if(StringUtils.isBlank(String.valueOf(map.get("senderPostmanId")))) { + throw new InCompleteMessageException("senderPostmanId 값을 찾을 수 없습니다."); + } + else { + senderPostmanId = String.valueOf(map.get("senderPostmanId")); + } String messageUuid; if(map.get("messageUuid") == null) { throw new InCompleteMessageException("messageUuid 엘리먼트를 찾을 수 없습니다."); @@ -123,6 +130,18 @@ public class MessageUtils { else { recipientDropBoxId = String.valueOf(map.get("recipientDropBoxId")); } + List routingHostList = null; + if(map.get("routingHostList") == null) { + throw new InCompleteMessageException("routingHostList 엘리먼트를 찾을 수 없습니다."); + } + else { + List> mapList = (List>) map.get("routingHostList"); + routingHostList = new ArrayList<>(); + for(Map map1 : mapList) { + ReceiveMessageDto.RoutingHost routingHost = ReceiveMessageDto.RoutingHost.builder().hostId(String.valueOf(map1.get("hostId"))).receivedTimestamp(Long.parseLong(String.valueOf(map1.get("receivedTimestamp")))).build(); + routingHostList.add(routingHost); + } + } String dataString; if(map.get("data") == null) { throw new InCompleteMessageException("data 엘리먼트를 찾을 수 없습니다."); @@ -133,11 +152,22 @@ public class MessageUtils { else { dataString = String.valueOf(map.get("data")); } + ReceiveMessageDto.ProcessStatus processStatus; + if(map.get("processStatus") == null) { + throw new InCompleteMessageException("processStatus 엘리먼트를 찾을 수 없습니다."); + } + else if(StringUtils.isBlank(String.valueOf(map.get("processStatus")))) { + throw new InCompleteMessageException("processStatus 값을 찾을 수 없습니다."); + } + else { + processStatus = EnumUtils.getEnum(ReceiveMessageDto.ProcessStatus.class, String.valueOf(map.get("processStatus"))); + } receiveMessageDto = ReceiveMessageDto.builder() - .senderHostId(senderHostId).senderTimestamp(senderTimestamp) + .senderHostId(senderHostId).senderTimestamp(senderTimestamp).senderPostmanId(senderPostmanId) .messageUuid(messageUuid).messageType(messageType).receivedTimestamp(receivedTimestamp) .recipientHostId(recipientHostId).recipientDropBoxId(recipientDropBoxId) - .data(dataString).attachFileList(new ArrayList<>()).processStatus(ReceiveMessageDto.ProcessStatus.PROCESS_RECEIVED) + .routingHostList(routingHostList) + .data(dataString).attachFileList(new ArrayList<>()).processStatus(processStatus) .build(); } catch(JsonProcessingException e) { @@ -147,7 +177,6 @@ public class MessageUtils { } public static AckDto toAckDto(String messageJsonString) throws NullMessageException, IllegalMessageException { - ObjectMapper objectMapper = new ObjectMapper(); Map map = null; AckDto ackDto; try { @@ -170,7 +199,6 @@ public class MessageUtils { public static CommandDto toCommandDto(String messageJsonString) throws IllegalMessageException, NullMessageException { CommandDto commandDto; - ObjectMapper objectMapper = new ObjectMapper(); Map map = null; try { map = objectMapper.readValue(messageJsonString, new TypeReference>() {}); diff --git a/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/DfxAgentInfoController.java b/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/DfxAgentInfoController.java index 809a0da..585bbab 100644 --- a/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/DfxAgentInfoController.java +++ b/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/DfxAgentInfoController.java @@ -2,10 +2,7 @@ package com.bsmlab.dfx.dfxconsole.app.agent; import com.bsmlab.dfx.agent.config.AgentConfigDto; import com.bsmlab.dfx.agent.listener.dto.ReceiveMessageDto; -import com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentInfoDto; -import com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentInfoService; -import com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageHistoryDto; -import com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageHistoryService; +import com.bsmlab.dfx.dfxconsole.app.agent.service.*; import com.bsmlab.dfx.dfxconsole.framework.support.ResponseUtils; import com.fasterxml.jackson.core.JsonProcessingException; import lombok.RequiredArgsConstructor; @@ -50,19 +47,25 @@ public class DfxAgentInfoController { } } - @PostMapping("/app-api/agent/getAgentMessageHistoryDtoList") - public ResponseEntity> getAgentMessageHistoryDtoList() { - List dfxAgentMessageHistoryDtoList = dfxAgentMessageHistoryService.selectDfxAgentMessageHistoryDtoList(); - return ResponseEntity.ok().body(dfxAgentMessageHistoryDtoList); + @PostMapping("/app-api/agent/getAgentMessageDtoList") + public ResponseEntity> getAgentMessageDtoList() { + List dfxAgentMessageDtoList = dfxAgentMessageHistoryService.selectDfxAgentMessageDtoList(); + return ResponseEntity.ok().body(dfxAgentMessageDtoList); } @PostMapping("/app-api/agent/getAgentMessageData") - public ResponseEntity getAgentMessageData(DfxAgentMessageHistoryDto dfxAgentMessageHistoryDto) { + public ResponseEntity getAgentMessageData(DfxAgentMessageDto dfxAgentMessageDto) { try { - List> dataMapList = this.dfxAgentMessageHistoryService.selectDfxAgentMessageData(dfxAgentMessageHistoryDto); + List> dataMapList = this.dfxAgentMessageHistoryService.selectDfxAgentMessageData(dfxAgentMessageDto); return ResponseEntity.ok().body(dataMapList); } catch (JsonProcessingException e) { return ResponseEntity.internalServerError().body(ResponseUtils.toExceptionResponseDto(e)); } } + + @PostMapping("/app-api/agent/getAgentMessageHistoryDtoList") + public ResponseEntity> getAgentMessageHistoryDtoList() { + List dfxAgentMessageHistoryDtoList = dfxAgentMessageHistoryService.selectDfxAgentMessageHistoryDtoList(); + return ResponseEntity.ok().body(dfxAgentMessageHistoryDtoList); + } } diff --git a/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageDto.java b/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageDto.java new file mode 100644 index 0000000..13fdcf3 --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageDto.java @@ -0,0 +1,31 @@ +package com.bsmlab.dfx.dfxconsole.app.agent.service; + +import lombok.*; + +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder +@ToString +public class DfxAgentMessageDto { + private String messageUuid; + private String senderAgentId; + private String senderHostName; + private String senderPostmanId; + private long senderTs; + private String senderTimeString; + private String recipientAgentId; + private String recipientHostName; + private String recipientDropboxId; + private long recipientTs; + private String recipientTimeString; + private String messageTypeCode; + private long consoleReceiveTs; + private String consoleReceiveTimeString; + private String processStatusCode; + private String messageData; + private int messageDataCount; + private long processAckTs; + private long processAckTimeString; +} diff --git a/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageHistoryDto.java b/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageHistoryDto.java index ee96f3a..ab47a26 100644 --- a/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageHistoryDto.java +++ b/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageHistoryDto.java @@ -9,6 +9,7 @@ import lombok.*; @Builder @ToString public class DfxAgentMessageHistoryDto { + private long agentMessageHistorySeq; private String messageUuid; private String senderAgentId; private String senderHostName; diff --git a/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageHistoryMapper.java b/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageHistoryMapper.java index ef0edc2..09d57fa 100644 --- a/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageHistoryMapper.java +++ b/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageHistoryMapper.java @@ -6,6 +6,9 @@ import java.util.List; @Mapper public interface DfxAgentMessageHistoryMapper { + List selectDfxAgentMessageList(); + DfxAgentMessageDto selectDfxAgentMessageBySenderAgentIdAndMessageUuid(DfxAgentMessageDto dfxAgentMessageDto); + void insertDfxAgentMessage(DfxAgentMessageDto dfxAgentMessageDto); List selectDfxAgentMessageHistoryList(); DfxAgentMessageHistoryDto selectDfxAgentMessageHistoryBySenderAgentIdAndMessageUuid(DfxAgentMessageHistoryDto dfxAgentMessageHistoryDto); void insertDfxAgentMessageHistory(DfxAgentMessageHistoryDto dfxAgentMessageHistoryDto); diff --git a/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageHistoryService.java b/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageHistoryService.java index a841139..f0dccff 100644 --- a/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageHistoryService.java +++ b/src/main/java/com/bsmlab/dfx/dfxconsole/app/agent/service/DfxAgentMessageHistoryService.java @@ -16,15 +16,20 @@ import java.util.Map; public class DfxAgentMessageHistoryService { private final DfxAgentMessageHistoryMapper dfxAgentMessageHistoryMapper; - public List selectDfxAgentMessageHistoryDtoList() { - List dfxAgentMessageHistoryDtoList = dfxAgentMessageHistoryMapper.selectDfxAgentMessageHistoryList(); - return dfxAgentMessageHistoryDtoList; + public List selectDfxAgentMessageDtoList() { + List dfxAgentMessageDtoList = dfxAgentMessageHistoryMapper.selectDfxAgentMessageList(); + return dfxAgentMessageDtoList; } - public List> selectDfxAgentMessageData(DfxAgentMessageHistoryDto inputDfxAgentMessageHistoryDto) throws JsonProcessingException { - DfxAgentMessageHistoryDto dfxAgentMessageHistoryDto = dfxAgentMessageHistoryMapper.selectDfxAgentMessageHistoryBySenderAgentIdAndMessageUuid(inputDfxAgentMessageHistoryDto); + public List> selectDfxAgentMessageData(DfxAgentMessageDto inputDfxAgentMessageDto) throws JsonProcessingException { + DfxAgentMessageDto dfxAgentMessageDto = dfxAgentMessageHistoryMapper.selectDfxAgentMessageBySenderAgentIdAndMessageUuid(inputDfxAgentMessageDto); ObjectMapper objectMapper = new ObjectMapper(); - List> dataMapList = objectMapper.readValue(dfxAgentMessageHistoryDto.getMessageData(), new TypeReference>>() {}); + List> dataMapList = objectMapper.readValue(dfxAgentMessageDto.getMessageData(), new TypeReference>>() {}); return dataMapList; } + + public List selectDfxAgentMessageHistoryDtoList() { + List dfxAgentMessageHistoryDtoList = dfxAgentMessageHistoryMapper.selectDfxAgentMessageHistoryList(); + return dfxAgentMessageHistoryDtoList; + } } diff --git a/src/main/java/com/bsmlab/dfx/dfxconsole/app/communicate/service/ListenerService.java b/src/main/java/com/bsmlab/dfx/dfxconsole/app/communicate/service/ListenerService.java index 2a5c580..0d1d296 100644 --- a/src/main/java/com/bsmlab/dfx/dfxconsole/app/communicate/service/ListenerService.java +++ b/src/main/java/com/bsmlab/dfx/dfxconsole/app/communicate/service/ListenerService.java @@ -19,6 +19,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Map; @@ -30,6 +31,7 @@ public class ListenerService { private final DfxAgentInfoService dfxAgentInfoService; private final DfxAgentMessageHistoryMapper dfxAgentMessageHistoryMapper; + @Transactional public AckDto receiveCommand(String messageJsonString) { AckDto ackDto; CommandDto commandDto; @@ -73,6 +75,7 @@ public class ListenerService { * @param messageJsonString * @return */ + @Transactional public AckDto receiveMessage(String messageJsonString) { AckDto ackDto = null; try { @@ -88,21 +91,17 @@ public class ListenerService { DfxAgentMessageHistoryDto dfxAgentMessageHistoryDto = DfxAgentMessageHistoryDto.builder() .senderAgentId(receiveMessageDto.getSenderHostId()) .senderTs(receiveMessageDto.getSenderTimestamp()) + .senderPostmanId(receiveMessageDto.getSenderPostmanId()) .recipientAgentId(receiveMessageDto.getRecipientHostId()) .recipientTs(receiveMessageDto.getReceivedTimestamp()) + .recipientDropboxId(receiveMessageDto.getRecipientDropBoxId()) .messageUuid(receiveMessageDto.getMessageUuid()) .messageTypeCode(receiveMessageDto.getMessageType().name()) .consoleReceiveTs(System.currentTimeMillis()) .processStatusCode(receiveMessageDto.getProcessStatus().name()) .messageData(receiveMessageDto.getData()).messageDataCount(messageDataCount) .build(); - if(receiveMessageDto.getProcessStatus() == ReceiveMessageDto.ProcessStatus.PROCESS_RECEIVED) { - dfxAgentMessageHistoryMapper.insertDfxAgentMessageHistory(dfxAgentMessageHistoryDto); - } - else if(receiveMessageDto.getProcessStatus() == ReceiveMessageDto.ProcessStatus.PROCESS_DONE) { - // update PROCESS_DONE - dfxAgentMessageHistoryMapper.updateDfxAgentMessageHistoryForProcessAck((dfxAgentMessageHistoryDto)); - } + dfxAgentMessageHistoryMapper.insertDfxAgentMessageHistory(dfxAgentMessageHistoryDto); ackDto = AckDto.builder().result(AckDto.ResultType.RECEIVE_SUCCESS).build(); } catch (IllegalMessageException | NullMessageException | InCompleteMessageException e) { log.error("{}", e, e); diff --git a/src/main/java/com/bsmlab/dfx/dfxconsole/framework/support/mybatis/RefreshableSqlSessionFactoryBean.java b/src/main/java/com/bsmlab/dfx/dfxconsole/framework/support/mybatis/RefreshableSqlSessionFactoryBean.java new file mode 100644 index 0000000..9a4ca45 --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/dfxconsole/framework/support/mybatis/RefreshableSqlSessionFactoryBean.java @@ -0,0 +1,181 @@ +package com.bsmlab.dfx.dfxconsole.framework.support.mybatis; + +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.*; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +@Slf4j +public class RefreshableSqlSessionFactoryBean extends SqlSessionFactoryBean implements DisposableBean { + private SqlSessionFactory proxy; + private int interval = 500; + private Timer timer; + private TimerTask task; + private Resource[] mapperLocations; + private boolean running = false; + private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); + private final Lock r = rwl.readLock(); + private final Lock w = rwl.writeLock(); + + @Override + public void setMapperLocations(Resource[] mapperLocations) { + super.setMapperLocations(mapperLocations); + this.mapperLocations = mapperLocations; + } + + public void setInterval(int interval) { + this.interval = interval; + } + + public void refresh() throws Exception { + if(log.isInfoEnabled()) { + log.info("> Refreshing SQL Mapper Configuration..."); + } + w.lock(); + try { + super.afterPropertiesSet(); + // refresh()가 호출될 때 기존 proxy를 재생성하여 기존 세션과 충돌을 방지한다. + proxy = (SqlSessionFactory) Proxy.newProxyInstance( + SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSessionFactory.class} + , (proxy, method, args) -> method.invoke(getParentObject(), args) + ); + } finally { + w.unlock(); + } + } + + @Override + public void afterPropertiesSet() throws Exception { + super.afterPropertiesSet(); + setRefreshable(); + } + + private void setRefreshable() { + proxy = (SqlSessionFactory) Proxy.newProxyInstance( + SqlSessionFactory.class.getClassLoader(), + new Class[]{SqlSessionFactory.class}, + new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return method.invoke(getParentObject(), args); + } + }); + + task = new TimerTask() { + private Map map = new HashMap<>(); + + public void run() { + if(isModified()) { + try { + refresh(); + } catch(Exception e) { + log.error("caught exception", e); + } + } + } + + private boolean isModified() { + boolean retVal = false; + if(mapperLocations != null) { + for(int i = 0; i < mapperLocations.length; i++) { + Resource mappingLocation = mapperLocations[i]; + retVal |= findModifiedResource(mappingLocation); + } + } + return retVal; + } + + private boolean findModifiedResource(Resource resource) { + boolean retVal = false; + List modifiedResources = new ArrayList<>(); + try { + long modified = resource.lastModified(); + + if(map.containsKey(resource)) { + long lastModified = ((Long) map.get(resource)) .longValue(); + + if(lastModified != modified) { + map.put(resource, modified); + + //modifiedResources.add(resource.getDescription()); // 전체경로 + modifiedResources.add(resource.getFilename()); // 파일명 + + retVal = true; + } + } else { + map.put(resource, modified); + } + } catch (IOException e) { + log.error("caught exception", e); + } + if(retVal) { + if(log.isInfoEnabled()) { + log.info("======================================================================================"); + log.info("> Update File name : " + modifiedResources); + } + } + return retVal; + } + }; + + timer = new Timer(true); + resetInterval(); + } + + private Object getParentObject() throws Exception { + r.lock(); + try { + return super.getObject(); + } finally { + r.unlock(); + } + } + + @Override + public SqlSessionFactory getObject() { + return this.proxy; + } + + @Override + public Class getObjectType() { + return (this.proxy != null ? this.proxy.getClass() : SqlSessionFactory.class); + } + + @Override + public boolean isSingleton() { + return true; + } + + public void setCheckInterval(int ms) { + interval = ms; + if(timer != null) { + resetInterval(); + } + } + + private void resetInterval() { + if(running) { + timer.cancel(); + + running = false; + } + + if(interval > 0) { + timer.schedule(task, 0, interval); running = true; + } + } + + @Override + public void destroy() throws Exception { + timer.cancel(); + } +} diff --git a/src/main/java/com/bsmlab/dfx/dfxconsole/framework/support/mybatis/handler/EpochMillisWithTimeZoneTypeHandler.java b/src/main/java/com/bsmlab/dfx/dfxconsole/framework/support/mybatis/handler/EpochMillisWithTimeZoneTypeHandler.java index d332f6d..3146dfa 100644 --- a/src/main/java/com/bsmlab/dfx/dfxconsole/framework/support/mybatis/handler/EpochMillisWithTimeZoneTypeHandler.java +++ b/src/main/java/com/bsmlab/dfx/dfxconsole/framework/support/mybatis/handler/EpochMillisWithTimeZoneTypeHandler.java @@ -10,6 +10,15 @@ import java.sql.*; @MappedTypes(Long.class) @MappedJdbcTypes(JdbcType.TIMESTAMP_WITH_TIMEZONE) public class EpochMillisWithTimeZoneTypeHandler extends BaseTypeHandler { + @Override + public void setParameter(PreparedStatement ps, int i, Long parameter, JdbcType jdbcType) throws SQLException { + if(parameter == 0L) { + ps.setNull(i, Types.TIMESTAMP_WITH_TIMEZONE); + } + else { + ps.setTimestamp(i, new Timestamp(parameter)); // 밀리초 → java.sql.Timestamp + } + } @Override public void setNonNullParameter(PreparedStatement ps, int i, Long parameter, JdbcType jdbcType) throws SQLException { ps.setTimestamp(i, new Timestamp(parameter)); // 밀리초 → java.sql.Timestamp diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 367b6fa..0b76167 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -51,6 +51,7 @@ mybatis: use-generated-keys: true mapper-locations: classpath*:/mapper/**/*.xml type-handlers-package: com.bsmlab.dfx.dfxconsole.framework.support.mybatis.handler + refresh-interval: 3000 logging: level: diff --git a/src/main/resources/mapper/app/dfx-agent-message-history.xml b/src/main/resources/mapper/app/dfx-agent-message-history.xml index b356dfb..1923aab 100644 --- a/src/main/resources/mapper/app/dfx-agent-message-history.xml +++ b/src/main/resources/mapper/app/dfx-agent-message-history.xml @@ -2,7 +2,7 @@ - - + + + + + + + + +