에이전트-콘솔 메시지 송수신 로직 개선

전송 이력 화면 진행중
main
semin.baek 5 months ago
parent 5e962963a8
commit 011ab54fd6

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

@ -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 },
},
],
})

@ -54,7 +54,7 @@ onMounted(async () => {
<td class="text-center"><button type="button" class="btn btn-primary btn-sm" @click.prevent="setDfaAgentInfoDto(dfxAgentInfoDto)">설정</button></td>
</tr>
<tr v-else>
<td scope="row" class="text-center" colspan="7">no data.</td>
<td scope="row" class="text-center" colspan="7">no datas.</td>
</tr>
</tbody>
</table>

@ -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(() => {
<RouterLink to="/agent-manage.html" class="nav-link" :class="{ active: props.contentId == 'agent-manage-view', 'link-body-emphasis': props.contentId != 'agent-manage-view' }" aria-current="page"> <i class="bi bi-cpu" style="font-size: 1.1rem; padding-right: 0.5rem"></i> Agents </RouterLink>
</li>
<li>
<a href="#" class="nav-link link-body-emphasis"> <i class="bi bi-card-checklist" style="font-size: 1.1rem; padding-right: 0.5rem"></i> History </a>
<RouterLink to="/message-history.html" class="nav-link" :class="{ active: props.contentId == 'message-history-view', 'link-body-emphasis': props.contentId != 'message-history-view' }" aria-current="page"> <i class="bi bi-card-checklist" style="font-size: 1.1rem; padding-right: 0.5rem"></i> Messages </RouterLink>
</li>
</ul>
</aside>

@ -0,0 +1,68 @@
<script setup>
import '@/assets/main.css'
import { useApiClient } from '@/main'
import { onMounted, ref } from 'vue'
const apiClient = useApiClient()
async function getAgentMessageDtoList() {
const response = await apiClient.post('/app-api/agent/getAgentMessageDtoList')
return response.data
}
let dfxAgentMessageDtoList = ref([])
function setDfaAgentInfoDto(dfxAgentMessageDto) {
alert(dfxAgentMessageDto)
}
onMounted(async () => {
dfxAgentMessageDtoList.value = await getAgentMessageDtoList()
})
</script>
<template>
<main class="dfx-main container w-100">
<div class="row">
<div class="col-12 pt-2 border-bottom">
<h3 class="h3">Dashboard</h3>
</div>
</div>
<div class="row">
<article class="col-12 pt-3">
<h5 class="h5">송수신 메시지 목록</h5>
<table class="table table-striped table-bordered align-middle">
<thead>
<tr>
<th scope="col" class="text-center">Message UUID</th>
<th scope="col" class="text-center">Sender (Postman)</th>
<th scope="col" class="text-center">Recipient (Dropbox)</th>
<th scope="col" class="text-center">Send</th>
<th scope="col" class="text-center">Receive</th>
<th scope="col" class="text-center">Status</th>
<th scope="col" class="text-center">Data count</th>
<th scope="col" class="text-center">Data</th>
</tr>
</thead>
<tbody class="table-group-divider">
<tr v-if="dfxAgentMessageDtoList.length > 0" v-for="dfxAgentMessageDto in dfxAgentMessageDtoList" :key="`${dfxAgentMessageDto.senderAgentId}-${dfxAgentMessageDto.messageUuid}`">
<td scope="row">{{ dfxAgentMessageDto.messageUuid }}</td>
<td class="text-center">{{ dfxAgentMessageDto.senderAgentId }}({{ dfxAgentMessageDto.senderPostmanId }})</td>
<td class="text-center">{{ dfxAgentMessageDto.recipientAgentId }}({{ dfxAgentMessageDto.receipientDropboxId }})</td>
<td class="text-center">{{ dfxAgentMessageDto.senderTimeString }}</td>
<td class="text-center">{{ dfxAgentMessageDto.recipientTimeString }}</td>
<td class="text-center">{{ dfxAgentMessageDto.processStatusCode }}</td>
<td class="text-right">{{ dfxAgentMessageDto.messageDateCount }}</td>
<td class="text-center"><button type="button" class="btn btn-primary btn-sm" @click.prevent="setDfaAgentInfoDto(dfxAgentInfoDto)">Data View</button></td>
</tr>
<tr v-else>
<td scope="row" class="text-center" colspan="8">no datas.</td>
</tr>
</tbody>
</table>
</article>
</div>
</main>
</template>
<style></style>

@ -27,6 +27,8 @@ public class AckDto {
RECEIVE_SUCCESS,
RECEIVE_FAIL,
PROCESS_SUCCESS,
PROCESS_FAIL
PROCESS_FAIL,
TRANSFER_SUCCESS,
TRANSFER_FAIL
}
}

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

@ -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<String, Object> map = null;
try {
map = objectMapper.readValue(messageJsonString, new TypeReference<HashMap<String, Object>>() {});
@ -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<ReceiveMessageDto.RoutingHost> routingHostList = null;
if(map.get("routingHostList") == null) {
throw new InCompleteMessageException("routingHostList 엘리먼트를 찾을 수 없습니다.");
}
else {
List<Map<String, Object>> mapList = (List<Map<String, Object>>) map.get("routingHostList");
routingHostList = new ArrayList<>();
for(Map<String, Object> 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<String, Object> 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<String, Object> map = null;
try {
map = objectMapper.readValue(messageJsonString, new TypeReference<Map<String, Object>>() {});

@ -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<List<DfxAgentMessageHistoryDto>> getAgentMessageHistoryDtoList() {
List<DfxAgentMessageHistoryDto> dfxAgentMessageHistoryDtoList = dfxAgentMessageHistoryService.selectDfxAgentMessageHistoryDtoList();
return ResponseEntity.ok().body(dfxAgentMessageHistoryDtoList);
@PostMapping("/app-api/agent/getAgentMessageDtoList")
public ResponseEntity<List<DfxAgentMessageDto>> getAgentMessageDtoList() {
List<DfxAgentMessageDto> 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<Map<String, Object>> dataMapList = this.dfxAgentMessageHistoryService.selectDfxAgentMessageData(dfxAgentMessageHistoryDto);
List<Map<String, Object>> 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<List<DfxAgentMessageHistoryDto>> getAgentMessageHistoryDtoList() {
List<DfxAgentMessageHistoryDto> dfxAgentMessageHistoryDtoList = dfxAgentMessageHistoryService.selectDfxAgentMessageHistoryDtoList();
return ResponseEntity.ok().body(dfxAgentMessageHistoryDtoList);
}
}

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

@ -9,6 +9,7 @@ import lombok.*;
@Builder
@ToString
public class DfxAgentMessageHistoryDto {
private long agentMessageHistorySeq;
private String messageUuid;
private String senderAgentId;
private String senderHostName;

@ -6,6 +6,9 @@ import java.util.List;
@Mapper
public interface DfxAgentMessageHistoryMapper {
List<DfxAgentMessageDto> selectDfxAgentMessageList();
DfxAgentMessageDto selectDfxAgentMessageBySenderAgentIdAndMessageUuid(DfxAgentMessageDto dfxAgentMessageDto);
void insertDfxAgentMessage(DfxAgentMessageDto dfxAgentMessageDto);
List<DfxAgentMessageHistoryDto> selectDfxAgentMessageHistoryList();
DfxAgentMessageHistoryDto selectDfxAgentMessageHistoryBySenderAgentIdAndMessageUuid(DfxAgentMessageHistoryDto dfxAgentMessageHistoryDto);
void insertDfxAgentMessageHistory(DfxAgentMessageHistoryDto dfxAgentMessageHistoryDto);

@ -16,15 +16,20 @@ import java.util.Map;
public class DfxAgentMessageHistoryService {
private final DfxAgentMessageHistoryMapper dfxAgentMessageHistoryMapper;
public List<DfxAgentMessageHistoryDto> selectDfxAgentMessageHistoryDtoList() {
List<DfxAgentMessageHistoryDto> dfxAgentMessageHistoryDtoList = dfxAgentMessageHistoryMapper.selectDfxAgentMessageHistoryList();
return dfxAgentMessageHistoryDtoList;
public List<DfxAgentMessageDto> selectDfxAgentMessageDtoList() {
List<DfxAgentMessageDto> dfxAgentMessageDtoList = dfxAgentMessageHistoryMapper.selectDfxAgentMessageList();
return dfxAgentMessageDtoList;
}
public List<Map<String, Object>> selectDfxAgentMessageData(DfxAgentMessageHistoryDto inputDfxAgentMessageHistoryDto) throws JsonProcessingException {
DfxAgentMessageHistoryDto dfxAgentMessageHistoryDto = dfxAgentMessageHistoryMapper.selectDfxAgentMessageHistoryBySenderAgentIdAndMessageUuid(inputDfxAgentMessageHistoryDto);
public List<Map<String, Object>> selectDfxAgentMessageData(DfxAgentMessageDto inputDfxAgentMessageDto) throws JsonProcessingException {
DfxAgentMessageDto dfxAgentMessageDto = dfxAgentMessageHistoryMapper.selectDfxAgentMessageBySenderAgentIdAndMessageUuid(inputDfxAgentMessageDto);
ObjectMapper objectMapper = new ObjectMapper();
List<Map<String, Object>> dataMapList = objectMapper.readValue(dfxAgentMessageHistoryDto.getMessageData(), new TypeReference<List<Map<String, Object>>>() {});
List<Map<String, Object>> dataMapList = objectMapper.readValue(dfxAgentMessageDto.getMessageData(), new TypeReference<List<Map<String, Object>>>() {});
return dataMapList;
}
public List<DfxAgentMessageHistoryDto> selectDfxAgentMessageHistoryDtoList() {
List<DfxAgentMessageHistoryDto> dfxAgentMessageHistoryDtoList = dfxAgentMessageHistoryMapper.selectDfxAgentMessageHistoryList();
return dfxAgentMessageHistoryDtoList;
}
}

@ -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));
}
ackDto = AckDto.builder().result(AckDto.ResultType.RECEIVE_SUCCESS).build();
} catch (IllegalMessageException | NullMessageException | InCompleteMessageException e) {
log.error("{}", e, e);

@ -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<Resource, Long> 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<String> 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<? extends SqlSessionFactory> 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();
}
}

@ -10,6 +10,15 @@ import java.sql.*;
@MappedTypes(Long.class)
@MappedJdbcTypes(JdbcType.TIMESTAMP_WITH_TIMEZONE)
public class EpochMillisWithTimeZoneTypeHandler extends BaseTypeHandler<Long> {
@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

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

@ -2,7 +2,7 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageHistoryMapper">
<select id="selectDfxAgentMessageHistoryList" resultType="com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageHistoryDto">
<select id="selectDfxAgentMessageList" resultType="com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageDto">
<![CDATA[
SELECT A.MESSAGE_UUID, A.SENDER_AGENT_ID, A.SENDER_POSTMAN_ID
, (EXTRACT(EPOCH FROM A.SENDER_TS) * 1000)::BIGINT AS SENDER_TS, TO_CHAR(A.SENDER_TS, 'YYYY-MM-DD HH24:MI:SS') AS SENDER_TIME_STRING
@ -14,8 +14,8 @@
, (EXTRACT(EPOCH FROM A.PROCESS_ACK_TS) * 1000)::BIGINT AS PROCESS_ACK_TS, TO_CHAR(A.PROCESS_ACK_TS, 'YYYY-MM-DD HH24:MI:SS') AS PROCESS_ACK_TIME_STRING
, B.HOST_NAME AS SENDER_HOST_NAME, C.HOST_NAME AS RECIPIENT_HOST_NAME
FROM (
SELECT SENDER_AGENT_ID, SENDER_TS, RECIPIENT_AGENT_ID, RECIPIENT_TS, MESSAGE_UUID, MESSAGE_TYPE_CODE, CONSOLE_RECEIVE_TS, PROCESS_STATUS_CODE, MESSAGE_DATA, MESSAGE_DATA_COUNT
FROM TB_DFX_AGENT_MESSAGE_HISTORY
SELECT SENDER_AGENT_ID, SENDER_POSTMAN_ID, SENDER_TS, RECIPIENT_AGENT_ID, RECIPIENT_DROPBOX_ID, RECIPIENT_TS, MESSAGE_UUID, MESSAGE_TYPE_CODE, CONSOLE_RECEIVE_TS, PROCESS_STATUS_CODE, MESSAGE_DATA, MESSAGE_DATA_COUNT, PROCESS_ACK_TS
FROM TB_DFX_AGENT_MESSAGE
WHERE 1 = 1
ORDER BY CONSOLE_RECEIVE_TS
LIMIT 20
@ -29,7 +29,7 @@
]]>
</select>
<select id="selectDfxAgentMessageHistoryBySenderAgentIdAndMessageUuid" parameterType="com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageHistoryDto" resultType="com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageHistoryDto">
<select id="selectDfxAgentMessageBySenderAgentIdAndMessageUuid" parameterType="com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageDto" resultType="com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageDto">
<![CDATA[
SELECT A.MESSAGE_UUID, A.SENDER_AGENT_ID, A.SENDER_POSTMAN_ID
, (EXTRACT(EPOCH FROM A.SENDER_TS) * 1000)::BIGINT AS SENDER_TS, TO_CHAR(A.SENDER_TS, 'YYYY-MM-DD HH24:MI:SS') AS SENDER_TIME_STRING
@ -40,6 +40,78 @@
, A.PROCESS_STATUS_CODE, A.MESSAGE_DATA, A.MESSAGE_DATA_COUNT
, (EXTRACT(EPOCH FROM A.PROCESS_ACK_TS) * 1000)::BIGINT AS PROCESS_ACK_TS, TO_CHAR(A.PROCESS_ACK_TS, 'YYYY-MM-DD HH24:MI:SS') AS PROCESS_ACK_TIME_STRING
, B.HOST_NAME AS SENDER_HOST_NAME, C.HOST_NAME AS RECIPIENT_HOST_NAME
FROM TB_DFX_AGENT_MESSAGE A
JOIN TB_DFX_AGENT_INFO B ON (A.SENDER_AGENT_ID = B.AGENT_ID)
JOIN TB_DFX_AGENT_INFO C ON (A.RECIPIENT_AGENT_ID = C.AGENT_ID)
WHERE 1 = 1
AND A.SENDER_AGENT_ID = #{senderAgentId}
AND A.MESSAGE_UUID = #{messageUuid}
]]>
</select>
<insert id="insertDfxAgentMessage" parameterType="com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageDto">
<![CDATA[
INSERT INTO TB_DFX_AGENT_MESSAGE (
MESSAGE_UUID, SENDER_AGENT_ID, SENDER_POSTMAN_ID
, SENDER_TS
, RECIPIENT_AGENT_ID, RECIPIENT_DROPBOX_ID
, RECIPIENT_TS
, MESSAGE_TYPE_CODE
, CONSOLE_RECEIVE_TS
, PROCESS_STATUS_CODE, MESSAGE_DATA, MESSAGE_DATA_COUNT
, PROCESS_ACK_TS
)
VALUES (
#{messageUuid}, #{senderAgentId}, #{senderPostmanId}
, #{senderTs, jdbcType=TIMESTAMP_WITH_TIMEZONE, javaType=Long}
, #{recipientAgentId}, #{recipientDropboxId}
, #{recipientTs, jdbcType=TIMESTAMP_WITH_TIMEZONE, javaType=Long}
, #{messageTypeCode}
, #{consoleReceiveTs, jdbcType=TIMESTAMP_WITH_TIMEZONE, javaType=Long}
, #{processStatusCode}, #{messageData}, #{messageDataCount}
, #{processAckTs, jdbcType=TIMESTAMP_WITH_TIMEZONE, javaType=Long}
)
]]>
</insert>
<select id="selectDfxAgentMessageHistoryList" resultType="com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageHistoryDto">
<![CDATA[
SELECT A.AGENT_MESSAGE_HISTORY_SEQ, A.MESSAGE_UUID, A.SENDER_AGENT_ID, A.SENDER_POSTMAN_ID
, (EXTRACT(EPOCH FROM A.SENDER_TS) * 1000)::BIGINT AS SENDER_TS, TO_CHAR(A.SENDER_TS, 'YYYY-MM-DD HH24:MI:SS') AS SENDER_TIME_STRING
, A.RECIPIENT_AGENT_ID, A.RECIPIENT_DROPBOX_ID
, (EXTRACT(EPOCH FROM A.RECIPIENT_TS) * 1000)::BIGINT AS RECIPIENT_TS, TO_CHAR(A.RECIPIENT_TS, 'YYYY-MM-DD HH24:MI:SS') AS RECIPIENT_TIME_STRING
, A.MESSAGE_TYPE_CODE
, (EXTRACT(EPOCH FROM A.CONSOLE_RECEIVE_TS) * 1000)::BIGINT AS CONSOLE_RECEIVE_TS, TO_CHAR(A.CONSOLE_RECEIVE_TS, 'YYYY-MM-DD HH24:MI:SS') AS CONSOLE_RECEIVE_TIME_STRING
, A.PROCESS_STATUS_CODE, A.MESSAGE_DATA, A.MESSAGE_DATA_COUNT
, (EXTRACT(EPOCH FROM A.PROCESS_ACK_TS) * 1000)::BIGINT AS PROCESS_ACK_TS, TO_CHAR(A.PROCESS_ACK_TS, 'YYYY-MM-DD HH24:MI:SS') AS PROCESS_ACK_TIME_STRING
, B.HOST_NAME AS SENDER_HOST_NAME, C.HOST_NAME AS RECIPIENT_HOST_NAME
FROM (
SELECT AGENT_MESSAGE_HISTORY_SEQ, SENDER_AGENT_ID, SENDER_TS, RECIPIENT_AGENT_ID, RECIPIENT_TS, MESSAGE_UUID, MESSAGE_TYPE_CODE, CONSOLE_RECEIVE_TS, PROCESS_STATUS_CODE, MESSAGE_DATA, MESSAGE_DATA_COUNT
FROM TB_DFX_AGENT_MESSAGE_HISTORY
WHERE 1 = 1
ORDER BY AGENT_MESSAGE_HISTORY_SEQ DESC
LIMIT 20
OFFSET (1 - 1) * 20
) A
JOIN TB_DFX_AGENT_INFO B ON (A.SENDER_AGENT_ID = B.AGENT_ID)
JOIN TB_DFX_AGENT_INFO C ON (A.RECIPIENT_AGENT_ID = C.AGENT_ID)
WHERE 1 = 1
AND A.SENDER_AGENT_ID = #{senderAgentId}
AND A.MESSAGE_UUID = #{messageUuid}
]]>
</select>
<select id="selectDfxAgentMessageHistoryBySenderAgentIdAndMessageUuid" parameterType="com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageHistoryDto" resultType="com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageHistoryDto">
<![CDATA[
SELECT A.AGENT_MESSAGE_HISTORY_SEQ, A.MESSAGE_UUID, A.SENDER_AGENT_ID, A.SENDER_POSTMAN_ID
, (EXTRACT(EPOCH FROM A.SENDER_TS) * 1000)::BIGINT AS SENDER_TS, TO_CHAR(A.SENDER_TS, 'YYYY-MM-DD HH24:MI:SS') AS SENDER_TIME_STRING
, A.RECIPIENT_AGENT_ID, A.RECIPIENT_DROPBOX_ID
, (EXTRACT(EPOCH FROM A.RECIPIENT_TS) * 1000)::BIGINT AS RECIPIENT_TS, TO_CHAR(A.RECIPIENT_TS, 'YYYY-MM-DD HH24:MI:SS') AS RECIPIENT_TIME_STRING
, A.MESSAGE_TYPE_CODE
, (EXTRACT(EPOCH FROM A.CONSOLE_RECEIVE_TS) * 1000)::BIGINT AS CONSOLE_RECEIVE_TS, TO_CHAR(A.CONSOLE_RECEIVE_TS, 'YYYY-MM-DD HH24:MI:SS') AS CONSOLE_RECEIVE_TIME_STRING
, A.PROCESS_STATUS_CODE, A.MESSAGE_DATA, A.MESSAGE_DATA_COUNT
, (EXTRACT(EPOCH FROM A.PROCESS_ACK_TS) * 1000)::BIGINT AS PROCESS_ACK_TS, TO_CHAR(A.PROCESS_ACK_TS, 'YYYY-MM-DD HH24:MI:SS') AS PROCESS_ACK_TIME_STRING
, B.HOST_NAME AS SENDER_HOST_NAME, C.HOST_NAME AS RECIPIENT_HOST_NAME
FROM TB_DFX_AGENT_MESSAGE_HISTORY A
JOIN TB_DFX_AGENT_INFO B ON (A.SENDER_AGENT_ID = B.AGENT_ID)
JOIN TB_DFX_AGENT_INFO C ON (A.RECIPIENT_AGENT_ID = C.AGENT_ID)
@ -50,9 +122,13 @@
</select>
<insert id="insertDfxAgentMessageHistory" parameterType="com.bsmlab.dfx.dfxconsole.app.agent.service.DfxAgentMessageHistoryDto">
<selectKey resultType="long" keyColumn="AGENT_MESSAGE_HISTORY_SEQ" keyProperty="agentMessageHistorySeq" order="BEFORE">
SELECT NEXTVAL('SQ_DFX_AGENT_MESSAGE_HISTORY') AS AGENT_MESSAGE_HISTORY_SEQ
</selectKey>
<![CDATA[
INSERT INTO TB_DFX_AGENT_MESSAGE_HISTORY (
MESSAGE_UUID, SENDER_AGENT_ID, SENDER_POSTMAN_ID
AGENT_MESSAGE_HISTORY_SEQ
, MESSAGE_UUID, SENDER_AGENT_ID, SENDER_POSTMAN_ID
, SENDER_TS
, RECIPIENT_AGENT_ID, RECIPIENT_DROPBOX_ID
, RECIPIENT_TS
@ -62,14 +138,15 @@
, PROCESS_ACK_TS
)
VALUES (
#{messageUuid}, #{senderAgentId}, #{senderPostmanId}
, CASE WHEN #{senderTs} = 0 THEN NULL ELSE #{senderTs, jdbcType=TIMESTAMP_WITH_TIMEZONE, javaType=long} END
#{agentMessageHistorySeq}
, #{messageUuid}, #{senderAgentId}, #{senderPostmanId}
, #{senderTs, jdbcType=TIMESTAMP_WITH_TIMEZONE, javaType=Long}
, #{recipientAgentId}, #{recipientDropboxId}
, CASE WHEN #{recipientTs} = 0 THEN NULL ELSE #{recipientTs, jdbcType=TIMESTAMP_WITH_TIMEZONE, javaType=long} END
, #{recipientTs, jdbcType=TIMESTAMP_WITH_TIMEZONE, javaType=Long}
, #{messageTypeCode}
, CASE WHEN #{consoleReceiveTs} = 0 THEN NULL ELSE #{consoleReceiveTs, jdbcType=TIMESTAMP_WITH_TIMEZONE, javaType=long} END
, #{consoleReceiveTs, jdbcType=TIMESTAMP_WITH_TIMEZONE, javaType=Long}
, #{processStatusCode}, #{messageData}, #{messageDataCount}
, CASE WHEN #{processAckTs} = 0 THEN NULL ELSE #{processAckTs, jdbcType=TIMESTAMP_WITH_TIMEZONE, javaType=long} END
, #{processAckTs, jdbcType=TIMESTAMP_WITH_TIMEZONE, javaType=Long}
)
]]>
</insert>
@ -80,7 +157,7 @@
SET PROCESS_STATUS_CODE = #{processStatusCode}
, PROCESS_ACK_TS = NOW()
WHERE 1 = 1
SENDER_AGENT_ID = #{senderAgentUId}
SENDER_AGENT_ID = #{senderAgentId}
AND MESSAGE_UUID = #{messageUuid}
]]>
</update>

Loading…
Cancel
Save