diff --git a/src/main/java/com/bsmlab/dfx/agent/config/AgentConfigReader.java b/src/main/java/com/bsmlab/dfx/agent/config/AgentConfigReader.java index 8ec3992..bea9a21 100644 --- a/src/main/java/com/bsmlab/dfx/agent/config/AgentConfigReader.java +++ b/src/main/java/com/bsmlab/dfx/agent/config/AgentConfigReader.java @@ -1,11 +1,13 @@ package com.bsmlab.dfx.agent.config; +import com.bsmlab.dfx.agent.config.jmx.DfxJmxRegistrar; import com.fasterxml.jackson.databind.DatabindException; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.stereotype.Component; import java.io.File; @@ -29,6 +31,11 @@ public class AgentConfigReader { private String applicationCommitId; @Setter private boolean connectedConsole; + private final ConfigurableApplicationContext configurableApplicationContext; + + public AgentConfigReader(ConfigurableApplicationContext configurableApplicationContext) { + this.configurableApplicationContext = configurableApplicationContext; + } public void loadConfigFile(String configFilePath) { try { @@ -41,6 +48,7 @@ public class AgentConfigReader { this.knownAgentStatusMap.put(knownAgent.getHostId(), "DOWN"); } } + DfxJmxRegistrar.register(this.agentConfigDto.getMyHostId(), this.configurableApplicationContext); } catch (DatabindException e) { log.error("cannot parse a setting file. {}", configFilePath, e); log.error(e.getMessage(), e); diff --git a/src/main/java/com/bsmlab/dfx/agent/config/DfxAgentConfiguration.java b/src/main/java/com/bsmlab/dfx/agent/config/DfxAgentConfiguration.java index c07f76a..e1b7239 100644 --- a/src/main/java/com/bsmlab/dfx/agent/config/DfxAgentConfiguration.java +++ b/src/main/java/com/bsmlab/dfx/agent/config/DfxAgentConfiguration.java @@ -15,6 +15,7 @@ import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScans; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.Resource; @@ -55,13 +56,13 @@ public class DfxAgentConfiguration { // agent 설정 관리자. 대부분의 기능에 필요함 @Bean(name = "agentConfigReader") - public AgentConfigReader agentConfigReader() throws URISyntaxException, IOException { // 실행확인됨 + public AgentConfigReader agentConfigReader(ConfigurableApplicationContext configurableApplicationContext) throws URISyntaxException, IOException { // 실행확인됨 if(StringUtils.isBlank(this.settingFile)) { log.error("cannot found a setting file. {}", this.settingFile); log.error("exit application"); System.exit(0); } - AgentConfigReader agentConfigReader = new AgentConfigReader(); + AgentConfigReader agentConfigReader = new AgentConfigReader(configurableApplicationContext); agentConfigReader.loadConfigFile(this.settingFile); // Process ID를 찾아 app_home/proc/pid 파일에 기록한다. 이후 shutdown.sh에서 해당 pid를 사용하여 kill -9 pid 를 실행한다. this.pid = String.valueOf(ProcessHandle.current().pid()); diff --git a/src/main/java/com/bsmlab/dfx/agent/config/jmx/DfxAgentControl.java b/src/main/java/com/bsmlab/dfx/agent/config/jmx/DfxAgentControl.java new file mode 100644 index 0000000..453e8ce --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/agent/config/jmx/DfxAgentControl.java @@ -0,0 +1,27 @@ +package com.bsmlab.dfx.agent.config.jmx; + +import lombok.RequiredArgsConstructor; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; + +@RequiredArgsConstructor +public class DfxAgentControl implements DfxAgentControlMBean { + private final ConfigurableApplicationContext ctx; + + @Override + public String status() { + return "OK"; + } + + @Override + public void shutdown(int exitCode, int delaySeconds) { + new Thread(() -> { + try { + if (delaySeconds > 0) Thread.sleep(delaySeconds * 1000L); + } catch (InterruptedException ignored) { } + + int code = SpringApplication.exit(ctx, () -> exitCode); + System.exit(code); + }, "dfxagent-jmx-shutdown").start(); + } +} diff --git a/src/main/java/com/bsmlab/dfx/agent/config/jmx/DfxAgentControlMBean.java b/src/main/java/com/bsmlab/dfx/agent/config/jmx/DfxAgentControlMBean.java new file mode 100644 index 0000000..f5ca990 --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/agent/config/jmx/DfxAgentControlMBean.java @@ -0,0 +1,6 @@ +package com.bsmlab.dfx.agent.config.jmx; + +public interface DfxAgentControlMBean { + String status(); + void shutdown(int exitCode, int delaySeconds); +} diff --git a/src/main/java/com/bsmlab/dfx/agent/config/jmx/DfxJmxRegistrar.java b/src/main/java/com/bsmlab/dfx/agent/config/jmx/DfxJmxRegistrar.java new file mode 100644 index 0000000..eb899cb --- /dev/null +++ b/src/main/java/com/bsmlab/dfx/agent/config/jmx/DfxJmxRegistrar.java @@ -0,0 +1,28 @@ +package com.bsmlab.dfx.agent.config.jmx; + +import javax.management.MBeanServer; +import javax.management.ObjectName; +import java.lang.management.ManagementFactory; + +import org.springframework.context.ConfigurableApplicationContext; + +public final class DfxJmxRegistrar { + + private DfxJmxRegistrar() {} + + public static ObjectName register(String agentId, ConfigurableApplicationContext configurableApplicationContext) { + try { + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); + // agentId는 특수문자 가능하므로 quote 처리 + String quotedAgentId = ObjectName.quote(agentId); + ObjectName name = new ObjectName("com.bsmlab.dfx.agent:type=Control,agentId=" + quotedAgentId); + if (!mbs.isRegistered(name)) { + mbs.registerMBean(new DfxAgentControl(configurableApplicationContext), name); + } + return name; + } + catch (Exception e) { + throw new IllegalStateException("Failed to register JMX MBean for agentId=" + agentId, e); + } + } +}