From 604601e8c6a8ec84b59908da705a623184bd7fb8 Mon Sep 17 00:00:00 2001 From: Chris Dessonville Date: Fri, 5 Dec 2014 11:31:57 -0800 Subject: [PATCH 1/2] Allow dynamic reconfiguring in the background. --- README | 2 +- .../BackgroundConfigurationRefresher.java | 105 ++++++++++++++++++ .../GangliaXmlConfigurationService.java | 6 +- .../info/ganglia/jmxetric/JMXetricAgent.java | 5 +- .../ganglia/jmxetric/JMXetricAgentIT.java | 2 - src/test/resources/jmxetric_test.xml | 12 +- 6 files changed, 118 insertions(+), 14 deletions(-) create mode 100644 src/main/java/info/ganglia/jmxetric/BackgroundConfigurationRefresher.java diff --git a/README b/README index 85206ae..c697572 100644 --- a/README +++ b/README @@ -62,7 +62,7 @@ Configuration (so that metrics from different JVMs on the same host can be determined) - XML Configuation File + XML Configuration File JMXetric schedules a number of "samples", that queries a list of "mbeans", that have "attributes". diff --git a/src/main/java/info/ganglia/jmxetric/BackgroundConfigurationRefresher.java b/src/main/java/info/ganglia/jmxetric/BackgroundConfigurationRefresher.java new file mode 100644 index 0000000..c8f5f47 --- /dev/null +++ b/src/main/java/info/ganglia/jmxetric/BackgroundConfigurationRefresher.java @@ -0,0 +1,105 @@ +package info.ganglia.jmxetric; + +import info.ganglia.gmetric4j.gmetric.GMetric; +import org.xml.sax.InputSource; + +import org.w3c.dom.Node; +import java.io.*; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Timer; +import java.util.TimerTask; + + +public class BackgroundConfigurationRefresher extends XMLConfigurationService { + + private JMXetricAgent agent = null; + private final InputSource inputSource; + private final CommandLineArgs args; + private final String agentArgs; + private int pollingFrequency; + private int lastHash = -1; + + public BackgroundConfigurationRefresher(String agentArgs) { + this.agentArgs = agentArgs; + this.args = new CommandLineArgs(agentArgs); + this.inputSource = new InputSource(args.getConfig()); + } + + public void initialize() throws Exception { + Node root = getXmlNode("/jmxetric-config/refreshconfig", inputSource); + + startNewAgent(); + + if (isEnabled(root)) { + pollingFrequency = getPollingFrequencySeconds(root); + startBackgroundThread(); + } + } + + private void startNewAgent() throws Exception { + if (agent != null) { + agent.stop(); + GMetric gmetric = agent.getGmetric(); + if (gmetric != null) { + gmetric.close(); + } + agent = null; + } + + agent = new JMXetricAgent(); + XMLConfigurationService.configure(agent, agentArgs); + agent.start(); + lastHash = getConfigHash(); + } + + private boolean isEnabled(Node root) { + return selectParameterFromNode(root, "enabled", "false").toLowerCase().equals("true"); + } + + private int getPollingFrequencySeconds(Node root) { + return Integer.parseInt(selectParameterFromNode(root, "frequency", "5")); + } + + private int getConfigHash() throws NoSuchAlgorithmException, IOException { + MessageDigest md = MessageDigest.getInstance("MD5"); + + File theFile = new File(args.getConfig()); + InputStream is = new FileInputStream(theFile); + + DigestInputStream dis = new DigestInputStream(is, md); + while (dis.read() != -1) { + // Place holder to read the entire file + } + + byte[] digest = md.digest(); + return new String(digest).hashCode(); + } + + private void startBackgroundThread() { + TimerTask task = new TimerTask() { + @Override + public void run() { + try { + int currentHash = getConfigHash(); + if (currentHash != lastHash) { + System.out.println("The file has changed. Initializing a dynamic reload for the instance."); + startNewAgent(); + } + } catch (FileNotFoundException e) { + System.out.println(String.format("Could not find the config file: %s", args.getConfig())); + } catch (NoSuchAlgorithmException e) { + System.out.println("Your system does not contain the MD5 digest algorithm."); + } catch (Exception e) { + System.out.println("Unknown exception encountered."); + e.printStackTrace(); + } + } + }; + + Timer timer = new Timer(true); + timer.scheduleAtFixedRate(task, pollingFrequency * 1000, pollingFrequency * 1000); + } + +} diff --git a/src/main/java/info/ganglia/jmxetric/GangliaXmlConfigurationService.java b/src/main/java/info/ganglia/jmxetric/GangliaXmlConfigurationService.java index 601db29..7286665 100644 --- a/src/main/java/info/ganglia/jmxetric/GangliaXmlConfigurationService.java +++ b/src/main/java/info/ganglia/jmxetric/GangliaXmlConfigurationService.java @@ -126,9 +126,9 @@ private int getPort() { /** * UDPAddressingMode to use for reporting * - * @return {@link info.ganglia.gmetric4j.gmetric.UDPAddressingMode.UNICAST} + * @return {@link info.ganglia.gmetric4j.gmetric.GMetric.UDPAddressingMode.UNICAST} * or - * {@link info.ganglia.gmetric4j.gmetric.UDPAddressingMode.MULTICAST} + * {@link info.ganglia.gmetric4j.gmetric.GMetric.UDPAddressingMode.MULTICAST} */ private UDPAddressingMode getAddressingMode() { String mode = getGangliaConfig(args.getMode(), ganglia, "mode", @@ -206,4 +206,4 @@ String getConfigString() throws XPathExpressionException { buf.append(" spoof=").append(spoof); return buf.toString(); } -} \ No newline at end of file +} diff --git a/src/main/java/info/ganglia/jmxetric/JMXetricAgent.java b/src/main/java/info/ganglia/jmxetric/JMXetricAgent.java index fb14b8d..3c2517d 100644 --- a/src/main/java/info/ganglia/jmxetric/JMXetricAgent.java +++ b/src/main/java/info/ganglia/jmxetric/JMXetricAgent.java @@ -45,11 +45,8 @@ public static void main(String[] args) throws Exception { */ public static void premain(String agentArgs, Instrumentation inst) { System.out.println(STARTUP_NOTICE) ; - JMXetricAgent a = null ; try { - a = new JMXetricAgent(); - XMLConfigurationService.configure(a, agentArgs); - a.start(); + new BackgroundConfigurationRefresher(agentArgs).initialize(); } catch ( Exception ex ) { // log.severe("Exception starting JMXetricAgent"); ex.printStackTrace(); diff --git a/src/test/java/info/ganglia/jmxetric/JMXetricAgentIT.java b/src/test/java/info/ganglia/jmxetric/JMXetricAgentIT.java index c2412f8..0f1edf1 100644 --- a/src/test/java/info/ganglia/jmxetric/JMXetricAgentIT.java +++ b/src/test/java/info/ganglia/jmxetric/JMXetricAgentIT.java @@ -4,8 +4,6 @@ import info.ganglia.gmetric4j.gmetric.GMetricResult; -import info.ganglia.jmxetric.JMXetricAgent; -import info.ganglia.jmxetric.XMLConfigurationService; import java.lang.management.ManagementFactory; diff --git a/src/test/resources/jmxetric_test.xml b/src/test/resources/jmxetric_test.xml index 71ce6da..eb0c416 100644 --- a/src/test/resources/jmxetric_test.xml +++ b/src/test/resources/jmxetric_test.xml @@ -1,6 +1,6 @@ + @@ -28,6 +28,9 @@ + + + ]> @@ -74,11 +77,12 @@ - - - + + + + From ba3351c1e6f9e2de7166dae8f403fe2e6b5dfcfe Mon Sep 17 00:00:00 2001 From: Chris Dessonville Date: Tue, 2 Jun 2015 10:19:25 -0700 Subject: [PATCH 2/2] Prevent file descriptor leaks by closing streams --- .../BackgroundConfigurationRefresher.java | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/main/java/info/ganglia/jmxetric/BackgroundConfigurationRefresher.java b/src/main/java/info/ganglia/jmxetric/BackgroundConfigurationRefresher.java index c8f5f47..26ea905 100644 --- a/src/main/java/info/ganglia/jmxetric/BackgroundConfigurationRefresher.java +++ b/src/main/java/info/ganglia/jmxetric/BackgroundConfigurationRefresher.java @@ -68,13 +68,21 @@ private int getConfigHash() throws NoSuchAlgorithmException, IOException { File theFile = new File(args.getConfig()); InputStream is = new FileInputStream(theFile); - DigestInputStream dis = new DigestInputStream(is, md); - while (dis.read() != -1) { - // Place holder to read the entire file - } + try { + DigestInputStream dis = new DigestInputStream(is, md); + try { + while (dis.read() != -1) { + // NOTE: This reads the entire file, that's why the while loop is blank. + } - byte[] digest = md.digest(); - return new String(digest).hashCode(); + byte[] digest = md.digest(); + return new String(digest).hashCode(); + } finally { + dis.close(); + } + } finally { + is.close(); + } } private void startBackgroundThread() {