15 Commits

Author SHA1 Message Date
eastargh bc478ad4ed 배치잡 수정시 clustered에 무조건 추가되는 버그 수정
eapim-admin CI / build (push) Has been cancelled
2026-06-17 10:58:00 +09:00
Rinjae 161a68a0a2 - Tomcat readiness probe 타임아웃 180s → 300s 연장
eapim-admin CI / build (push) Has been cancelled
- readiness probe URL /monitoring/login.do → /monitoring/loginForm.do 수정
- readiness 실패 시 로그 출력 내용 및 포트 점검 추가
2026-06-16 18:46:28 +09:00
Rinjae-gf63 3b59f7acd7 Merge branch 'master' of ssh://172.30.1.50:2222/djb-eapim/eapim-admin
eapim-admin CI / build (push) Has been cancelled
2026-06-16 16:44:16 +09:00
Rinjae-gf63 1e29d1d721 Merge remote-tracking branch 'origin/master' into master
Resolve conflict in PortalNoticeManService.java import block:
union both sides — keep Arrays (HEAD) and LocalDateTime/ArrayList/Collections (origin/master),
all of which are used in the merged body.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 16:41:57 +09:00
Rinjae-gf63 9342b6484c 포탈 공지사항 삭제 기능 추가 / ADM -> APP 2026-06-16 16:37:56 +09:00
eastargh dc3842aa9b 배치잡 CLUSTERED 기능 추가
eapim-admin CI / build (push) Has been cancelled
2026-06-16 16:33:55 +09:00
eastargh a440ac842d API상태 변화 감지 (점검 게시판 적용)
eapim-admin CI / build (push) Has been cancelled
2026-06-16 16:05:44 +09:00
Rinjae df5bbb162a ci 수정
eapim-admin CI / build (push) Has been cancelled
2026-06-16 15:56:46 +09:00
Rinjae 3eec267769 Merge commit '58fe176715b2027f783a2dae98e1583beaf34c8c'
eapim-admin CI / build (push) Has been cancelled
2026-06-16 15:48:41 +09:00
Rinjae 6ab7e1fe16 ci 수정 2026-06-16 15:48:30 +09:00
eastargh 58fe176715 조회시 정렬순서 추가
eapim-admin CI / build (push) Has been cancelled
2026-06-16 15:05:48 +09:00
Rinjae 6b6e2863cd 공지사항 관리 - 장애 점검 등록 UI 추가
eapim-admin CI / build (push) Has been cancelled
2026-06-16 14:55:39 +09:00
eastargh cfafccc34d CA 인증타입 제거
eapim-admin CI / build (push) Has been cancelled
2026-06-16 14:32:35 +09:00
eastargh fe98ac45c4 Merge branch 'master' of https://git.eactive.synology.me:8090/eapim/djbank/eapim-admin 2026-06-16 14:26:21 +09:00
eastargh 5f9de93d35 인증타입 CA 제거 2026-06-16 14:19:27 +09:00
20 changed files with 605 additions and 141 deletions
+15 -15
View File
@@ -17,7 +17,7 @@ jobs:
CATALINA_BASE: /prod/eapim/adminportal
CATALINA_HOME: /prod/eapim/apache-tomcat-9.0.116
CATALINA_PID: /prod/eapim/adminportal/temp/catalina.pid
DEPLOY_HTTP_PORT: "30120"
DEPLOY_HTTP_PORT: "39120"
defaults:
run:
working-directory: eapim-admin
@@ -65,14 +65,6 @@ jobs:
path: eapim-admin/build/libs/eapim-admin.war
if-no-files-found: error
- name: Sync systemd user unit
working-directory: ${{ github.workspace }}
run: |
install -D -m 644 eapim-admin/deploy/systemd/eapim-admin.service \
"$HOME/.config/systemd/user/eapim-admin.service"
systemctl --user daemon-reload
systemctl --user enable eapim-admin 2>/dev/null || true
- name: Stop tomcat (pre-clean)
working-directory: ${{ github.workspace }}
run: |
@@ -97,7 +89,7 @@ jobs:
cp eapim-admin/build/libs/eapim-admin.war "$DEPLOY_DIR/monitoring.war"
ls -la "$DEPLOY_DIR"
- name: Start tomcat and readiness probe (timeout 120s)
- name: Start tomcat and readiness probe (timeout 300s)
working-directory: ${{ github.workspace }}
run: |
LOG="$CATALINA_BASE/logs/catalina.$(date +%Y-%m-%d).log"
@@ -107,14 +99,17 @@ jobs:
# systemd 가 Tomcat 을 새 cgroup 으로 띄움 — runner step 종료와 무관하게 살아남음
systemctl --user start eapim-admin
DEADLINE=$(($(date +%s) + 120))
# /loginForm.do 는 GET 매핑(MainController#loginForm) → tomcat·webapp 양쪽 살아있으면 200
# /login.do 는 POST 처리 — GET 으로 probe 시 405/잘못된 응답 가능성 있어 회피
PROBE_URL="http://localhost:$DEPLOY_HTTP_PORT/monitoring/loginForm.do"
DEADLINE=$(($(date +%s) + 300))
STATUS=000
while [ $(date +%s) -lt $DEADLINE ]; do
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' --max-time 3 \
"http://localhost:$DEPLOY_HTTP_PORT/monitoring/login.do" 2>/dev/null || echo "000")
"$PROBE_URL" 2>/dev/null || echo "000")
case "$STATUS" in
200|302|401)
echo "Readiness OK (/monitoring/login.do HTTP $STATUS)"
echo "Readiness OK ($PROBE_URL HTTP $STATUS)"
break
;;
esac
@@ -130,8 +125,13 @@ jobs:
case "$STATUS" in
200|302|401) ;;
*)
echo "::error::Readiness probe failed within 120s (last HTTP status: $STATUS)"
[ -f "$LOG" ] && tail -c +$((BEFORE+1)) "$LOG" | tail -100
echo "::error::Readiness probe failed within 300s (last HTTP status: $STATUS)"
echo "--- last 300 lines of catalina log ---"
[ -f "$LOG" ] && tail -c +$((BEFORE+1)) "$LOG" | tail -300
echo "--- systemd unit status ---"
systemctl --user status eapim-admin --no-pager || true
echo "--- listening ports ---"
ss -tlnp 2>/dev/null | grep -E ":$DEPLOY_HTTP_PORT\b" || echo "(port $DEPLOY_HTTP_PORT 미점유)"
exit 1
;;
esac
@@ -44,14 +44,16 @@
'<%=localeMessage.getString("stdDefaultMan.columDesc")%>',
'<%=localeMessage.getString("stdDefaultMan.columLen")%>',
'<%=localeMessage.getString("stdDefaultMan.keyOr")%>',
'<%=localeMessage.getString("stdDefaultMan.ifOr")%>'
'<%=localeMessage.getString("stdDefaultMan.ifOr")%>',
'<%=localeMessage.getString("stdDefaultMan.orderSeq")%>'
],
colModel:[
{ name : 'COLUMNNAME' , align:'left' , sortable:false },
{ name : 'COLUMNDESC' , align:'left' },
{ name : 'COLUMNLENGTH' , align:'right' },
{ name : 'ISKEY' , align:'center' },
{ name : 'ISINTERFACE' , align:'center' }
{ name : 'COLUMNNAME' , align:'left' , width: '200px', sortable:false },
{ name : 'COLUMNDESC' , align:'left' , width: '200px' },
{ name : 'COLUMNLENGTH' , align:'center' , width: '100px' },
{ name : 'ISKEY' , align:'center', width: '100px' },
{ name : 'ISINTERFACE' , align:'center', width: '100px' },
{ name : 'ORDERSEQ' , align:'center', width: '100px' }
],
jsonReader: {
repeatitems:false
@@ -46,8 +46,39 @@
var icon = rowObject.hasAttachment ? '<img src="' + iconPath + '" alt="File Attached" style="vertical-align: middle; margin-left: 5px; width: 16px; height: 16px;">' : '';
return cellvalue + icon;
}
function deleteSelectedRows() {
var selectedIds = $("#grid").jqGrid('getGridParam', 'selarrrow');
if (selectedIds.length === 0) {
alert('항목을 선택하세요.');
return;
}
if (!confirm('선택된 ' + selectedIds.length + '개 항목을 삭제하시겠습니까?')) {
return;
}
$.ajax({
type: "POST",
url: url,
dataType: "json",
data: {
cmd: 'DELETE_BULK',
ids: selectedIds.join(',')
},
success: function(data) {
alert('삭제되었습니다.');
$("#grid").jqGrid('clearGridData', true);
$("#grid").trigger("reloadGrid");
},
error: function(e) {
alert('삭제 중 오류가 발생했습니다.');
}
});
}
function init(){
$.ajax({
type : "POST",
@@ -103,6 +134,8 @@
autowidth: true,
viewrecords: true,
rowList : eval('[${rmsDefaultRowList}]'),
multiselect: true,
multiboxonly: true,
ondblClickRow: function(rowId) {
var rowData = $(this).getRowData(rowId);
var id = rowData['id'];
@@ -157,6 +190,10 @@
goNav(url2);
});
$("#btn_delete_selected").click(function(){
deleteSelectedRows();
});
$("select[name=searchUseYn], input[name^=search]").keydown(function(key){
if (key.keyCode == 13){
$("#btn_search").click();
@@ -179,6 +216,7 @@
<div class="search_wrap">
<button type="button" class="cssbtn" id="btn_new" level="W"><i class="material-icons">add</i> <%= localeMessage.getString("button.new") %></button>
<button type="button" class="cssbtn" id="btn_search" level="R"><i class="material-icons">search</i> <%= localeMessage.getString("button.search") %></button>
<button type="button" class="cssbtn" id="btn_delete_selected" level="W" style="background-color: #dc3545; border-color: #dc3545; color: white;"><i class="material-icons">delete_forever</i> 선택 삭제</button>
</div>
<div class="title">공지사항 목록<span class="tooltip">공지사항을 관리합니다.</span></div>
<form id="ajaxForm" onsubmit="return false;">
@@ -19,10 +19,23 @@
<script language="javascript" >
var url = '<c:url value="/onl/apim/portalnotice/portalNoticeMan.json" />';
var url_view = '<c:url value="/onl/apim/portalnotice/portalNoticeMan.view" />';
var apiPopupUrl = '<c:url value="/onl/apim/apigroup/apiGroupMan.view" />';
var file_download = '<c:url value="/file/download.do" />';
var isDetail = false;
var fileInfo = null;
// 코드 상수 — PortalNoticeUI 와 동기화
var NOTICE_TYPE_NORMAL = '1';
var NOTICE_TYPE_MAINTENANCE = '2';
var NOTICE_TYPE_INCIDENT = '3';
// 영향 API 목록 (UI 상태)
var affectedApis = [];
function isIncidentType(t) {
return t === NOTICE_TYPE_INCIDENT || t === NOTICE_TYPE_MAINTENANCE;
}
function init() {
var key = "${param.id}";
isDetail = key != "" && key != "null";
@@ -35,8 +48,7 @@
$("#btn_modify").html('<i class="material-icons">save</i> <%= localeMessage.getString("button.register") %>');
buttonControl(false);
}
// 초기화 로직
$.ajax({
type : "POST",
url:url,
@@ -45,7 +57,8 @@
success:function(json){
combo = json;
new makeOptions("CODE","NAME").setObj($("select[name=noticeType]")).setNoValueInclude(false).setData(json.noticeTypeList).rendering();
toggleIncidentFields();
if (key) {
detail(key);
}
@@ -56,6 +69,22 @@
});
}
function toggleIncidentFields() {
var t = $('#noticeType').val();
if (isIncidentType(t)) {
$('.incident-row').show();
// INCIDENT 만 상태 영역 표시
if (t === NOTICE_TYPE_INCIDENT) {
$('.incident-state-row').show();
} else {
$('.incident-state-row').hide();
}
} else {
$('.incident-row').hide();
$('.incident-state-row').hide();
}
}
function decodeHTMLEntities(text) {
var textArea = document.createElement('textarea');
textArea.innerHTML = text;
@@ -73,57 +102,100 @@
function displayAttachFile(file) {
var $attachFiles = $('#attachFiles');
$attachFiles.empty();
if (file) {
var iconPath = '${pageContext.request.contextPath}/images/icon_file.png';
var fileItem = $('<div>').addClass('file-item');
$('<img>').attr('src', iconPath).css({'marginLeft': '15px','marginRight': '5px' ,'height': '1.2em'}).appendTo(fileItem);
var fileNameSpan = $('<span>')
.addClass('fileName')
.text(file.originalFileName + '.' + file.fileExtension + ' (' + formatFileSize(file.size) + ')')
.css('cursor', 'pointer')
.on('click', function() {
if (file.fileId) {
downloadFile(file.fileId);
}
});
var fileNameSpan = $('<span>').addClass('fileName')
.text(file.originalFileName + '.' + file.fileExtension + ' (' + formatFileSize(file.size) + ')')
.css('cursor', 'pointer')
.on('click', function() { if (file.fileId) downloadFile(file.fileId); });
fileNameSpan.appendTo(fileItem);
$('<button>').text('X').addClass('delete-file').css('marginLeft', '15px')
.on('click', function() {
fileInfo = null;
displayAttachFile(null);
// 폼에 삭제 플래그 추가
$('<input>').attr({
type: 'hidden',
name: 'fileDeleted',
value: 'true'
}).appendTo('#ajaxForm');
})
.appendTo(fileItem);
.on('click', function() {
fileInfo = null;
displayAttachFile(null);
$('<input>').attr({type: 'hidden', name: 'fileDeleted', value: 'true'}).appendTo('#ajaxForm');
}).appendTo(fileItem);
fileItem.appendTo($attachFiles);
}
}
function downloadFile(fileId) {
if (fileId) {
var fileSn = 1; // 단일 파일이므로 시리얼 넘버를 1로 고정
window.location.href = file_download + '?fileId=' + fileId + '&fileSn=' + fileSn;
} else {
console.log("File not yet uploaded or doesn't have an ID");
window.location.href = file_download + '?fileId=' + fileId + '&fileSn=1';
}
}
function renderAffectedApis() {
var $tb = $('#affectedApiTbody');
$tb.empty();
if (!affectedApis.length) {
$tb.append('<tr><td colspan="3" style="text-align:center;color:#999;">선택된 영향 API가 없습니다.</td></tr>');
return;
}
$.each(affectedApis, function(idx, api) {
var $tr = $('<tr>');
$tr.append('<td style="text-align:center;width:40px;"><input type="checkbox" class="affected-api-check" data-idx="' + idx + '"></td>');
$tr.append('<td>' + (api.apiId || '') + '</td>');
$tr.append('<td>' + (api.apiName || '') + '</td>');
$tb.append($tr);
});
}
function openApiPopup() {
// showModal 헬퍼는 urlAddServiceType + &menuId=getMenuId() + &pop=true 를 자동 부여 →
// AuthorizeInterceptor 가 정상적으로 권한 인식. window.open 직접 호출 시 권한 부족 발생.
// 본 페이지는 summernote 본문 에디터 등 다른 iframe 이 함께 존재할 수 있어
// frames[0] 가정은 위험 → DOM 에서 .ui-dialog 내 iframe 중 src=apiGroupMan 인 것을 식별.
var selectedIds = affectedApis.map(function(a) { return a.apiId; }).join(',');
var url2 = apiPopupUrl + '?cmd=POPUP&selectedApiIds=' + encodeURIComponent(selectedIds);
var args = {};
showModal(url2, args, 1280, 860, function () {
var doc = args.self && args.self.document;
if (!doc) return;
var popupFrame = null;
var iframes = doc.querySelectorAll('iframe');
for (var i = 0; i < iframes.length; i++) {
var src = iframes[i].getAttribute('src') || '';
if (src.indexOf('apiGroupMan') >= 0) {
popupFrame = iframes[i].contentWindow;
break;
}
}
if (!popupFrame) return;
var rv = popupFrame.returnValue;
if (!rv) return;
var rows = Array.isArray(rv) ? rv : [rv];
$.each(rows, function(_, item) {
if (!item || !item.apiId) return;
var exists = affectedApis.some(function(a){ return a.apiId === item.apiId; });
if (!exists) {
affectedApis.push({apiId: item.apiId, apiName: item.apiDesc || item.apiName || ''});
}
});
renderAffectedApis();
});
}
function removeSelectedAffectedApis() {
var idxes = [];
$('.affected-api-check:checked').each(function() {
idxes.push(parseInt($(this).data('idx'), 10));
});
if (!idxes.length) {
alert('삭제할 항목을 선택해주세요.');
return;
}
// 역순 삭제
idxes.sort(function(a,b){return b-a;}).forEach(function(i){ affectedApis.splice(i, 1); });
renderAffectedApis();
}
function detail(key) {
if (!isDetail) return;
$.ajax({
type: "POST",
url: url,
dataType: "json",
type: "POST", url: url, dataType: "json",
data: {cmd: 'DETAIL', id: key},
success: function (data) {
$("#id").val(key);
@@ -135,7 +207,18 @@
var decodedContent = decodeHTMLEntities(data.noticeDetail);
$('#contents').summernote('code', decodedContent);
// 파일 정보 표시
// 장애/점검 필드
$('#summary').val(data.summary || '');
$('#startedAt').val(data.startedAt || '');
$('#endAt').val(data.endAt || '');
$('#state').val(data.state || 'INVESTIGATING');
$('#previousState').text(data.previousState || '없음');
affectedApis = (data.affectedApis || []).map(function(a){
return {apiId: a.apiId, apiName: a.apiName || ''};
});
renderAffectedApis();
toggleIncidentFields();
if (data.fileInfo) {
fileInfo = {
originalFileName: data.fileInfo.fileDetails[0].originalFileName,
@@ -146,24 +229,21 @@
displayAttachFile(fileInfo);
}
},
error: function (e) {
alert(e.responseText);
}
error: function (e) { alert(e.responseText); }
});
}
$(document).ready(function () {
var returnUrl = getReturnUrlForReturn();
init();
// Summernote 에디터 초기화 (이미지 붙여넣기 data-uri 방식, 리사이징 지원)
initSummernote('#contents', {
placeholder: '여기에 내용을 입력하세요.',
height: 300
});
initSummernote('#contents', { placeholder: '여기에 내용을 입력하세요.', height: 300 });
$('#noticeType').on('change', toggleIncidentFields);
$('#btn_addAffectedApi').click(openApiPopup);
$('#btn_removeAffectedApi').click(removeSelectedAffectedApis);
$('#fileInput').change(function(e) {
var file = e.target.files[0];
@@ -171,52 +251,66 @@
fileInfo = {
originalFileName: file.name.split('.').slice(0, -1).join('.'),
fileExtension: file.name.split('.').pop(),
file: file,
size: file.size
file: file, size: file.size
};
displayAttachFile(fileInfo);
}
this.value = ''; // 입력 필드 초기화
});
$('#addFile').click(function() {
$('#fileInput').click();
this.value = '';
});
$('#addFile').click(function() { $('#fileInput').click(); });
$("#btn_modify").click(function () {
if (!checkRequired("ajaxForm")) return;
if (confirm("<%= localeMessage.getString("common.checkSave")%>") != true)
return;
var noticeType = $('#noticeType').val();
if (isIncidentType(noticeType)) {
if (!$('#startedAt').val()) {
alert('시작 시각을 입력해 주십시오.');
return;
}
if (noticeType === NOTICE_TYPE_MAINTENANCE && !$('#endAt').val()) {
alert('점검 종료 시각을 입력해 주십시오.');
return;
}
if (affectedApis.length === 0) {
alert('영향 API를 1건 이상 선택해 주십시오.');
return;
}
}
if (confirm("<%= localeMessage.getString("common.checkSave")%>") != true) return;
var formData = new FormData($("#ajaxForm")[0]);
formData.set("useYn", $("#useYn").is(":checked") ? "Y" : "N");
formData.set("fixYn", $("#fixYn").is(":checked") ? "Y" : "N");
formData.set("noticeDetail", $('#contents').summernote('code'));
// 시작/종료 시각이 비어 있으면 전송하지 않음 (서버 파싱 오류 방지)
if (!$('#startedAt').val()) formData.delete('startedAt');
if (!$('#endAt').val()) formData.delete('endAt');
// 영향 API → affectedApis[i].apiId/apiName 으로 전송
$.each(affectedApis, function(i, api) {
formData.set('affectedApis[' + i + '].apiId', api.apiId);
formData.set('affectedApis[' + i + '].apiName', api.apiName || '');
});
formData.append("cmd", isDetail ? "UPDATE" : "INSERT");
// 파일 처리
if (fileInfo && fileInfo.file) {
formData.set("files", fileInfo.file);
formData.set("fileName", fileInfo.originalFileName + '.' + fileInfo.fileExtension);
}
$.ajax({
type: "POST",
url: url,
data: formData,
processData: false,
contentType: false,
success: function (json) {
type: "POST", url: url, data: formData,
processData: false, contentType: false,
success: function () {
alert("<%= localeMessage.getString("common.saveMsg") %>");
goNav(returnUrl);
},
error: function (e) {
alert(e.responseText);
}
error: function (e) { alert(e.responseText); }
});
});
@@ -224,25 +318,18 @@
if (confirm("<%= localeMessage.getString("common.confirmMsg")%>")) {
var postData = $('#ajaxForm').serializeArray();
postData.push({name: "cmd", value: "DELETE"});
$.ajax({
type: "POST",
url: url,
data: postData,
success: function (args) {
type: "POST", url: url, data: postData,
success: function () {
alert("<%= localeMessage.getString("common.deleteMsg") %>");
goNav(returnUrl);
},
error: function (e) {
alert(e.responseText);
}
error: function (e) { alert(e.responseText); }
});
}
});
$("#btn_previous").click(function () {
goNav(returnUrl);
});
$("#btn_previous").click(function () { goNav(returnUrl); });
});
</script>
</head>
@@ -252,7 +339,7 @@
<ul class="path">
<li><a href="#">${rmsMenuPath}</a></li>
</ul>
</div><!-- end content_top -->
</div>
<div class="content_middle">
<div class="search_wrap">
<button type="button" class="cssbtn" id="btn_modify" level="W" status="DETAIL,NEW"><i class="material-icons">save</i> <%= localeMessage.getString("button.modify") %></button>
@@ -270,9 +357,9 @@
<col style="width: 40%"/>
</colgroup>
<tr>
<th>게시유형</th>
<td colspan="3">
<div class="select-style" >
<th>게시유형 <font color="red">*</font></th>
<td colspan="3">
<div class="select-style">
<select name="noticeType" id="noticeType"></select>
</div>
</td>
@@ -286,7 +373,7 @@
<td>
<input type="checkbox" name="useYn" id="useYn" value="Y" ${portalNoticeUI.useYn eq 'Y' ? 'checked' : ''}> 사용
</td>
<th>고정여부</th>
<th>상단고정</th>
<td>
<input type="checkbox" name="fixYn" id="fixYn" value="Y" ${portalNoticeUI.fixYn eq 'Y' ? 'checked' : ''}> 고정
</td>
@@ -297,19 +384,58 @@
<textarea id="contents" name="noticeDetail" style="width:100%;height:300px" data-required data-warning="본문을 입력하여 주십시오."></textarea>
</td>
</tr>
<!-- <tr>
<th>첨부파일</th>
<td colspan="3">
<div style="margin: 5px 0px; display: inline-block;">
<input type="file" id="fileInput" name="files" style="display:none;" />
<button type="button" id="addFile" class="cssbtn smallBtn">파일 선택</button>
</div>
<div id="attachFiles" style="margin: 5px 0px; display: inline-block;"></div>
<!-- ───── 장애/점검 전용 영역 ───── -->
<tr class="incident-row" style="display:none;">
<th>시작 <font color="red">*</font></th>
<td>
<input type="datetime-local" id="startedAt" name="startedAt" style="width:90%">
</td>
</tr> -->
<th>종료</th>
<td>
<input type="datetime-local" id="endAt" name="endAt" style="width:90%" placeholder="해소시 자동 채움">
</td>
</tr>
<tr class="incident-row incident-state-row" style="display:none;">
<th>상태</th>
<td>
<div class="select-style">
<select id="state" name="state">
<option value="INVESTIGATING">INVESTIGATING - 조사중</option>
<option value="IDENTIFIED">IDENTIFIED - 원인 파악</option>
<option value="MONITORING">MONITORING - 모니터링</option>
<option value="RESOLVED">RESOLVED - 해소</option>
<option value="CANCELED">CANCELED - 취소</option>
</select>
</div>
</td>
<th>이전 상태</th>
<td><span id="previousState">없음</span></td>
</tr>
<tr class="incident-row" style="display:none;">
<th>영향 API 선택</th>
<td colspan="3">
<div style="margin-bottom:5px;">
<button type="button" class="cssbtn smallBtn" id="btn_removeAffectedApi"><i class="material-icons">delete</i> 삭제</button>
<button type="button" class="cssbtn smallBtn" id="btn_addAffectedApi"><i class="material-icons">add</i> 추가</button>
</div>
<table class="table_row" cellspacing="0" style="width:100%;">
<thead>
<tr>
<th style="width:40px;text-align:center;"><input type="checkbox" id="affectedApiAll" onclick="$('.affected-api-check').prop('checked', this.checked);"></th>
<th>API ID</th>
<th>API 명</th>
</tr>
</thead>
<tbody id="affectedApiTbody">
<tr><td colspan="3" style="text-align:center;color:#999;">선택된 영향 API가 없습니다.</td></tr>
</tbody>
</table>
</td>
</tr>
</table>
</form>
</div>
</div>
</body>
</html>
</html>
@@ -1686,7 +1686,7 @@
<option value="none" selected>NONE</option>
<option value="oauth">OAUTH</option>
<option value="api_key">API_KEY</option>
<option value="ca">CA</option>
<!-- <option value="ca">CA</option> -->
</select>
</div>
</div>
@@ -15,4 +15,6 @@ public interface EMSScheduler {
void deleteAndAddJob(Scheduler scheduler, String jobName) throws SchedulerException, ClassNotFoundException;
void reloadJob(String jobName) throws SchedulerException, ClassNotFoundException;
}
@@ -120,6 +120,32 @@ public class EMSSchedulerImpl implements EMSScheduler {
}
}
@Override
public void reloadJob(String jobName) throws SchedulerException, ClassNotFoundException {
JobInfo jobInfo = jobInfoService.getById(jobName);
if (CLUSTERED.equals(jobInfo.getInstanceName())) {
reloadClusteredJob(jobInfo);
} else {
deleteAndAddJob(scheduler, jobInfo);
deleteJob(clusteredScheduler, jobName);
}
}
private void reloadClusteredJob(JobInfo jobInfo) throws SchedulerException, ClassNotFoundException {
TriggerKey triggerKey = new TriggerKey(getTriggerName(jobInfo.getId()), Scheduler.DEFAULT_GROUP);
Trigger existing = clusteredScheduler.getTrigger(triggerKey);
if (existing != null) {
CronTrigger newTrigger = newTrigger()
.withIdentity(triggerKey)
.withSchedule(cronSchedule(jobInfo.getCronExp()))
.build();
Date ft = clusteredScheduler.rescheduleJob(triggerKey, newTrigger);
log.info("{} rescheduled at: {} cron: {}", jobInfo.getId(), ft, jobInfo.getCronExp());
} else {
deleteAndAddJob(clusteredScheduler, jobInfo);
}
}
public void deleteJob(Scheduler inScheduler, String jobNm) throws SchedulerException {
JobKey key = new JobKey(jobNm, Scheduler.DEFAULT_GROUP);
JobDetail jobDetail = inScheduler.getJobDetail(key);
@@ -45,6 +45,9 @@ public class SchedulerManController extends BaseAnnotationController {
@Qualifier("monitoringSyncAgent")
private MonitoringSyncAgent monitoringSyncAgent;
@Autowired
private EMSScheduler emsScheduler;
@GetMapping(value = "/common/scheduler/schedulerMan.view")
public String viewList() {
return "/common/scheduler/schedulerMan";
@@ -96,6 +99,11 @@ public class SchedulerManController extends BaseAnnotationController {
@PostMapping(value = "/common/scheduler/schedulerMan.json", params = "cmd=UPDATE")
public ResponseEntity<Void> update(JobInfoUI jobInfoUI) {
service.update(jobInfoUI);
try {
emsScheduler.reloadJob(jobInfoUI.getId());
} catch (Exception e) {
logger.error("reloadJob error on current server", e);
}
syncReloadScheduler(jobInfoUI.getId());
return ResponseEntity.ok().build();
}
@@ -115,7 +123,7 @@ public class SchedulerManController extends BaseAnnotationController {
String url = "/monitoring/schedulerReload.do";
try {
ret = monitoringSyncAgent.sync(url, paramMap);
ret = monitoringSyncAgent.targetSync(url, paramMap);
} catch (Exception e) {
logger.error("syncReloadScheduler error", e);
} finally {
@@ -13,10 +13,16 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import java.util.List;
@Service
@Transactional(transactionManager = "transactionManagerForEMS")
public class PortalNoticeService extends AbstractEMSDataSerivce<PortalNotice, String, PortalNoticeRepository> {
public void deleteByIds(List<String> ids) {
repository.deleteAllById(ids);
}
public Page<PortalNotice> findAll(Pageable pageable, PortalNoticeUISearch portalNoticeUISearch) {
QPortalNotice qPortalNotice = QPortalNotice.portalNotice;
@@ -5,7 +5,9 @@ import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -42,7 +44,12 @@ public class ExtendedColumnDefinitionService
predicate = predicate.and(qExtendedColumnDefinition.columnDesc.containsIgnoreCase(searchColumnDesc));
}
return repository.findAll(predicate, pageable);
Pageable orderedPageable = PageRequest.of(
pageable.getPageNumber(),
pageable.getPageSize(),
Sort.by(Sort.Order.asc("orderSeq"), Sort.Order.asc("columnName")));
return repository.findAll(predicate, orderedPageable);
}
}
@@ -39,11 +39,10 @@ public interface ApiStatusRepository extends BaseRepository<ApiStatus, String> {
+ " FROM ("
+ " SELECT A.EAISVCNAME"
+ " , NVL(B.STATUS_CODE, 'N') AS STATUS_CODE"
//TODO: 게시판 임시로 생성하여 사용. 유차장이 완료하면 변경할것
+ " , NVL(( SELECT 'Y' FROM PTL_NOTICE_API X "
+ " WHERE EXISTS (SELECT 1 FROM EMSAPP.PTL_NOTICE Y WHERE X.NOTICE_ID = Y.ID)"
//+ " AND TO_CHAR(SYSDATE, 'YYYYMMDDHH24MI') BETWEEN START_TIME AND END_TIME) "
+ " AND A.EAISVCNAME = X.API_NAME),'N') AS CTRL_YN "
+ " , NVL(( SELECT MAX('Y') FROM EMSAPP.DJB_APISTATUS_INCIDENT_API X"
+ " WHERE EXISTS (SELECT 1 FROM EMSAPP.DJB_APISTATUS_INCIDENT Y WHERE X.INCIDENT_ID = Y.INCIDENT_ID "
+ " AND SYSTIMESTAMP BETWEEN STARTED_AT AND END_AT) "
+ " AND A.EAISVCNAME = X.API_ID),'N') AS CTRL_YN "
+ " , (SELECT CASE WHEN TOTAL = 0 THEN 'X'"
+ " WHEN (TOTAL - SUCCESS) * 100 / TOTAL > :errorRate THEN 'Y'"
+ " ELSE 'N' END"
@@ -1,6 +1,9 @@
package com.eactive.eai.rms.ext.djb.job;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.quartz.DisallowConcurrentExecution;
@@ -68,10 +71,24 @@ public class ApiStatusMonitorJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
// execute.instances 가 지정된 경우 해당 인스턴스에서만 실행
String allowedInstances = jobDataMap.getString("execute.instances");
if (StringUtils.isNotEmpty(allowedInstances)) {
Set<String> instanceSet = Arrays.stream(allowedInstances.split(","))
.map(s -> s.trim().toLowerCase())
.collect(Collectors.toSet());
String currentInstance = StringUtils.defaultString(System.getProperty("inst.Name")).trim().toLowerCase();
if (!instanceSet.contains(currentInstance)) {
log.debug("Job 실행 인스턴스 제한 - 현재: {}, 허용: {}", currentInstance, allowedInstances);
return;
}
}
log.info("*** START ApiStatusUpdateJob run({})", CommonUtil.getToday("yyyy-MM-dd HH:mm"));
// Job 파라미터 로깅
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
logJobParameters(jobDataMap);
HashMap<String, String> param = this.checkParameters(jobDataMap);
@@ -1,8 +1,12 @@
package com.eactive.eai.rms.ext.djb.job;
import java.util.Arrays;
import java.util.Date;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDataMap;
@@ -48,6 +52,21 @@ public class InflowTokenMonitorJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
// execute.instances 가 지정된 경우 해당 인스턴스에서만 실행
String allowedInstances = jobDataMap.getString("execute.instances");
if (StringUtils.isNotEmpty(allowedInstances)) {
Set<String> instanceSet = Arrays.stream(allowedInstances.split(","))
.map(s -> s.trim().toLowerCase())
.collect(Collectors.toSet());
String currentInstance = StringUtils.defaultString(System.getProperty("inst.Name")).trim().toLowerCase();
if (!instanceSet.contains(currentInstance)) {
log.debug("Job 실행 인스턴스 제한 - 현재: {}, 허용: {}", currentInstance, allowedInstances);
return;
}
}
log.info("*** START InflowTokenFailMonitorJob run({})", CommonUtil.getToday("yyyy-MM-dd HH:mm"));
long execMinute = 1; //default 배치 실행주기(분)
@@ -5,14 +5,20 @@ import com.eactive.eai.rms.common.datasource.DataSourceTypeManager;
import com.eactive.eai.rms.common.util.CommonUtil;
import com.eactive.eai.rms.onl.apim.portalInquiry.PortalInquiryClosingService;
import com.eactive.eai.rms.onl.common.util.DateUtil;
import org.apache.commons.lang3.StringUtils;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Arrays;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 매일 00:05 실행 - RESPONDED 상태 문의글 자동 종료 배치
*
@@ -44,6 +50,21 @@ public class PortalInquiryClosingJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
// execute.instances 가 지정된 경우 해당 인스턴스에서만 실행
String allowedInstances = jobDataMap.getString("execute.instances");
if (StringUtils.isNotEmpty(allowedInstances)) {
Set<String> instanceSet = Arrays.stream(allowedInstances.split(","))
.map(s -> s.trim().toLowerCase())
.collect(Collectors.toSet());
String currentInstance = StringUtils.defaultString(System.getProperty("inst.Name")).trim().toLowerCase();
if (!instanceSet.contains(currentInstance)) {
log.debug("Job 실행 인스턴스 제한 - 현재: {}, 허용: {}", currentInstance, allowedInstances);
return;
}
}
log.info("*** START PortalInquiryCommentClosingService run({})", CommonUtil.getToday("yyyy-MM-dd HH:mm"));
int closingDays = DEFAULT_CLOSING_DAYS;
@@ -0,0 +1,11 @@
package com.eactive.eai.rms.onl.apim.portalnotice;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class IncidentAffectedApiUI {
private String apiId;
private String apiName;
}
@@ -80,4 +80,10 @@ public class PortalNoticeManController extends BaseAnnotationController {
return ResponseEntity.ok().build();
}
@PostMapping(value = "/onl/apim/portalnotice/portalNoticeMan.json", params = "cmd=DELETE_BULK")
public ResponseEntity<String> deleteBulk(String ids) {
portalNoticeManService.deleteBulk(ids);
return ResponseEntity.ok().build();
}
}
@@ -1,5 +1,13 @@
package com.eactive.eai.rms.onl.apim.portalnotice;
import com.eactive.apim.portal.djb.apistatus.incident.entity.DjbApistatusIncident;
import com.eactive.apim.portal.djb.apistatus.incident.entity.DjbApistatusIncidentApi;
import com.eactive.apim.portal.djb.apistatus.incident.entity.DjbApistatusIncidentApiId;
import com.eactive.apim.portal.djb.apistatus.incident.entity.IncidentKind;
import com.eactive.apim.portal.djb.apistatus.incident.entity.IncidentState;
import com.eactive.apim.portal.djb.apistatus.incident.repository.DjbApistatusIncidentApiRepository;
import com.eactive.apim.portal.djb.apistatus.incident.repository.DjbApistatusIncidentRepository;
import com.eactive.apim.portal.djb.apistatus.incident.repository.DjbApistatusIncidentTimelineRepository;
import com.eactive.apim.portal.file.entity.FileDetail;
import com.eactive.apim.portal.file.entity.FileInfo;
import com.eactive.apim.portal.file.service.FileService;
@@ -23,7 +31,13 @@ import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@Transactional(transactionManager = "transactionManagerForEMS")
@@ -32,6 +46,9 @@ public class PortalNoticeManService extends BaseService {
private final PortalNoticeService portalNoticeService;
private final PortalNoticeUIMapper portalNoticeUIMapper;
private final FileService fileService;
private final DjbApistatusIncidentRepository incidentRepository;
private final DjbApistatusIncidentApiRepository incidentApiRepository;
private final DjbApistatusIncidentTimelineRepository incidentTimelineRepository;
private String decodeString(String value) {
if (ContainerUtil.get() == ContainerUtil.TOMCAT) {
@@ -48,10 +65,27 @@ public class PortalNoticeManService extends BaseService {
@Autowired
public PortalNoticeManService(PortalNoticeService portalNoticeService,
PortalNoticeUIMapper portalNoticeUIMapper,
FileService fileService){
FileService fileService,
DjbApistatusIncidentRepository incidentRepository,
DjbApistatusIncidentApiRepository incidentApiRepository,
DjbApistatusIncidentTimelineRepository incidentTimelineRepository){
this.portalNoticeService = portalNoticeService;
this.portalNoticeUIMapper = portalNoticeUIMapper;
this.fileService = fileService;
this.incidentRepository = incidentRepository;
this.incidentApiRepository = incidentApiRepository;
this.incidentTimelineRepository = incidentTimelineRepository;
}
private static boolean isIncidentKind(String noticeType) {
return PortalNoticeUI.NOTICE_TYPE_INCIDENT.equals(noticeType)
|| PortalNoticeUI.NOTICE_TYPE_MAINTENANCE.equals(noticeType);
}
private static IncidentKind toKind(String noticeType) {
if (PortalNoticeUI.NOTICE_TYPE_INCIDENT.equals(noticeType)) return IncidentKind.INCIDENT;
if (PortalNoticeUI.NOTICE_TYPE_MAINTENANCE.equals(noticeType)) return IncidentKind.MAINTENANCE;
return null;
}
@@ -89,9 +123,39 @@ public class PortalNoticeManService extends BaseService {
PortalNoticeUI ui = portalNoticeUIMapper.toVo(portalNotice);
setFileInfo(portalNotice, ui);
populateIncident(portalNotice, ui);
return ui;
}
private void populateIncident(PortalNotice portalNotice, PortalNoticeUI ui) {
if (!isIncidentKind(portalNotice.getNoticeType())) {
ui.setAffectedApis(Collections.emptyList());
return;
}
Optional<DjbApistatusIncident> incidentOpt = incidentRepository.findByNoticeId(portalNotice.getId());
if (!incidentOpt.isPresent()) {
ui.setAffectedApis(Collections.emptyList());
return;
}
DjbApistatusIncident incident = incidentOpt.get();
ui.setIncidentId(incident.getIncidentId());
ui.setSummary(incident.getSummary());
ui.setStartedAt(incident.getStartedAt());
ui.setEndAt(incident.getEndAt());
ui.setState(incident.getState() == null ? null : incident.getState().name());
ui.setPreviousState(incident.getPreviousState() == null ? null : incident.getPreviousState().name());
List<IncidentAffectedApiUI> apis = incidentApiRepository
.findByIncidentIdOrderByApiId(incident.getIncidentId()).stream()
.map(api -> {
IncidentAffectedApiUI vo = new IncidentAffectedApiUI();
vo.setApiId(api.getApiId());
vo.setApiName(api.getApiName());
return vo;
}).collect(Collectors.toList());
ui.setAffectedApis(apis);
}
public void insert(PortalNoticeUI portalNoticeUI) throws IOException {
//portalNoticeUI.setNoticeSubject(decodeString(portalNoticeUI.getNoticeSubject()));
//portalNoticeUI.setNoticeDetail(decodeString(StringEscapeUtils.unescapeHtml(portalNoticeUI.getNoticeDetail())));
@@ -106,6 +170,61 @@ public class PortalNoticeManService extends BaseService {
}
portalNoticeService.save(portalNotice);
if (isIncidentKind(portalNoticeUI.getNoticeType())) {
persistIncident(portalNoticeUI, portalNotice, true);
}
}
private void persistIncident(PortalNoticeUI portalNoticeUI, PortalNotice portalNotice, boolean isCreate) {
IncidentKind kind = toKind(portalNoticeUI.getNoticeType());
DjbApistatusIncident incident = incidentRepository.findByNoticeId(portalNotice.getId())
.orElseGet(DjbApistatusIncident::new);
incident.setKind(kind);
incident.setTitle(portalNotice.getNoticeSubject());
incident.setSummary(portalNoticeUI.getSummary());
incident.setStartedAt(portalNoticeUI.getStartedAt() != null
? portalNoticeUI.getStartedAt() : LocalDateTime.now());
incident.setEndAt(portalNoticeUI.getEndAt());
incident.setDetectedBy("MANUAL");
incident.setNoticeId(portalNotice.getId());
incident.setDraftYn("N");
incident.setFixYn(StringUtils.defaultIfBlank(portalNotice.getFixYn(), "N"));
if (kind == IncidentKind.INCIDENT) {
IncidentState newState = StringUtils.isNotBlank(portalNoticeUI.getState())
? IncidentState.valueOf(portalNoticeUI.getState())
: IncidentState.INVESTIGATING;
if (!isCreate && incident.getState() != newState) {
incident.setPreviousState(incident.getState());
}
incident.setState(newState);
} else {
incident.setState(null);
incident.setPreviousState(null);
}
DjbApistatusIncident saved = incidentRepository.save(incident);
// 영향 API 동기화 — 단순 전체 삭제 후 재삽입
incidentApiRepository.deleteByIncidentId(saved.getIncidentId());
List<IncidentAffectedApiUI> affected = portalNoticeUI.getAffectedApis();
if (affected != null && !affected.isEmpty()) {
List<DjbApistatusIncidentApi> rows = new ArrayList<>();
for (IncidentAffectedApiUI src : affected) {
if (StringUtils.isBlank(src.getApiId())) continue;
DjbApistatusIncidentApi api = new DjbApistatusIncidentApi();
api.setIncidentId(saved.getIncidentId());
api.setApiId(src.getApiId());
api.setApiName(src.getApiName());
rows.add(api);
}
if (!rows.isEmpty()) {
incidentApiRepository.saveAll(rows);
}
}
}
private void handleFileUpload(PortalNoticeUI portalNoticeUI, PortalNotice portalNotice) throws IOException {
@@ -129,6 +248,7 @@ public class PortalNoticeManService extends BaseService {
portalNoticeUI.setNoticeDetail(StringEscapeUtils.unescapeHtml(portalNoticeUI.getNoticeDetail()));
PortalNotice portalNotice = portalNoticeService.getById(portalNoticeUI.getId());
String previousNoticeType = portalNotice.getNoticeType();
if (portalNoticeUI.getFiles() != null && !portalNoticeUI.getFiles().isEmpty()) {
handleFileUpload(portalNoticeUI, portalNotice);
@@ -139,12 +259,44 @@ public class PortalNoticeManService extends BaseService {
portalNoticeUIMapper.updateToEntity(portalNoticeUI, portalNotice);
portalNoticeService.save(portalNotice);
if (isIncidentKind(portalNotice.getNoticeType())) {
persistIncident(portalNoticeUI, portalNotice, false);
} else if (isIncidentKind(previousNoticeType)) {
// 일반 공지로 유형 변경 → 기존 incident 정리
deleteIncidentIfExists(portalNotice.getId());
}
}
private void deleteIncidentIfExists(String noticeId) {
incidentRepository.findByNoticeId(noticeId).ifPresent(incident -> {
incidentApiRepository.deleteByIncidentId(incident.getIncidentId());
incidentTimelineRepository.deleteByIncidentId(incident.getIncidentId());
incidentRepository.delete(incident);
});
}
public void delete(String id) {
PortalNotice portalNotice = portalNoticeService.getById(id);
Optional.ofNullable(portalNotice.getFileId()).ifPresent(fileService::deleteFile);
deleteIncidentIfExists(id);
portalNoticeService.deleteById(id);
}
public void deleteBulk(String ids) {
if (StringUtils.isBlank(ids)) {
return;
}
List<String> idList = Arrays.asList(ids.split(","));
// 첨부파일 정리 (단건 삭제와 동일한 처리)
idList.forEach(id -> {
PortalNotice portalNotice = portalNoticeService.getById(id);
Optional.ofNullable(portalNotice.getFileId()).ifPresent(fileService::deleteFile);
});
portalNoticeService.deleteByIds(idList);
}
}
@@ -8,11 +8,16 @@ import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
import java.util.List;
@Data
public class PortalNoticeUI {
public static final String NOTICE_TYPE_NORMAL = "1";
public static final String NOTICE_TYPE_MAINTENANCE = "2";
public static final String NOTICE_TYPE_INCIDENT = "3";
private String id;
private String noticeSubject;
@@ -53,4 +58,24 @@ public class PortalNoticeUI {
private boolean hasAttachment; // 첨부파일 여부
private boolean fileDeleted; // 파일 삭제 여부
// ───── 장애/점검(djb_apistatus_incident) 연동 필드 ─────
private Long incidentId;
private String summary;
// HTML <input type="datetime-local"> 는 'T' 구분자 형식 (예: 2026-06-16T10:29).
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm")
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm")
private LocalDateTime startedAt;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm")
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm")
private LocalDateTime endAt;
private String state; // INCIDENT 한정 (INVESTIGATING/IDENTIFIED/MONITORING/RESOLVED/CANCELED)
private String previousState; // 직전 상태
private List<IncidentAffectedApiUI> affectedApis;
}
@@ -3,7 +3,9 @@ package com.eactive.eai.rms.onl.apim.portalnotice;
import com.eactive.apim.portal.portalNotice.entity.PortalNotice;
import org.mapstruct.*;
@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
@Mapper(componentModel = "spring",
unmappedTargetPolicy = ReportingPolicy.IGNORE,
unmappedSourcePolicy = ReportingPolicy.IGNORE)
public interface PortalNoticeUIMapper {
PortalNoticeUI toVo(PortalNotice entity);
@@ -1,10 +1,10 @@
package com.eactive.eai.rms.onl.common.controller;
import com.eactive.eai.rms.common.interceptor.InterceptorSkipController;
import com.eactive.eai.rms.common.scheduler.EMSScheduler;
import com.eactive.eai.rms.onl.manage.adapter.spcftmadapter.AdapterJobInfo;
import com.eactive.eai.rms.onl.manage.adapter.spcftmadapter.AdapterSchedulerUtil;
import com.eactive.eai.rms.onl.manage.adapter.spcftmadapter.SpcfTmAdapterDao;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.quartz.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -15,9 +15,11 @@ import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import com.eactive.eai.rms.common.interceptor.InterceptorSkipController;
import com.eactive.eai.rms.common.scheduler.EMSScheduler;
import com.eactive.eai.rms.onl.manage.adapter.spcftmadapter.AdapterJobInfo;
import com.eactive.eai.rms.onl.manage.adapter.spcftmadapter.AdapterSchedulerUtil;
import com.eactive.eai.rms.onl.manage.adapter.spcftmadapter.SpcfTmAdapterDao;
@Controller
public class SchedulerReloadController implements InterceptorSkipController {
@@ -34,19 +36,14 @@ public class SchedulerReloadController implements InterceptorSkipController {
private EMSScheduler emsScheduler;
@Autowired
@Qualifier("scheduler")
@Qualifier("scheduler")
private Scheduler scheduler;
@Autowired
@Qualifier("clusteredScheduler")
private Scheduler clusteredScheduler;
@RequestMapping("/schedulerReload.do")
public ResponseEntity<Map<String, Object>> rmsSchedulerReload(String jobName) {
Map<String, Object> result = new HashMap<>();
try {
emsScheduler.deleteAndAddJob(scheduler, jobName);
emsScheduler.deleteAndAddJob(clusteredScheduler, jobName);
emsScheduler.reloadJob(jobName);
result.put("status", "success");
} catch (Exception e) {
logger.error("", e);