<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %><%@ page import="org.quartz.*, org.quartz.impl.matchers.GroupMatcher, org.springframework.web.context.WebApplicationContext, org.springframework.web.context.support.WebApplicationContextUtils, java.util.*, java.text.SimpleDateFormat" %><%! private String fmt(Date date) { if (date == null) return "-"; return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date); } private String relative(Date date) { if (date == null) return ""; long diff = date.getTime() - System.currentTimeMillis(); String prefix = diff >= 0 ? "+" : "-"; long abs = Math.abs(diff) / 1000; if (abs < 60) return prefix + abs + "s"; if (abs < 3600) return prefix + (abs / 60) + "m " + (abs % 60) + "s"; return prefix + (abs / 3600) + "h " + ((abs % 3600) / 60) + "m"; } private String stateClass(Trigger.TriggerState state) { if (state == Trigger.TriggerState.NORMAL) return "state-normal"; if (state == Trigger.TriggerState.PAUSED) return "state-paused"; if (state == Trigger.TriggerState.BLOCKED) return "state-blocked"; if (state == Trigger.TriggerState.ERROR) return "state-error"; return ""; } private Map buildTriggerInfo(Scheduler scheduler, Trigger trigger) throws Exception { Map info = new LinkedHashMap<>(); Trigger.TriggerState state = scheduler.getTriggerState(trigger.getKey()); info.put("triggerName", trigger.getKey().getName()); info.put("triggerGroup", trigger.getKey().getGroup()); info.put("state", state.name()); info.put("stateClass", stateClass(state)); info.put("prevFire", fmt(trigger.getPreviousFireTime())); info.put("prevFireRel", relative(trigger.getPreviousFireTime())); info.put("nextFire", fmt(trigger.getNextFireTime())); info.put("nextFireRel", relative(trigger.getNextFireTime())); info.put("priority", trigger.getPriority()); if (trigger instanceof CronTrigger) { info.put("type", "Cron"); info.put("cronExp", ((CronTrigger) trigger).getCronExpression()); } else if (trigger instanceof SimpleTrigger) { SimpleTrigger st = (SimpleTrigger) trigger; info.put("type", "Simple"); info.put("cronExp", "interval=" + st.getRepeatInterval() + "ms, repeat=" + (st.getRepeatCount() == SimpleTrigger.REPEAT_INDEFINITELY ? "INF" : st.getRepeatCount())); } else { info.put("type", trigger.getClass().getSimpleName()); info.put("cronExp", "-"); } if (trigger.getStartTime() != null) info.put("startTime", fmt(trigger.getStartTime())); if (trigger.getEndTime() != null) info.put("endTime", fmt(trigger.getEndTime())); return info; } private List> getJobList(Scheduler scheduler) throws Exception { List> jobs = new ArrayList<>(); for (String group : scheduler.getJobGroupNames()) { for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(group))) { JobDetail detail = scheduler.getJobDetail(jobKey); List triggers = scheduler.getTriggersOfJob(jobKey); Map job = new LinkedHashMap<>(); job.put("name", jobKey.getName()); job.put("group", jobKey.getGroup()); job.put("jobClass", detail.getJobClass().getSimpleName()); job.put("disallowConcurrent", detail.isConcurrentExectionDisallowed()); StringBuilder dataStr = new StringBuilder(); for (String key : detail.getJobDataMap().getKeys()) { if (dataStr.length() > 0) dataStr.append(", "); dataStr.append(key).append("=").append(detail.getJobDataMap().getString(key)); } job.put("jobData", dataStr.toString()); job.put("triggerCount", triggers.size()); List> triggerList = new ArrayList<>(); for (Trigger trigger : triggers) { triggerList.add(buildTriggerInfo(scheduler, trigger)); } job.put("triggerList", triggerList); if (!triggers.isEmpty()) { Trigger trigger = triggers.get(0); Trigger.TriggerState state = scheduler.getTriggerState(trigger.getKey()); job.put("state", state.name()); job.put("stateClass", stateClass(state)); job.put("prevFire", fmt(trigger.getPreviousFireTime())); job.put("prevFireRel", relative(trigger.getPreviousFireTime())); job.put("nextFire", fmt(trigger.getNextFireTime())); job.put("nextFireRel", relative(trigger.getNextFireTime())); if (trigger instanceof CronTrigger) { job.put("cronExp", ((CronTrigger) trigger).getCronExpression()); } else { job.put("cronExp", "-"); } } else { job.put("state", "NO_TRIGGER"); job.put("stateClass", "state-error"); job.put("prevFire", "-"); job.put("prevFireRel", ""); job.put("nextFire", "-"); job.put("nextFireRel", ""); job.put("cronExp", "-"); } jobs.add(job); } } jobs.sort(Comparator.comparing(m -> (String) m.get("name"))); return jobs; } %><% WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(application); Scheduler scheduler = null; Scheduler clusteredScheduler = null; String error = null; try { scheduler = (Scheduler) ctx.getBean("scheduler"); } catch (Exception e) { error = "scheduler bean not found: " + e.getMessage(); } try { clusteredScheduler = (Scheduler) ctx.getBean("clusteredScheduler"); } catch (Exception e) { // clusteredScheduler is optional } // 자동 새로고침 간격 (초), 기본 10초 String refreshParam = request.getParameter("refresh"); int refreshSec = 10; if (refreshParam != null) { try { refreshSec = Integer.parseInt(refreshParam); } catch (NumberFormatException e) { /* ignore */ } if (refreshSec < 1) refreshSec = 1; } boolean autoRefresh = refreshParam != null; %> <% if (autoRefresh) { %> <% } %> Quartz Scheduler Status

Quartz Scheduler Status <% if (autoRefresh) { %>Auto <%= refreshSec %>s<% } %>

Instance: <%= System.getProperty("inst.Name", "N/A") %> | Server Time: <%= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) %>

Auto Refresh: <% if (!autoRefresh) { %>OFF<% } else { %>OFF<% } %> | <% int[] intervals = {5, 10, 30, 60}; %> <% for (int iv : intervals) { %> <% if (autoRefresh && refreshSec == iv) { %><%= iv %>s<% } else { %><%= iv %>s<% } %> <% } %>

<% if (error != null) { %>
<%= error %>
<% } %> <% if (scheduler != null) { %>

Scheduler: <%= scheduler.getSchedulerName() %> (RAM)

Status: "><%= scheduler.isStarted() ? "STARTED" : "STOPPED" %> Instance ID: <%= scheduler.getSchedulerInstanceId() %> Job Count: <%= getJobList(scheduler).size() %>
<% int jobIdx = 0; for (Map job : getJobList(scheduler)) { String rowId = "s_" + jobIdx; List> tList = (List>) job.get("triggerList"); %> <% jobIdx++; } %>
Job Name Job Class Cron State Concurrent Prev Fire Next Fire Job Data
<%= job.get("name") %>(<%= job.get("triggerCount") %> triggers) <%= job.get("jobClass") %> <%= job.get("cronExp") %> "><%= job.get("state") %> "> <%= (Boolean)job.get("disallowConcurrent") ? "NO (protected)" : "YES (allowed)" %> <%= job.get("prevFire") %><%= job.get("prevFireRel") %> <%= job.get("nextFire") %><%= job.get("nextFireRel") %> <%= job.get("jobData") %>
<% if (tList.isEmpty()) { %> No triggers <% } else { %> <% for (Map t : tList) { %> <% } %>
Trigger NameGroupTypeExpression / IntervalStatePriorityPrev FireNext Fire
<%= t.get("triggerName") %> <%= t.get("triggerGroup") %> <%= t.get("type") %> <%= t.get("cronExp") %> "><%= t.get("state") %> <%= t.get("priority") %> <%= t.get("prevFire") %><%= t.get("prevFireRel") %> <%= t.get("nextFire") %><%= t.get("nextFireRel") %>
<% } %>
<% } %> <% if (clusteredScheduler != null) { %>

Clustered Scheduler: <%= clusteredScheduler.getSchedulerName() %> (DB)

Status: "><%= clusteredScheduler.isStarted() ? "STARTED" : "STOPPED" %> Instance ID: <%= clusteredScheduler.getSchedulerInstanceId() %> Job Count: <%= getJobList(clusteredScheduler).size() %>
<% int cJobIdx = 0; for (Map job : getJobList(clusteredScheduler)) { String rowId = "c_" + cJobIdx; List> tList = (List>) job.get("triggerList"); %> <% cJobIdx++; } %>
Job Name Job Class Cron State Concurrent Prev Fire Next Fire Job Data
<%= job.get("name") %>(<%= job.get("triggerCount") %> triggers) <%= job.get("jobClass") %> <%= job.get("cronExp") %> "><%= job.get("state") %> "> <%= (Boolean)job.get("disallowConcurrent") ? "NO (protected)" : "YES (allowed)" %> <%= job.get("prevFire") %><%= job.get("prevFireRel") %> <%= job.get("nextFire") %><%= job.get("nextFireRel") %> <%= job.get("jobData") %>
<% if (tList.isEmpty()) { %> No triggers <% } else { %> <% for (Map t : tList) { %> <% } %>
Trigger NameGroupTypeExpression / IntervalStatePriorityPrev FireNext Fire
<%= t.get("triggerName") %> <%= t.get("triggerGroup") %> <%= t.get("type") %> <%= t.get("cronExp") %> "><%= t.get("state") %> <%= t.get("priority") %> <%= t.get("prevFire") %><%= t.get("prevFireRel") %> <%= t.get("nextFire") %><%= t.get("nextFireRel") %>
<% } %>
<% } %>