├── .classpath ├── .gitignore ├── .project ├── .travis.yml ├── COPYING ├── README ├── etc ├── jmxetric.xml └── logging.properties ├── pom.xml └── src ├── main ├── assembly │ └── bin.xml ├── java │ └── info │ │ └── ganglia │ │ └── jmxetric │ │ ├── CommandLineArgs.java │ │ ├── GangliaXmlConfigurationService.java │ │ ├── JMXetricAgent.java │ │ ├── JMXetricXmlConfigurationService.java │ │ ├── MBeanAttribute.java │ │ ├── MBeanHolder.java │ │ ├── MBeanSampler.java │ │ ├── MBeanScanner.java │ │ ├── XMLConfigurationService.java │ │ └── package.html └── resources │ └── META-INF │ └── MANIFEST.MF └── test ├── java └── info │ └── ganglia │ └── jmxetric │ ├── CommandLineArgsTest.java │ ├── Example.java │ ├── ExampleComposite.java │ ├── GangliaXmlConfigurationServiceTest.java │ ├── JMXetricAgentIT.java │ ├── JMXetricXmlConfigurationServiceTest.java │ ├── MBeanSamplerTest.java │ └── TestExampleMXBean.java └── resources └── jmxetric_test.xml /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | /.settings 3 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | jmxetric 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.maven.ide.eclipse.maven2Builder 15 | 16 | 17 | 18 | 19 | org.eclipse.m2e.core.maven2Builder 20 | 21 | 22 | 23 | 24 | 25 | org.eclipse.m2e.core.maven2Nature 26 | org.maven.ide.eclipse.maven2Nature 27 | org.eclipse.jdt.core.javanature 28 | 29 | 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk7 4 | - openjdk6 5 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2011 Jasper Humphrey 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Name 2 | jmxetric - jvm instrumentation to ganglia 3 | 4 | Version 5 | The latest version of this software and document will be found at 6 | https://github.com/ganglia/jmxetric 7 | 8 | Synopsis 9 | JMXetric is a 100% java, configurable JVM agent that periodically polls 10 | MBean attributes and reports their values to Ganglia. 11 | 12 | Project goals are that JMXetric should be 13 | * configurable (xml) 14 | * lightweight (small memory and cpu footprint) 15 | * standalone (not depend on third party libraries) 16 | 17 | The gmetric protocol implementation uses classes generated by the LGPL 18 | remotetea project (http://remotetea.sf.net). 19 | 20 | Installation 21 | 22 | Unzip the archive 23 | Add the following to your JVM 24 | java -javaagent:/jmxetric.jar=host="",port="",config="",process="" usual.java.main.class 25 | 26 | Demo / Quickstart 27 | Check the matching version of gmetric4j.jar in the pom.xml 28 | 1) Ensure you have a gmond running on localhost:8649 29 | $ pgrep gmond # should return a valid PID 30 | $ nc localhost 8649 # dumps some XML to stdout 31 | 2) $ git clone https://github.com/ganglia/jmxetric.git 32 | 3) Download jmxetric.jar, gmetric4j.jar and remotetea-oncrpc.jar 33 | all into the same directory. 34 | 4) $ cd jmxetric 35 | 5) In bash do: 36 | $ export config="host=localhost,port=8649,wireformat31x=true,config=etc/jmxetric.xml" 37 | $ java -Djava.util.logging.config.file=etc/logging.properties \ 38 | -cp gmetric4j.jar:remotetea-oncrpc.jar:jmxetric.jar \ 39 | -javaagent:jmxetric.jar=$config info.ganglia.jmxetric.JMXetricAgent 40 | 41 | Configuration 42 | The configuration of JMXetric is loaded from an xml file in the working 43 | directory of the shell, or specified as an argument to the JVM agent 44 | ("config"). The JVM agent arguments can also be used to specify the 45 | following: 46 | 47 | host, port The multicast address that is used to publish metrics to the 48 | ganglia gmond process 49 | 50 | config The full path to the config file (jmxetric.xml) 51 | 52 | mode The UDP addressing mode, either multicast or unicast (default multicast) 53 | 54 | wireformat31x True if the ganglia v3.1.x wire format should be used (default false) 55 | 56 | spoof An IP:hostname pair that will be used to spoof the metric host information. 57 | (default: no spoofing, i.e., local hostname reported by OS) 58 | 59 | process A name that is prefixed to the metric name before publication 60 | (so that metrics from different JVMs on the same host can be 61 | determined) 62 | 63 | XML Configuation File 64 | 65 | JMXetric schedules a number of "samples", that queries a list of "mbeans", 66 | that have "attributes". 67 | 68 | Element/Attribute Description 69 | mbean/name The name of the mbean to query 70 | mbean/pname The metric name for this mbean. This should always be 71 | used as ganglia/rrdtool misbehaves if the filename is 72 | "unusual". 73 | attribute/nam The name of the attribute. This can have two levels 74 | for the case of a composite key see below example for 75 | attribute "HeapMemoryUsage", key "used" 76 | attribute/type The type used for the metric in ganglia 77 | attribute/units The units used for the metric in ganglia 78 | attribute/pname The metric name for this attribute 79 | 80 | An example file: 81 | 82 | 83 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | ]> 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | Support/Queries 141 | I'm sure there are bugs and I'm sure there are some mbeans that can't be 142 | sampled currently. If you find some, then let me know jasper521 at gmail 143 | 144 | If you are using this at all, then I'd love to know! 145 | 146 | Copyright 147 | Copyright (C) 2008-2011 Jasper Humphrey, jasper521@gmail.com 148 | Copyright (C) 2011-2015 Daniel Pocock, http://danielpocock.com 149 | 150 | -------------------------------------------------------------------------------- /etc/jmxetric.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ]> 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 59 | 60 | 61 | 62 | 65 | 66 | 67 | 70 | 71 | 72 | 75 | 76 | 77 | 81 | 82 | 83 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /etc/logging.properties: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # Default Logging Configuration File 3 | # 4 | # You can use a different file by specifying a filename 5 | # with the java.util.logging.config.file system property. 6 | # For example java -Djava.util.logging.config.file=myfile 7 | ############################################################ 8 | 9 | handlers= java.util.logging.ConsoleHandler 10 | 11 | .level=INFO 12 | 13 | java.util.logging.ConsoleHandler.level = ALL 14 | java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter 15 | 16 | info.ganglia.jmxetric.level=ALL 17 | info.ganglia.gmetric4j.level=ALL 18 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | info.ganglia.jmxetric 5 | jmxetric 6 | JMXetric 7 | jar 8 | 1.0.8 9 | JVM instrumentation to Ganglia 10 | http://github.com/ganglia/jmxetric 11 | 12 | 13 | The MIT License 14 | http://www.opensource.org/licenses/mit-license.php 15 | repo 16 | 17 | 18 | 19 | scm:git:git@github.com:ganglia/jmxetric.git 20 | scm:git:git@github.com:ganglia/jmxetric.git 21 | scm:git:git@github.com:ganglia/jmxetric.git 22 | 23 | 24 | 25 | humphrej 26 | Jasper Humphrey 27 | jasper521@googlemail.com 28 | 29 | 30 | pocock 31 | Daniel Pocock 32 | daniel@pocock.pro 33 | http://danielpocock.com 34 | 35 | 36 | 37 | 1.0.10 38 | 39 | 40 | 41 | info.ganglia.gmetric4j 42 | gmetric4j 43 | ${gmetric4j.version} 44 | jar 45 | 46 | 47 | info.ganglia.gmetric4j 48 | gmetric4j 49 | ${gmetric4j.version} 50 | test-jar 51 | test 52 | 53 | 54 | junit 55 | junit 56 | 4.1 57 | jar 58 | test 59 | 60 | 61 | 62 | org.sonatype.oss 63 | oss-parent 64 | 7 65 | 66 | 67 | 68 | 69 | src/main/resources 70 | true 71 | 72 | 73 | 74 | 75 | org.apache.maven.plugins 76 | maven-compiler-plugin 77 | 2.5.1 78 | 79 | 1.5 80 | 1.5 81 | 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-jar-plugin 86 | 2.4 87 | 88 | 89 | target/classes/META-INF/MANIFEST.MF 90 | 91 | 92 | 93 | 94 | org.apache.maven.plugins 95 | maven-source-plugin 96 | 97 | 98 | attach-sources 99 | 100 | jar 101 | 102 | 103 | 104 | 105 | 106 | org.apache.maven.plugins 107 | maven-javadoc-plugin 108 | 109 | 110 | attach-javadocs 111 | 112 | jar 113 | 114 | 115 | 116 | 117 | 118 | org.apache.maven.plugins 119 | maven-assembly-plugin 120 | 2.3 121 | 122 | 123 | src/main/assembly/bin.xml 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | org.codehaus.mojo 133 | findbugs-maven-plugin 134 | 2.5.2 135 | 136 | 137 | org.apache.maven.plugins 138 | maven-javadoc-plugin 139 | 140 | 141 | 142 | 143 | 144 | release-sign-artifacts 145 | 146 | 147 | performRelease 148 | true 149 | 150 | 151 | 152 | 153 | 154 | org.apache.maven.plugins 155 | maven-gpg-plugin 156 | 1.4 157 | 158 | 159 | sign-artifacts 160 | verify 161 | 162 | sign 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /src/main/assembly/bin.xml: -------------------------------------------------------------------------------- 1 | 2 | bin 3 | 4 | zip 5 | 6 | true 7 | 8 | 9 | false 10 | 11 | 12 | 13 | 14 | 15 | etc/* 16 | README* 17 | LICENSE* 18 | NOTICE* 19 | COPYING* 20 | CHANGES* 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/info/ganglia/jmxetric/CommandLineArgs.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | public class CommandLineArgs { 7 | public static final String DEFAULT_CONFIG = "jmxetric.xml"; 8 | private final static Pattern pattern = Pattern.compile("(\\S+?)\\=(\\S*)"); 9 | 10 | private String host = null; 11 | private String port = null; 12 | private String config = null; 13 | private String mode = null; 14 | private String wireformat = null; 15 | private String processName = null; 16 | private String spoof = null; 17 | 18 | public CommandLineArgs(String arguments) { 19 | String commandLine = arguments == null ? "" : arguments; 20 | String[] args = commandLine.split(","); 21 | 22 | host = getTagValue("host", args, null); 23 | port = getTagValue("port", args, null); 24 | config = getTagValue("config", args, DEFAULT_CONFIG); 25 | mode = getTagValue("mode", args, null); 26 | wireformat = getTagValue("wireformat31x", args, null); 27 | processName = getTagValue("process", args, null); 28 | spoof = getTagValue("spoof", args, null); 29 | } 30 | 31 | public String getHost() { 32 | return host; 33 | } 34 | 35 | public String getPort() { 36 | return port; 37 | } 38 | 39 | public String getConfig() { 40 | return config; 41 | } 42 | 43 | public String getMode() { 44 | return mode; 45 | } 46 | 47 | public String getWireformat() { 48 | return wireformat; 49 | } 50 | 51 | public String getProcessName() { 52 | return processName; 53 | } 54 | 55 | public String getSpoof() { 56 | return spoof; 57 | } 58 | 59 | /* 60 | * Parses the string array, input, looking for a pattern tag=value 61 | * 62 | * @param tag the tag to search for 63 | * 64 | * @param input the array list 65 | * 66 | * @param defaultValue the default value if tag is not found 67 | * 68 | * @return tha value 69 | */ 70 | private String getTagValue(String tag, String[] input, String defaultValue) { 71 | for (String arg : input) { 72 | Matcher matcher = CommandLineArgs.pattern.matcher(arg); 73 | // Get tagname and contents of tag 74 | if (matcher.find()) { 75 | String tagname = matcher.group(1); 76 | if (tag.equals(tagname)) { 77 | return matcher.group(2); 78 | } 79 | } 80 | } 81 | return defaultValue; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/info/ganglia/jmxetric/GangliaXmlConfigurationService.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import info.ganglia.gmetric4j.gmetric.GMetric; 4 | import info.ganglia.gmetric4j.gmetric.GMetric.UDPAddressingMode; 5 | 6 | import java.io.IOException; 7 | import java.util.logging.Logger; 8 | 9 | import javax.xml.xpath.XPathExpressionException; 10 | 11 | import org.w3c.dom.Node; 12 | import org.xml.sax.InputSource; 13 | 14 | /** 15 | * Configures JMXetricAgent based on command line arguments and parameters 16 | * specified in the XML configuration source. 17 | * 18 | */ 19 | class GangliaXmlConfigurationService extends XMLConfigurationService { 20 | private static Logger log = Logger.getLogger(JMXetricAgent.class.getName()); 21 | 22 | /** 23 | * default host name gmond is running on 24 | */ 25 | private static final String DEFAULT_HOSTNAME = "localhost"; 26 | 27 | /** 28 | * default port gmond listens to 29 | */ 30 | private static final String DEFAULT_PORT = "8649"; 31 | 32 | /** 33 | * default transport mode 34 | */ 35 | private static final String DEFAULT_MODE = "multicast"; 36 | 37 | /** 38 | * default multicast TTL = 5 (same site) 39 | */ 40 | private static final int DEFAULT_TTL = 5; 41 | 42 | /** 43 | * the XML configuration file source 44 | */ 45 | private final InputSource inputSource; 46 | 47 | /** 48 | * command line arguments that was passed in 49 | */ 50 | private final CommandLineArgs args; 51 | 52 | private Node ganglia; 53 | 54 | public GangliaXmlConfigurationService(InputSource inputSource, 55 | CommandLineArgs args) { 56 | this.inputSource = inputSource; 57 | this.args = args; 58 | } 59 | 60 | /** 61 | * Creates a GMetric attribute on the JMXetricAgent from the XML config 62 | * 63 | * @return 64 | * @throws IOException 65 | * @throws XPathExpressionException 66 | */ 67 | public GMetric getConfig() throws IOException, XPathExpressionException { 68 | // TODO what happens when the node cannot be found? do we use all 69 | // default values? 70 | ganglia = getXmlNode("/jmxetric-config/ganglia", inputSource); 71 | // Gets the config for ganglia 72 | // Note that the ganglia config needs to be found before the samplers 73 | // are created. 74 | GMetric gmetric = makeGMetricFromXml(); 75 | return gmetric; 76 | } 77 | 78 | /** 79 | * Makes a GMetric object that can be use to define configuration for an 80 | * agent. 81 | * 82 | * @return GMetric object with the configuration 83 | * @throws IOException 84 | */ 85 | GMetric makeGMetricFromXml() throws IOException { 86 | String hostname = getHostName(); 87 | int port = getPort(); 88 | UDPAddressingMode addressingMode = getAddressingMode(); 89 | boolean v31x = getV31(); 90 | String spoof = getSpoof(); 91 | 92 | StringBuilder buf = new StringBuilder(); 93 | buf.append("GMetric host=").append(hostname); 94 | buf.append(" port=").append(port); 95 | buf.append(" mode=").append(addressingMode); 96 | buf.append(" v31x=").append(v31x); 97 | buf.append(" spoof=").append(spoof); 98 | log.fine(buf.toString()); 99 | System.out.println(buf.toString()); 100 | return new GMetric(hostname, port, addressingMode, DEFAULT_TTL, v31x, 101 | null, spoof); 102 | } 103 | 104 | /** 105 | * Gets the name of the host to be reported to. 106 | * 107 | * @return name of host, defaults to "localhost" 108 | */ 109 | private String getHostName() { 110 | return getGangliaConfig(args.getHost(), ganglia, "hostname", 111 | DEFAULT_HOSTNAME); 112 | } 113 | 114 | /** 115 | * Gets the port that JMXetric will announce to, this is usually the port 116 | * gmond is running on 117 | * 118 | * @return port number, defaults to 8649 119 | */ 120 | private int getPort() { 121 | String port = getGangliaConfig(args.getPort(), ganglia, "port", 122 | DEFAULT_PORT); 123 | return Integer.parseInt(port); 124 | } 125 | 126 | /** 127 | * UDPAddressingMode to use for reporting 128 | * 129 | * @return {@link info.ganglia.gmetric4j.gmetric.UDPAddressingMode.UNICAST} 130 | * or 131 | * {@link info.ganglia.gmetric4j.gmetric.UDPAddressingMode.MULTICAST} 132 | */ 133 | private UDPAddressingMode getAddressingMode() { 134 | String mode = getGangliaConfig(args.getMode(), ganglia, "mode", 135 | DEFAULT_MODE); 136 | if (mode.toLowerCase().equals("unicast")) { 137 | return UDPAddressingMode.UNICAST; 138 | } else { 139 | return UDPAddressingMode.MULTICAST; 140 | } 141 | } 142 | 143 | /** 144 | * Whether the reporting be done on the new wire format 31. 145 | * 146 | * @return true if new format is to be used 147 | */ 148 | private boolean getV31() { 149 | String stringv31x = getGangliaConfig(args.getWireformat(), ganglia, 150 | "wireformat31x", "false"); 151 | return Boolean.parseBoolean(stringv31x); 152 | } 153 | 154 | /** 155 | * Gets the value of the spoof parameter. 156 | * 157 | * @return value of spoof 158 | */ 159 | private String getSpoof() { 160 | return getGangliaConfig(args.getSpoof(), ganglia, "spoof", null); 161 | } 162 | 163 | /** 164 | * Gets a configuration parameter for Ganglia. First checks if it was given 165 | * on the command line arguments. If it is not available, it looks for the 166 | * value in the XML node. 167 | * 168 | * @param cmdLine 169 | * command line value for this attribute 170 | * @param ganglia 171 | * the XML node 172 | * @param attributeName 173 | * name of the attribute 174 | * @param defaultValue 175 | * default value if this attribute cannot be found 176 | * @return the string value of the specified attribute 177 | */ 178 | private String getGangliaConfig(String cmdLine, Node ganglia, 179 | String attributeName, String defaultValue) { 180 | if (cmdLine == null) { 181 | return selectParameterFromNode(ganglia, attributeName, defaultValue); 182 | } else { 183 | return cmdLine; 184 | } 185 | } 186 | 187 | /** 188 | * Method used by tests to print out the read in configuration. 189 | * 190 | * @return string representation of configuration 191 | * @throws XPathExpressionException 192 | */ 193 | String getConfigString() throws XPathExpressionException { 194 | ganglia = getXmlNode("/jmxetric-config/ganglia", inputSource); 195 | String hostname = getHostName(); 196 | int port = getPort(); 197 | UDPAddressingMode addressingMode = getAddressingMode(); 198 | boolean v31x = getV31(); 199 | String spoof = getSpoof(); 200 | 201 | StringBuilder buf = new StringBuilder(); 202 | buf.append("GMetric host=").append(hostname); 203 | buf.append(" port=").append(port); 204 | buf.append(" mode=").append(addressingMode); 205 | buf.append(" v31x=").append(v31x); 206 | buf.append(" spoof=").append(spoof); 207 | return buf.toString(); 208 | } 209 | } -------------------------------------------------------------------------------- /src/main/java/info/ganglia/jmxetric/JMXetricAgent.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | 4 | import info.ganglia.gmetric4j.GMonitor; 5 | 6 | import java.lang.instrument.Instrumentation; 7 | // import java.util.logging.Logger; 8 | 9 | /** 10 | * JMXetricAgent is a JVM agent that will sample MBean attributes on a periodic basis, 11 | * publishing the value of those attributes to the Ganglia gmond process. 12 | *
13 | * Use:
14 | * java -javaagent:path/jmxetric.jar=args yourmainclass 15 | *
16 | * Example:
17 | * java -javaagent:/opt/jmxetric_0_1/jmxetric.jar=host="localhost",port="8649",config=/opt/jmxetric_0_1/jmxetric.xml yourmainclass 18 | *
19 | * Arguments can be:
20 | * 21 | * 22 | * 23 | * 24 | * 25 | *
ArgumentDefaultDescription
hostHost address for ganglia
portPort for ganglia
configjmxetric.xmlConfig file path
26 | */ 27 | public class JMXetricAgent extends GMonitor { 28 | // private static Logger log = 29 | // Logger.getLogger(JMXetricAgent.class.getName()); 30 | /** 31 | * A log running, trivial main method for test purposes 32 | * premain method 33 | * @param args Not used 34 | */ 35 | public static void main(String[] args) throws Exception { 36 | while( true ) { 37 | Thread.sleep(1000*60*5); 38 | System.out.println("Test wakeup"); 39 | } 40 | } 41 | /** 42 | * The JVM agent entry point 43 | * @param agentArgs 44 | * @param inst 45 | */ 46 | public static void premain(String agentArgs, Instrumentation inst) { 47 | System.out.println(STARTUP_NOTICE) ; 48 | JMXetricAgent a = null ; 49 | try { 50 | a = new JMXetricAgent(); 51 | XMLConfigurationService.configure(a, agentArgs); 52 | a.start(); 53 | } catch ( Exception ex ) { 54 | // log.severe("Exception starting JMXetricAgent"); 55 | ex.printStackTrace(); 56 | } 57 | } 58 | 59 | private static final String STARTUP_NOTICE="JMXetricAgent instrumented JVM, see https://github.com/ganglia/jmxetric"; 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/info/ganglia/jmxetric/JMXetricXmlConfigurationService.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import info.ganglia.gmetric4j.gmetric.GMetricSlope; 4 | import info.ganglia.gmetric4j.gmetric.GMetricType; 5 | 6 | import java.util.List; 7 | import java.util.Vector; 8 | import java.util.logging.Logger; 9 | 10 | import javax.xml.xpath.XPathExpressionException; 11 | 12 | import org.w3c.dom.Node; 13 | import org.w3c.dom.NodeList; 14 | import org.xml.sax.InputSource; 15 | 16 | /** 17 | * Configures JMXetric using an XML file. The XML file is read and MBeanSamplers 18 | * are created based on what the file specifies. These MBeanSamplers are then 19 | * added to the JMXetricAgent. 20 | */ 21 | public class JMXetricXmlConfigurationService extends XMLConfigurationService { 22 | private static Logger log = Logger.getLogger(JMXetricAgent.class.getName()); 23 | 24 | /** 25 | * agent that is configured using the XML file 26 | */ 27 | private JMXetricAgent agent; 28 | 29 | /** 30 | * XML configuration source 31 | */ 32 | private InputSource inputSource; 33 | 34 | /** 35 | * name that is associated with all declared metrics 36 | */ 37 | private String processName; 38 | 39 | public JMXetricXmlConfigurationService(JMXetricAgent agent, 40 | InputSource inputSource, String processName) { 41 | this.agent = agent; 42 | this.inputSource = inputSource; 43 | this.processName = processName; 44 | } 45 | 46 | /** 47 | * Configures {@link info.ganglia.jmxetric.JMXetricAgent} using XML source 48 | * 49 | * @throws Exception 50 | */ 51 | void configure() throws Exception { 52 | configureProcessName(); 53 | configureJMXetricAgent(); 54 | } 55 | 56 | /** 57 | * The XML file may specify a processName to be used, which can be different 58 | * from the one used in the constructor. The name in XML takes priority. 59 | * 60 | * @throws XPathExpressionException 61 | */ 62 | private void configureProcessName() throws XPathExpressionException { 63 | if (processName != null) { 64 | return; 65 | } 66 | processName = ""; 67 | Node jvm = getXmlNode("/jmxetric-config/jvm", inputSource); 68 | if (jvm != null) { 69 | processName = jvm.getAttributes().getNamedItem("process") 70 | .getNodeValue(); 71 | } 72 | } 73 | 74 | /** 75 | * Use the XML source to configure the agent. Source is read for 76 | * nodes, each node corresponds to a MBeanSampler to be added to the agent. 77 | * 78 | * @throws XPathExpressionException 79 | * @throws Exception 80 | */ 81 | private void configureJMXetricAgent() throws XPathExpressionException, 82 | Exception { 83 | // Gets the config for the samplers 84 | NodeList samples = getXmlNodeList("/jmxetric-config/sample", 85 | inputSource); 86 | for (int i = 0; i < samples.getLength(); i++) { 87 | Node sample = samples.item(i); 88 | MBeanSampler mbSampler = makeMBeanSampler(sample); 89 | agent.addSampler(mbSampler); 90 | } 91 | } 92 | 93 | /** 94 | * A node from the XML source is parsed to make an MBeanSampler. 95 | * 96 | * @param sample 97 | * @return 98 | * @throws Exception 99 | */ 100 | private MBeanSampler makeMBeanSampler(Node sample) throws Exception { 101 | String delayString = selectParameterFromNode(sample, "delay", "60"); 102 | int delay = Integer.parseInt(delayString); 103 | 104 | String initialDelayString = selectParameterFromNode(sample, 105 | "initialdelay", "0"); 106 | int initialDelay = Integer.parseInt(initialDelayString); 107 | 108 | String sampleDMax = selectParameterFromNode(sample, "dmax", "0"); 109 | 110 | MBeanSampler mBeanSampler = new MBeanSampler(initialDelay, delay, 111 | processName); 112 | 113 | NodeList mBeans = getXmlNodeSet("mbean", sample); 114 | for (int j = 0; j < mBeans.getLength(); j++) { 115 | Node mBean = mBeans.item(j); 116 | String mBeanName = selectParameterFromNode(mBean, "name", null); 117 | List attributes = getAttributesForMBean(mBean, 118 | sampleDMax); 119 | for (MBeanAttribute mBeanAttribute : attributes) { 120 | addMBeanAttributeToSampler(mBeanSampler, mBeanName, 121 | mBeanAttribute); 122 | } 123 | } 124 | return mBeanSampler; 125 | } 126 | 127 | /** 128 | * Adds a MBeanAttribute to an MBeanSampler. This also checks if the 129 | * MBeanAttribute to be added already has a MBeanSampler set, if not it will 130 | * set it. 131 | * 132 | * @param mBeanSampler 133 | * @param mBeanName 134 | * @param mBeanAttribute 135 | * @throws Exception 136 | */ 137 | private void addMBeanAttributeToSampler(MBeanSampler mBeanSampler, 138 | String mBeanName, MBeanAttribute mBeanAttribute) throws Exception { 139 | if (mBeanAttribute.getSampler() == null) { 140 | mBeanAttribute.setSampler(mBeanSampler); 141 | } 142 | mBeanSampler.addMBeanAttribute(mBeanName, mBeanAttribute); 143 | } 144 | 145 | /** 146 | * Gets a list of MBeanAttributes for a single , they correspond to 147 | * the tags in the XML file. 148 | * 149 | * @param mBean 150 | * the node 151 | * @param sampleDMax 152 | * value of dmax passed down from 153 | * @return a list of attributes associated to the mbean 154 | * @throws Exception 155 | */ 156 | List getAttributesForMBean(Node mBean, String sampleDMax) 157 | throws Exception { 158 | String mBeanName = selectParameterFromNode(mBean, "name", null); 159 | String mBeanPublishName = selectParameterFromNode(mBean, "pname", ""); 160 | String mBeanDMax = selectParameterFromNode(mBean, "dmax", sampleDMax); 161 | log.finer("Mbean is " + mBeanName); 162 | 163 | NodeList attrs = getXmlNodeSet("attribute", mBean); 164 | List attributes = new Vector(); 165 | 166 | for (int i = 0; i < attrs.getLength(); i++) { 167 | Node attr = attrs.item(i); 168 | if (isComposite(attr)) { 169 | attributes.addAll(makeCompositeAttributes(attr, mBeanName, 170 | mBeanPublishName, mBeanDMax)); 171 | } else { 172 | attributes.add(makeSimpleAttribute(attr, mBeanName, 173 | mBeanPublishName, mBeanDMax)); 174 | } 175 | } 176 | return attributes; 177 | } 178 | 179 | /** 180 | * Checks if the node is an attribute containing composites 181 | * 182 | * @param node 183 | * @return true if the node is an attribute containing composites 184 | */ 185 | private boolean isComposite(Node node) { 186 | return node.getNodeName().equals("attribute") 187 | && node.getChildNodes().getLength() > 0; 188 | } 189 | 190 | /** 191 | * Makes a {@link MBeanAttribute} that corresponds to a simple 192 | * tag. 193 | * 194 | * @param attr 195 | * the node 196 | * @param mBeanName 197 | * name of the parent mbean 198 | * @param mBeanPublishName 199 | * publish name of the parent mbean 200 | * @param mBeanDMax 201 | * value of dmax specified by parent mbean 202 | * @return 203 | */ 204 | private MBeanAttribute makeSimpleAttribute(Node attr, String mBeanName, 205 | String mBeanPublishName, String mBeanDMax) { 206 | MBeanAttribute mba = makeMBeanSimpleAttribute(attr, mBeanName, 207 | mBeanPublishName, mBeanDMax); 208 | return mba; 209 | } 210 | 211 | /** 212 | * Makes a list of {@link MBeanAttribute} that corresponds to an 213 | * node that contains multiple nodes. 214 | * 215 | * @param attr 216 | * the node 217 | * @param mBeanName 218 | * name of the parent mbean 219 | * @param mBeanPublishName 220 | * publish name of the parent mbean 221 | * @param mBeanDMax 222 | * value of dmax specified by parent mbean 223 | * @return list of {@link MBeanAttribute}, one for each 224 | * @throws XPathExpressionException 225 | */ 226 | private List makeCompositeAttributes(Node attr, 227 | String mBeanName, String mBeanPublishName, String mBeanDMax) 228 | throws XPathExpressionException { 229 | List mbas = new Vector(); 230 | MBeanAttribute mba = null; 231 | NodeList composites = getXmlNodeSet("composite", attr); 232 | String name = selectParameterFromNode(attr, "name", "NULL"); 233 | for (int l = 0; l < composites.getLength(); l++) { 234 | Node composite = composites.item(l); 235 | mba = makeMBeanCompositeAttribute(composite, mBeanName, 236 | mBeanPublishName, mBeanDMax, name); 237 | log.finer("Attr is " + name); 238 | mbas.add(mba); 239 | } 240 | return mbas; 241 | } 242 | 243 | private MBeanAttribute makeMBeanSimpleAttribute(Node attr, 244 | String mBeanName, String mBeanPublishName, String mBeanDMax) { 245 | return makeMBeanAttribute(attr, mBeanName, mBeanPublishName, mBeanDMax, 246 | null); 247 | } 248 | 249 | private MBeanAttribute makeMBeanCompositeAttribute(Node composite, 250 | String mBeanName, String mBeanPublishName, String mBeanDMax, 251 | String attrName) { 252 | return makeMBeanAttribute(composite, mBeanName, mBeanPublishName, 253 | mBeanDMax, attrName); 254 | } 255 | 256 | private MBeanAttribute makeMBeanAttribute(Node attr, String mBeanName, 257 | String mBeanPublishName, String mBeanDMax, String attrName) { 258 | String name = selectParameterFromNode(attr, "name", "NULL"); 259 | String units = selectParameterFromNode(attr, "units", ""); 260 | String pname = selectParameterFromNode(attr, "pname", ""); 261 | String slope = selectParameterFromNode(attr, "slope", ""); 262 | String dMax = selectParameterFromNode(attr, "dmax", mBeanDMax); 263 | String type = selectParameterFromNode(attr, "type", ""); 264 | GMetricType gType = GMetricType.valueOf(type.toUpperCase()); 265 | GMetricSlope gSlope = GMetricSlope.valueOf(slope.toUpperCase()); 266 | int dMaxInt = parseDMax(dMax); 267 | String metricName = buildMetricName(processName, mBeanName, 268 | mBeanPublishName, name, pname); 269 | 270 | if (attrName == null) { 271 | return new MBeanAttribute(processName, name, null, gType, units, 272 | gSlope, metricName, dMaxInt); 273 | } else { 274 | return new MBeanAttribute(processName, attrName, name, gType, 275 | units, gSlope, metricName, dMaxInt); 276 | } 277 | } 278 | 279 | /** 280 | * Parses dMaxString, which is the value of dmax read in from a 281 | * configuration file. 282 | * 283 | * @param dMaxString 284 | * value read in from configuration 285 | * @return int value of dMaxString if parse is successful, 0 (default value) 286 | * otherwise 287 | */ 288 | private int parseDMax(String dMaxString) { 289 | int dMax; 290 | try { 291 | dMax = Integer.parseInt(dMaxString); 292 | } catch (NumberFormatException e) { 293 | dMax = 0; 294 | } 295 | return dMax; 296 | } 297 | 298 | /** 299 | * Builds the metric name in ganglia 300 | * 301 | * @param process 302 | * the process name, or null if not used 303 | * @param mbeanName 304 | * the mbean name 305 | * @param mbeanPublishName 306 | * the mbean publish name, or null if not used 307 | * @param attribute 308 | * the mbean attribute name 309 | * @param attrPublishName 310 | * the mbean attribute publish name 311 | * @return the metric name 312 | */ 313 | private String buildMetricName(String process, String mbeanName, 314 | String mbeanPublishName, String attribute, String attrPublishName) { 315 | StringBuilder buf = new StringBuilder(); 316 | if (process != null) { 317 | buf.append(process); 318 | buf.append("_"); 319 | } 320 | if (mbeanPublishName != null) { 321 | buf.append(mbeanPublishName); 322 | } else { 323 | buf.append(mbeanName); 324 | } 325 | buf.append("_"); 326 | if (!"".equals(attrPublishName)) { 327 | buf.append(attrPublishName); 328 | } else { 329 | buf.append(attribute); 330 | } 331 | return buf.toString(); 332 | } 333 | 334 | } -------------------------------------------------------------------------------- /src/main/java/info/ganglia/jmxetric/MBeanAttribute.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import info.ganglia.gmetric4j.Publisher; 4 | import info.ganglia.gmetric4j.gmetric.GMetricSlope; 5 | import info.ganglia.gmetric4j.gmetric.GMetricType; 6 | 7 | import java.lang.management.ManagementFactory; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | 11 | import javax.management.MBeanServer; 12 | import javax.management.ObjectName; 13 | import javax.management.openmbean.CompositeData; 14 | 15 | /** 16 | * Data structure used to sample one attribute 17 | */ 18 | class MBeanAttribute { 19 | private static Logger log = Logger.getLogger(JMXetricAgent.class.getName()); 20 | 21 | private String process; 22 | private String attributeName; 23 | private String key; 24 | private String canonicalName; 25 | private String units; 26 | private GMetricType type; 27 | private GMetricSlope slope; 28 | private String publishName; 29 | private int dmax; 30 | private MBeanServer mbs; 31 | private MBeanSampler sampler; 32 | 33 | public MBeanAttribute(MBeanSampler sampler, String process, 34 | String attributeName, String compositeKey, GMetricType type, 35 | String units, GMetricSlope slope, String publishName, int dmax) { 36 | this.sampler = sampler; 37 | this.process = process; 38 | this.key = compositeKey; 39 | this.canonicalName = attributeName + "." + compositeKey; 40 | this.attributeName = attributeName; 41 | this.units = units; 42 | this.type = type; 43 | this.slope = slope; 44 | this.publishName = publishName; 45 | this.dmax = dmax; 46 | } 47 | 48 | public MBeanAttribute(String process, String attributeName, 49 | String compositeKey, GMetricType type, String units, 50 | GMetricSlope slope, String publishName, int dmax) { 51 | this(null, process, attributeName, compositeKey, type, units, slope, 52 | publishName, dmax); 53 | } 54 | 55 | public MBeanAttribute(String process, String attributeName, 56 | GMetricType type, String units, GMetricSlope slope, 57 | String publishName, int dmax) { 58 | this(process, attributeName, null, type, units, slope, publishName, 59 | dmax); 60 | } 61 | 62 | public void publish(ObjectName objectName) { 63 | try { 64 | String value = null; 65 | if (mbs == null) { 66 | mbs = ManagementFactory.getPlatformMBeanServer(); 67 | } 68 | Object o = mbs.getAttribute(objectName, attributeName); 69 | if (o instanceof CompositeData) { 70 | CompositeData cd = (CompositeData) o; 71 | if (key != null) { 72 | Object val = cd.get(key); 73 | log.fine("Sampling " + objectName + " attribute " 74 | + canonicalName + ":" + val); 75 | value = val.toString(); 76 | } 77 | } else { 78 | if (null != o) { 79 | value = o.toString(); 80 | log.fine("Sampling " + objectName + " attribute " 81 | + canonicalName + ":" + o); 82 | } else { 83 | log.fine("Not sampling " + objectName + " attribute " 84 | + canonicalName + " as value is null"); 85 | } 86 | } 87 | if (null != value) { 88 | Publisher gm = sampler.getPublisher(); 89 | // log.finer("Announcing metric " + this.toString() + " value=" 90 | // + value ); 91 | gm.publish(process, publishName, value, getType(), getSlope(), 92 | sampler.getDelay(), getDMax(), getUnits()); 93 | } 94 | 95 | } catch (javax.management.InstanceNotFoundException ex) { 96 | log.warning("Exception when getting " + objectName + " " 97 | + canonicalName); 98 | } catch (Exception ex) { 99 | log.log(Level.WARNING, "Exception when getting " + objectName + " " 100 | + canonicalName, ex); 101 | } 102 | } 103 | 104 | public String getAttributeName() { 105 | return attributeName; 106 | } 107 | 108 | public String getCanonicalName() { 109 | return canonicalName; 110 | } 111 | 112 | public String getUnits() { 113 | return units; 114 | } 115 | 116 | public GMetricType getType() { 117 | return type; 118 | } 119 | 120 | public GMetricSlope getSlope() { 121 | return slope; 122 | } 123 | 124 | public String getKey() { 125 | return key; 126 | } 127 | 128 | public int getDMax() { 129 | return dmax; 130 | } 131 | 132 | public MBeanSampler getSampler() { 133 | return sampler; 134 | } 135 | 136 | public void setSampler(MBeanSampler mBeanSampler) { 137 | sampler = mBeanSampler; 138 | } 139 | 140 | @Override 141 | public String toString() { 142 | StringBuilder buf = new StringBuilder(); 143 | buf.append("attributeName=").append(attributeName); 144 | buf.append(" canonicalName=").append(canonicalName); 145 | buf.append(" units=").append(units); 146 | buf.append(" type=").append(type); 147 | buf.append(" slope=").append(slope); 148 | buf.append(" publishName=").append(publishName); 149 | buf.append(" dmax=").append(dmax); 150 | return buf.toString(); 151 | } 152 | 153 | @Override 154 | public boolean equals(Object obj) { 155 | if (obj == null) 156 | return false; 157 | if (obj == this) 158 | return true; 159 | if (this.getClass() != obj.getClass()) 160 | return false; 161 | MBeanAttribute attribute = (MBeanAttribute) obj; 162 | return canonicalName.equals(attribute.getCanonicalName()); 163 | } 164 | 165 | @Override 166 | public int hashCode() { 167 | int hash = 7; 168 | hash = 79 169 | * hash 170 | + (this.canonicalName != null ? this.canonicalName.hashCode() 171 | : 0); 172 | return hash; 173 | } 174 | } -------------------------------------------------------------------------------- /src/main/java/info/ganglia/jmxetric/MBeanHolder.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import info.ganglia.gmetric4j.gmetric.GMetricSlope; 4 | import info.ganglia.gmetric4j.gmetric.GMetricType; 5 | 6 | import java.util.HashSet; 7 | import java.util.Set; 8 | 9 | import javax.management.ObjectName; 10 | 11 | /** 12 | * Data structure to hold and query mbean 13 | */ 14 | class MBeanHolder { 15 | /** 16 | * 17 | */ 18 | private final MBeanSampler mBeanSampler; 19 | private String process; 20 | private ObjectName objectName; 21 | private Set attributes = new HashSet(); 22 | 23 | public MBeanHolder(MBeanSampler mBeanSampler, String process, String name) 24 | throws Exception { 25 | this.mBeanSampler = mBeanSampler; 26 | this.process = process; 27 | objectName = new ObjectName(name); 28 | } 29 | 30 | public void addAttribute(String attributeName, String compositeName, 31 | GMetricType type, GMetricSlope slope, String units, 32 | String publishName, int dmax) { 33 | attributes.add(new MBeanAttribute(mBeanSampler, process, attributeName, 34 | compositeName, type, units, slope, publishName, dmax)); 35 | } 36 | 37 | public void addAttribute(MBeanAttribute attr) { 38 | attributes.add(attr); 39 | } 40 | 41 | public void publish() { 42 | for (MBeanAttribute attr : attributes) { 43 | try { 44 | attr.publish(objectName); 45 | } catch (Exception ex) { 46 | ex.printStackTrace(); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/main/java/info/ganglia/jmxetric/MBeanSampler.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import info.ganglia.gmetric4j.GSampler; 4 | import info.ganglia.gmetric4j.gmetric.GMetricSlope; 5 | import info.ganglia.gmetric4j.gmetric.GMetricType; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.logging.Logger; 10 | 11 | /** 12 | * A class that samples MBeans and publishes attributes to Ganglia. This 13 | * classes' run method will be called periodically to sample the mbeans. 14 | */ 15 | public class MBeanSampler extends GSampler { 16 | 17 | private static Logger log = Logger.getLogger(JMXetricAgent.class.getName()); 18 | 19 | /* 20 | * The internal data structure is a hashmap of key=mbean name 21 | */ 22 | private Map mbeanMap = new HashMap(); 23 | 24 | /** 25 | * Creates an MBeanSampler 26 | * 27 | * @param delay 28 | * the sample interval in seconds 29 | * @param process 30 | * the process name that is appended to metrics 31 | */ 32 | public MBeanSampler(int initialDelay, int delay, String process) { 33 | super(initialDelay, delay, process); 34 | } 35 | 36 | /** 37 | * Adds a mbean name/attribute pair to be sampled 38 | * 39 | * @param mbean 40 | * the name of the mbean 41 | * @param attribute 42 | * the name of the attribute 43 | * @param composite 44 | * the name of the composite 45 | * @param publishName 46 | * the name to publish this attribute on 47 | * @param tmax 48 | * maximum time (in seconds) between gmetric calls 49 | * @param dmax 50 | * lifetime (in seconds) of this metric (use 0 for always alive) 51 | * @throws java.lang.Exception 52 | */ 53 | public void addMBeanAttribute(String mbean, String attribute, 54 | String composite, GMetricType type, String units, 55 | GMetricSlope slope, String publishName, int dmax) throws Exception { 56 | MBeanAttribute mba = new MBeanAttribute(this, process, attribute, 57 | composite, type, units, slope, publishName, dmax); 58 | addMBeanAttribute(mbean, mba); 59 | } 60 | 61 | /** 62 | * Adds a mbean name/attribute pair to be sampled 63 | * 64 | * @param mbean 65 | * the name of the mbean 66 | * @param attribute 67 | * the name of the attribute 68 | * @param composite 69 | * the name of the composite 70 | * @param publishName 71 | * the name to publish this attribute on 72 | * @throws java.lang.Exception 73 | */ 74 | public void addMBeanAttribute(String mbean, String attribute, 75 | String composite, GMetricType type, String units, 76 | GMetricSlope slope, String publishName) throws Exception { 77 | addMBeanAttribute(mbean, attribute, composite, type, units, slope, 78 | publishName, 0); 79 | } 80 | 81 | /** 82 | * Adds a mbean name/attribute pair to be sampled 83 | * 84 | * @param mbean 85 | * the name of the mbean 86 | * @param attribute 87 | * the name of the attribute 88 | * @param publishName 89 | * the name to publish this attribute on 90 | * @throws java.lang.Exception 91 | */ 92 | public void addMBeanAttribute(String mbean, String attribute, 93 | GMetricType type, String units, GMetricSlope slope, 94 | String publishName) throws Exception { 95 | addMBeanAttribute(mbean, attribute, null, type, units, slope, 96 | publishName); 97 | } 98 | 99 | /** 100 | * Adds an {@link info.ganglia.jmxetric.MBeanAttribute} to be sampled. 101 | * 102 | * @param mbean 103 | * name of the mbean 104 | * @param attr 105 | * attribute to be sample 106 | * @throws Exception 107 | */ 108 | public void addMBeanAttribute(String mbean, MBeanAttribute attr) 109 | throws Exception { 110 | MBeanHolder mbeanHolder = mbeanMap.get(mbean); 111 | if (mbeanHolder == null) { 112 | mbeanHolder = new MBeanHolder(this, process, mbean); 113 | mbeanMap.put(mbean, mbeanHolder); 114 | } 115 | mbeanHolder.addAttribute(attr); 116 | log.info("Added attribute " + attr + " to " + mbean); 117 | } 118 | 119 | /** 120 | * Called by the JMXAgent periodically to sample the mbeans 121 | */ 122 | public void run() { 123 | try { 124 | for (String mbean : mbeanMap.keySet()) { 125 | MBeanHolder h = mbeanMap.get(mbean); 126 | h.publish(); 127 | } 128 | } catch (Exception ex) { 129 | // Robust exception to prevent thread death 130 | log.warning("Exception thrown sampling Mbeans"); 131 | log.throwing(this.getClass().getName(), 132 | "Exception thrown sampling Mbeans:", ex); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/info/ganglia/jmxetric/MBeanScanner.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.io.PrintStream; 6 | import java.lang.management.ManagementFactory; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.Set; 11 | import java.util.Vector; 12 | 13 | import javax.management.AttributeNotFoundException; 14 | import javax.management.InstanceNotFoundException; 15 | import javax.management.IntrospectionException; 16 | import javax.management.MBeanAttributeInfo; 17 | import javax.management.MBeanException; 18 | import javax.management.MBeanInfo; 19 | import javax.management.MBeanServer; 20 | import javax.management.ObjectInstance; 21 | import javax.management.ObjectName; 22 | import javax.management.ReflectionException; 23 | import javax.management.RuntimeMBeanException; 24 | import javax.management.openmbean.CompositeData; 25 | import javax.management.openmbean.CompositeType; 26 | 27 | /** 28 | * A utility class that scans the platform MBeanServer for registered 29 | * MBeans/MXBeans. The MBeans are queried and represented as private objects 30 | * These objects are then written using ConfigWriter to a 31 | * {@link java.io.PrintStream}. 32 | * 33 | */ 34 | public class MBeanScanner { 35 | private static final String ERR_FILE = "%s can not be written to, using System.out instead.\n"; 36 | private final MBeanServer mBeanServer = ManagementFactory 37 | .getPlatformMBeanServer(); 38 | 39 | /** 40 | * Used mainly for testing purposed to output a test configuration file to 41 | * System.out. Also shows how to use this class. 42 | * 43 | * @param args 44 | */ 45 | public static void main(String[] args) { 46 | PrintStream out = System.out; 47 | if (args.length > 0) { 48 | try { 49 | out = new PrintStream(new File(args[0])); 50 | } catch (FileNotFoundException e) { 51 | System.out.printf(ERR_FILE, args[0]); 52 | } 53 | } 54 | MBeanScanner mBeanScanner = new MBeanScanner(); 55 | List configs = mBeanScanner.scan(); 56 | ConfigWriter cw; 57 | cw = new ConfigWriter(out, configs); 58 | cw.write(); 59 | } 60 | 61 | /** 62 | * Scans the platform MBean server for registered MBeans, creating 63 | * see Config objects to represent these MBeans. 64 | */ 65 | public List scan() { 66 | Set mBeanObjects = mBeanServer.queryMBeans(null, null); 67 | List configs = getConfigForAllMBeans(mBeanObjects); 68 | return configs; 69 | } 70 | 71 | /** 72 | * Constructs a configuration for all MBeans. 73 | * 74 | * @param mBeanObjects 75 | * @return a list of Config for the MBeans 76 | */ 77 | private List getConfigForAllMBeans(Set mBeanObjects) { 78 | List configs = new Vector(); 79 | for (ObjectInstance objectInstance : mBeanObjects) { 80 | Config configMB = scanOneMBeanObject(objectInstance); 81 | configs.add(configMB); 82 | } 83 | return configs; 84 | } 85 | 86 | /** 87 | * Constructs the configuration for a single MBean. The configuration 88 | * includes the name, e.g. "java.util.loggin:type=Logging", and the 89 | * attributes belonging to that MBean. 90 | * 91 | * @param objectInstance 92 | * MBean object instance 93 | * @return configuration representing this MBean 94 | */ 95 | private Config scanOneMBeanObject(ObjectInstance objectInstance) { 96 | MBeanConfig mBeanConfig = new MBeanConfig(); 97 | ObjectName objectName = objectInstance.getObjectName(); 98 | mBeanConfig.addField("name", objectName.getCanonicalName()); 99 | scanMBeanAttributes(mBeanConfig, objectName); 100 | return mBeanConfig; 101 | } 102 | 103 | /** 104 | * Stores all attributes of an MBean into its MBeanConfig object 105 | * 106 | * @param mBeanConfig 107 | * the configuration object to store the attributes into 108 | * @param mBeanName 109 | * the name of the MBean object we are getting the attributes 110 | * from 111 | */ 112 | private void scanMBeanAttributes(MBeanConfig mBeanConfig, 113 | ObjectName mBeanName) { 114 | MBeanInfo mBeanInfo; 115 | try { 116 | mBeanInfo = mBeanServer.getMBeanInfo(mBeanName); 117 | MBeanAttributeInfo[] infos = mBeanInfo.getAttributes(); 118 | for (int i = 0; i < infos.length; i++) { 119 | MBeanAttributeConfig cMBAttr = makeConfigMBeanAttribute( 120 | mBeanName, infos[i]); 121 | mBeanConfig.addChild(cMBAttr); 122 | } 123 | } catch (IntrospectionException e) { 124 | System.err.println(e.getMessage()); 125 | } catch (InstanceNotFoundException e) { 126 | System.err.println(e.getMessage()); 127 | } catch (ReflectionException e) { 128 | System.err.println(e.getMessage()); 129 | } 130 | } 131 | 132 | /** 133 | * Creates an object to represent a single attribute of an MBean. An 134 | * attribute can be a simple attribute, or made up composites. 135 | * 136 | * @param mBeanName 137 | * @param attributeInfo 138 | * @return an AttributeConfig for this MBean 139 | */ 140 | private MBeanAttributeConfig makeConfigMBeanAttribute(ObjectName mBeanName, 141 | MBeanAttributeInfo attributeInfo) { 142 | // type determines if this should be composite 143 | Object attr; 144 | try { 145 | attr = mBeanServer.getAttribute(mBeanName, 146 | attributeInfo.getName()); 147 | MBeanAttributeConfig config = new MBeanAttributeConfig(); 148 | config.addField("name", attributeInfo.getName()); 149 | 150 | if (attr == null) { 151 | return null; 152 | } else if (attr instanceof CompositeData) { 153 | addComposites(config, (CompositeData) attr); 154 | } else { 155 | config.addField("type", 156 | translateDataType(attributeInfo.getType())); 157 | } 158 | return config; 159 | } catch (RuntimeMBeanException e) { 160 | System.err.println(e.getMessage()); 161 | } catch (AttributeNotFoundException e) { 162 | System.err.println(e.getMessage()); 163 | } catch (InstanceNotFoundException e) { 164 | System.err.println(e.getMessage()); 165 | } catch (MBeanException e) { 166 | System.err.println(e.getMessage()); 167 | } catch (ReflectionException e) { 168 | System.err.println(e.getMessage()); 169 | } 170 | return null; 171 | } 172 | 173 | /** 174 | * Adds the composite data of an MBean's attribute to an 175 | * MBeanAttributeConfig 176 | * 177 | * @param config 178 | * configuration which the composite belongs to 179 | * @param compositeData 180 | * object representing the composite data 181 | */ 182 | private void addComposites(MBeanAttributeConfig config, 183 | CompositeData compositeData) { 184 | CompositeType compositeType = compositeData.getCompositeType(); 185 | for (String key : compositeType.keySet()) { 186 | config.addChild(makeComposite(compositeType, key)); 187 | } 188 | } 189 | 190 | /** 191 | * Makes a configuration for JMXetric that represents the composite tag 192 | * 193 | * @param compositeType 194 | * @param name 195 | * @return a CompositeConfig for a composite MBean 196 | */ 197 | private MBeanCompositeConfig makeComposite(CompositeType compositeType, 198 | String name) { 199 | MBeanCompositeConfig config = new MBeanCompositeConfig(); 200 | config.addField("name", name); 201 | String rawType = compositeType.getType(name).toString(); 202 | config.addField("type", translateDataType(rawType)); 203 | return config; 204 | } 205 | 206 | /** 207 | * The date types returned by JMX calls are no the same as those accepted by 208 | * JMXetric and Ganglia. This methods provides the translation. e.g. 209 | * java.lang.Long -> int8 210 | * 211 | * @param possibleData 212 | * the data type string returned by Java JMX methods 213 | * @return a data type string that Ganglia recognizes 214 | */ 215 | private String translateDataType(String possibleData) { 216 | if (possibleData.equals("string") | possibleData.equals("int8") 217 | | possibleData.equals("uint8") | possibleData.equals("int16") 218 | | possibleData.equals("unit16") | possibleData.equals("int32") 219 | | possibleData.equals("uint32") | possibleData.equals("float") 220 | | possibleData.equals("double")) { 221 | return possibleData; 222 | } 223 | if (possibleData.contains("java.lang.Long")) { 224 | return "int8"; 225 | } else if (possibleData.contains("java.lang.Integer")) { 226 | return "int32"; 227 | } else if (possibleData.contains("java.lang.Float")) { 228 | return "int32"; 229 | } 230 | return "string"; 231 | } 232 | 233 | /* Data types that represent the MBean configuration */ 234 | 235 | /** 236 | * Config is a super class that represents a type of configuration that is 237 | * fed into JMXetric. 238 | */ 239 | private class Config { 240 | /* name of this Config, this is the name of the XML tag */ 241 | String name; 242 | /* used to determine the XML tag will be self-closing */ 243 | boolean hasChildren = false; 244 | /* a map of the parameters in the tag, e.g. delay, name, pname */ 245 | Map fields = new HashMap(); 246 | /* a list of inner/children Config */ 247 | List children = new Vector(); 248 | 249 | /* Users are not supposed to instantiate this class */ 250 | private Config() { 251 | }; 252 | 253 | /** 254 | * Adds a new field to this Config. This is written as a parameter in 255 | * this XML tag. 256 | * 257 | * @param key 258 | * the name of this key, e.g. "delay", "name" 259 | * @param value 260 | * value of this key, e.g. "0", "60", "java.lang:type=Memory" 261 | */ 262 | void addField(String key, String value) { 263 | fields.put(key, value); 264 | } 265 | 266 | /** 267 | * Adds a Config to the list of children this Config has 268 | * 269 | * @param config 270 | * child Config to be added to this 271 | */ 272 | void addChild(Config config) { 273 | if (config == null) { 274 | return; // ensure that the children contains no null values 275 | } 276 | if (hasChildren == false) { 277 | hasChildren = true; 278 | } 279 | children.add(config); 280 | } 281 | 282 | /** 283 | * A String representation of this Config, includes the name and the 284 | * fields. 285 | */ 286 | @Override 287 | public String toString() { 288 | return name + " " + fieldsToString(); 289 | } 290 | 291 | /** 292 | * Printable representation of all the fields of this Config 293 | * 294 | * @return A String with all the fields in this format ="" 295 | */ 296 | public String fieldsToString() { 297 | String result = ""; 298 | for (String key : fields.keySet()) { 299 | result += key + "=\"" + fields.get(key) + "\" "; 300 | } 301 | // remove the trailing whitespace 302 | return result.substring(0, result.length() - 1); 303 | } 304 | } 305 | 306 | /** 307 | * Represents a configuration with the name "mbean". This is the "" 308 | * tag in the XML configuration file. 309 | */ 310 | private class MBeanConfig extends Config { 311 | public MBeanConfig() { 312 | this.name = "mbean"; 313 | } 314 | } 315 | 316 | /** 317 | * Represents a configuration with the name "attribute". This is the 318 | * "" tag in the XML configuration file. 319 | */ 320 | private class MBeanAttributeConfig extends Config { 321 | public MBeanAttributeConfig() { 322 | this.name = "attribute"; 323 | } 324 | } 325 | 326 | /** 327 | * Represents a configuration with the name "composite". This is the 328 | * "" tag in the XML configuration file. 329 | */ 330 | private class MBeanCompositeConfig extends Config { 331 | public MBeanCompositeConfig() { 332 | this.name = "composite"; 333 | } 334 | } 335 | 336 | /** 337 | * Writes the configuration to a {@link java.io.PrintStream}. The output is 338 | * in the XML format, which is what JMXetric reads in. 339 | */ 340 | private static class ConfigWriter { 341 | /* The output stream that the configuration will be written to. */ 342 | private final PrintStream out; 343 | /* The list of configurations to be written. */ 344 | private final List configs; 345 | /* System-specific new-line separator. */ 346 | private static final String NL = System.getProperty("line.separator"); 347 | /* XML Declaration used by JMXetric */ 348 | private static final String XML_DECL = ""; 349 | /* XML Doctype used by JMXetric */ 350 | private static final String XML_DOCTYPE = "" 352 | + NL + " " + NL 353 | + " " + NL 354 | + " " + NL 355 | + " " + NL 356 | + " " + NL 357 | + " " + NL 358 | + " " + NL 359 | + " " 360 | + " " + NL 361 | + " " + NL 362 | + " " + NL 363 | + " " + NL 364 | + " " + NL 365 | + " " + NL 366 | + " " + NL 367 | + " " + NL 368 | + " " + NL 369 | + " " + NL 370 | + " " + NL 371 | + " " + NL 372 | + " " + NL 373 | + " " + NL 374 | + " " + NL 375 | + " " + NL 376 | + " " + NL 377 | + " " + NL 378 | + " " + NL 379 | + " " + NL 380 | + " " + NL + "]>"; 381 | 382 | public ConfigWriter(PrintStream outputStream, List config) { 383 | this.out = outputStream; 384 | this.configs = config; 385 | } 386 | 387 | /** 388 | * Write the configuration out to the PrintStream out. 389 | */ 390 | public void write() { 391 | if (configs == null) 392 | return; 393 | StringBuilder sb = new StringBuilder(); 394 | sb.append(XML_DECL + NL); 395 | sb.append(XML_DOCTYPE + NL); 396 | sb.append("" + NL); 397 | sb.append(" " + NL); 398 | sb.append(" " + NL); 399 | String output = buildXmlTagsFromList(configs, " "); 400 | sb.append(output); 401 | sb.append(" " + NL); 402 | sb.append("" + NL); 403 | out.print(sb.toString()); 404 | } 405 | 406 | /** 407 | * Builds a String of XML tags out of a list of Config. Each Config is 408 | * built by {@link buildConfig} and the result is interspersed with new 409 | * lines for readability. 410 | * 411 | * @param configs 412 | * list of Config to be converted to XML tags 413 | * @param indent 414 | * string that is prefixed to each XML tag 415 | * @return string of XML tags for list 416 | */ 417 | public String buildXmlTagsFromList(List configs, String indent) { 418 | StringBuilder sb = new StringBuilder(); 419 | for (Config config : configs) { 420 | sb.append(buildXmlTag(config, indent) + NL); 421 | } 422 | return sb.toString(); 423 | } 424 | 425 | /** 426 | * Builds a XML tag for config. The tag with have indent prefixed. 427 | * 428 | * @param config 429 | * to be converted to an XML tag 430 | * @param indent 431 | * indentation string that is prefixed to the tag 432 | * @return XML tag for this config 433 | */ 434 | private String buildXmlTag(Config config, String indent) { 435 | StringBuffer sb = new StringBuffer(); 436 | sb.append(indent + "<" + config.name + " " 437 | + config.fieldsToString()); 438 | if (!config.hasChildren) { 439 | // self-closing XML tag 440 | sb.append("/>"); 441 | } else { 442 | sb.append(">" + NL); 443 | sb.append(buildXmlTagsFromList(config.children, " " + indent)); 444 | sb.append(indent + ""); 445 | } 446 | return sb.toString(); 447 | } 448 | } 449 | } 450 | -------------------------------------------------------------------------------- /src/main/java/info/ganglia/jmxetric/XMLConfigurationService.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import info.ganglia.gmetric4j.gmetric.GMetric; 4 | 5 | import java.io.IOException; 6 | 7 | import javax.xml.xpath.XPath; 8 | import javax.xml.xpath.XPathConstants; 9 | import javax.xml.xpath.XPathExpressionException; 10 | import javax.xml.xpath.XPathFactory; 11 | 12 | import org.w3c.dom.Node; 13 | import org.w3c.dom.NodeList; 14 | import org.xml.sax.InputSource; 15 | 16 | /** 17 | * Configures the JMXetricAgent based on the XML config file 18 | */ 19 | public class XMLConfigurationService { 20 | 21 | private final static XPath xpath = XPathFactory.newInstance().newXPath(); 22 | 23 | /** 24 | * Configures the JMXetricAgent based on the supplied agentArgs Command line 25 | * arguments overwrites XML arguments. Any arguments that is required but no 26 | * supplied will be defaulted. 27 | * 28 | * @param agent 29 | * the agent to configure 30 | * @param agentArgs 31 | * the agent arguments list 32 | * @throws java.lang.Exception 33 | */ 34 | public static void configure(JMXetricAgent agent, String agentArgs) 35 | throws Exception { 36 | CommandLineArgs args = new CommandLineArgs(agentArgs); 37 | InputSource inputSource = new InputSource(args.getConfig()); 38 | 39 | configureGanglia(agent, inputSource, args); 40 | 41 | configureJMXetric(agent, inputSource, args); 42 | } 43 | 44 | private static void configureGanglia(JMXetricAgent agent, 45 | InputSource inputSource, CommandLineArgs args) throws IOException, 46 | XPathExpressionException { 47 | GangliaXmlConfigurationService gangliaConfigService = new GangliaXmlConfigurationService( 48 | inputSource, args); 49 | GMetric gmetric = gangliaConfigService.getConfig(); 50 | agent.setGmetric(gmetric); 51 | } 52 | 53 | private static void configureJMXetric(JMXetricAgent agent, 54 | InputSource inputSource, CommandLineArgs args) throws Exception { 55 | JMXetricXmlConfigurationService jmxetricConfigService = new JMXetricXmlConfigurationService( 56 | agent, inputSource, args.getProcessName()); 57 | jmxetricConfigService.configure(); 58 | } 59 | 60 | String selectParameterFromNode(Node ganglia, String attributeName, 61 | String defaultValue) { 62 | if (ganglia == null) { 63 | return defaultValue; 64 | } 65 | Node node = ganglia.getAttributes().getNamedItem(attributeName); 66 | if (node == null) { 67 | return defaultValue; 68 | } 69 | String value = node.getNodeValue(); 70 | if (value == null) { 71 | return defaultValue; 72 | } 73 | return value; 74 | } 75 | 76 | protected static Node getXmlNode(String expr, InputSource inputSource) 77 | throws XPathExpressionException { 78 | return (Node) xpath.evaluate(expr, inputSource, XPathConstants.NODE); 79 | } 80 | 81 | NodeList getXmlNodeList(String expression, InputSource inputSource) 82 | throws XPathExpressionException { 83 | NodeList samples = (NodeList) xpath.evaluate(expression, inputSource, 84 | XPathConstants.NODESET); 85 | return samples; 86 | } 87 | 88 | NodeList getXmlNodeSet(String expression, Node node) 89 | throws XPathExpressionException { 90 | NodeList samples = (NodeList) xpath.evaluate(expression, node, 91 | XPathConstants.NODESET); 92 | return samples; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/info/ganglia/jmxetric/package.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Provides a JVM agent that polls MBeans periodically and publishes the results to Ganglia 8 | 9 |

Package Specification

10 | 11 |

Related Documentation

12 | 13 | For a description of Ganglia, please see: 14 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/MANIFEST.MF: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | Premain-Class: info.ganglia.jmxetric.JMXetricAgent 3 | Boot-Class-Path: remotetea-oncrpc.jar gmetric4j.jar 4 | Can-Redefine-Classes: false 5 | -------------------------------------------------------------------------------- /src/test/java/info/ganglia/jmxetric/CommandLineArgsTest.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import org.junit.Test; 6 | 7 | public class CommandLineArgsTest { 8 | private CommandLineArgs commandLineArgs; 9 | 10 | /** 11 | * IMPT: Take note of this special case. When the `config` parameter is 12 | * missing from the command line, we cannot return null. We need a default 13 | * location for the XML file to be able to read configuration from. 14 | */ 15 | private static final String DEFAULT_CONFIG = "jmxetric.xml"; 16 | 17 | @Test 18 | public void testCommandLineArgs_emptyOrNullArguments() { 19 | commandLineArgs = new CommandLineArgs(null); 20 | assertCommandLineArgs(null, null, DEFAULT_CONFIG, null, null, null, 21 | null); 22 | 23 | commandLineArgs = new CommandLineArgs(""); 24 | assertCommandLineArgs(null, null, DEFAULT_CONFIG, null, null, null, 25 | null); 26 | } 27 | 28 | @Test 29 | public void testCommandLineArgs_allParamsPresent() { 30 | String args = "host=localhost,port=8649,config=etc/jmxetric.xml,mode=multicast,wireformat31x=true,process=ProcessName,spoof=SpoofName"; 31 | commandLineArgs = new CommandLineArgs(args); 32 | assertCommandLineArgs("localhost", "8649", "etc/jmxetric.xml", 33 | "multicast", "true", "ProcessName", "SpoofName"); 34 | } 35 | 36 | @Test 37 | public void testCommandLineArgs_missingHost() { 38 | String args = "port=8649,config=etc/jmxetric.xml,mode=multicast,wireformat31x=true,process=ProcessName,spoof=SpoofName"; 39 | commandLineArgs = new CommandLineArgs(args); 40 | assertCommandLineArgs(null, "8649", "etc/jmxetric.xml", "multicast", 41 | "true", "ProcessName", "SpoofName"); 42 | } 43 | 44 | @Test 45 | public void testCommandLineArgs_missingPort() { 46 | String args = "host=localhost,config=etc/jmxetric.xml,mode=multicast,wireformat31x=true,process=ProcessName,spoof=SpoofName"; 47 | commandLineArgs = new CommandLineArgs(args); 48 | assertCommandLineArgs("localhost", null, "etc/jmxetric.xml", 49 | "multicast", "true", "ProcessName", "SpoofName"); 50 | } 51 | 52 | @Test 53 | public void testCommandLineArgs_missingConfig() { 54 | String args = "host=localhost,port=8649,mode=multicast,wireformat31x=true,process=ProcessName,spoof=SpoofName"; 55 | commandLineArgs = new CommandLineArgs(args); 56 | assertCommandLineArgs("localhost", "8649", DEFAULT_CONFIG, "multicast", 57 | "true", "ProcessName", "SpoofName"); 58 | } 59 | 60 | @Test 61 | public void testCommandLineArgs_missingMode() { 62 | String args = "host=localhost,port=8649,config=etc/jmxetric.xml,wireformat31x=true,process=ProcessName,spoof=SpoofName"; 63 | commandLineArgs = new CommandLineArgs(args); 64 | assertCommandLineArgs("localhost", "8649", "etc/jmxetric.xml", null, 65 | "true", "ProcessName", "SpoofName"); 66 | } 67 | 68 | @Test 69 | public void testCommandLineArgs_missingWireformat() { 70 | String args = "host=localhost,port=8649,config=etc/jmxetric.xml,mode=multicast,process=ProcessName,spoof=SpoofName"; 71 | commandLineArgs = new CommandLineArgs(args); 72 | assertCommandLineArgs("localhost", "8649", "etc/jmxetric.xml", 73 | "multicast", null, "ProcessName", "SpoofName"); 74 | } 75 | 76 | @Test 77 | public void testCommandLineArgs_missingProcessName() { 78 | String args = "host=localhost,port=8649,config=etc/jmxetric.xml,mode=multicast,wireformat31x=true,spoof=SpoofName"; 79 | commandLineArgs = new CommandLineArgs(args); 80 | assertCommandLineArgs("localhost", "8649", "etc/jmxetric.xml", 81 | "multicast", "true", null, "SpoofName"); 82 | } 83 | 84 | @Test 85 | public void testCommandLineArgs_missingSpoofName() { 86 | String args = "host=localhost,port=8649,config=etc/jmxetric.xml,mode=multicast,wireformat31x=true,process=ProcessName"; 87 | commandLineArgs = new CommandLineArgs(args); 88 | assertCommandLineArgs("localhost", "8649", "etc/jmxetric.xml", 89 | "multicast", "true", "ProcessName", null); 90 | } 91 | 92 | private void assertCommandLineArgs(String host, String port, String config, 93 | String mode, String wireformat, String processName, String spoof) { 94 | assertEquals(host, commandLineArgs.getHost()); 95 | assertEquals(port, commandLineArgs.getPort()); 96 | assertEquals(config, commandLineArgs.getConfig()); 97 | assertEquals(mode, commandLineArgs.getMode()); 98 | assertEquals(wireformat, commandLineArgs.getWireformat()); 99 | assertEquals(processName, commandLineArgs.getProcessName()); 100 | assertEquals(spoof, commandLineArgs.getSpoof()); 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /src/test/java/info/ganglia/jmxetric/Example.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import java.lang.management.ManagementFactory; 4 | import java.util.concurrent.Executors; 5 | import java.util.concurrent.ScheduledExecutorService; 6 | import java.util.concurrent.ThreadFactory; 7 | import java.util.concurrent.TimeUnit; 8 | import java.util.concurrent.atomic.AtomicInteger; 9 | 10 | import javax.management.MBeanServer; 11 | import javax.management.ObjectName; 12 | 13 | /** 14 | * MXBean implementation used for testing 15 | * @author humphrej 16 | * 17 | */ 18 | public class Example implements TestExampleMXBean { 19 | private AtomicInteger counter = new AtomicInteger(0); 20 | private ScheduledExecutorService executor = null ; 21 | 22 | public Example() { 23 | executor = Executors.newSingleThreadScheduledExecutor( new ThreadFactory() { 24 | public Thread newThread(Runnable r) { 25 | Thread t = new Thread( r ); 26 | t.setName("ExampleMBean"); 27 | return t ; 28 | } 29 | }); 30 | } 31 | 32 | public void start() { 33 | executor.scheduleAtFixedRate( new Runnable() { 34 | public void run() { 35 | counter.incrementAndGet(); 36 | } 37 | }, 0, 1, TimeUnit.SECONDS); 38 | } 39 | 40 | public void stop() { 41 | executor.shutdown(); 42 | } 43 | public double getDouble() { 44 | return DOUBLE_VALUE ; 45 | } 46 | public float getFloat() { 47 | return FLOAT_VALUE; 48 | } 49 | public int getInt() { 50 | return INT_VALUE; 51 | } 52 | public long getLong() { 53 | return LONG_VALUE; 54 | } 55 | public String getString() { 56 | return STRING_VALUE; 57 | } 58 | public int getCounter() { 59 | return counter.get(); 60 | } 61 | public ExampleComposite getComposite() { 62 | return new ExampleComposite(ExampleComposite.DATE_VALUE, 63 | ExampleComposite.INT_VALUE, 64 | ExampleComposite.STRING_VALUE); 65 | } 66 | public static void register() { 67 | try { 68 | MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 69 | ObjectName name = new ObjectName("jmxetric:type=TestExample"); 70 | Example mbean = new Example(); 71 | mbs.registerMBean(mbean, name); 72 | } catch (Exception ex ){ 73 | ex.printStackTrace(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/info/ganglia/jmxetric/ExampleComposite.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import java.beans.ConstructorProperties; 4 | import java.util.Date; 5 | 6 | /** 7 | * Class used to test composite attribute of an MXBean 8 | * @author humphrej 9 | * 10 | */ 11 | public class ExampleComposite { 12 | 13 | public static final Date DATE_VALUE=new Date(1000); 14 | public static final int INT_VALUE=29111974; 15 | public static final String STRING_VALUE="WIBBLE"; 16 | 17 | private final Date date; 18 | private final int integer; 19 | private final String name; 20 | 21 | @ConstructorProperties({"date", "integer", "name"}) 22 | public ExampleComposite(Date date, int integer, String name) { 23 | this.date = date; 24 | this.integer = integer; 25 | this.name = name; 26 | } 27 | 28 | public Date getDate() { 29 | return date; 30 | } 31 | 32 | public int getInteger() { 33 | return integer; 34 | } 35 | 36 | public String getName() { 37 | return name; 38 | } 39 | 40 | } 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/test/java/info/ganglia/jmxetric/GangliaXmlConfigurationServiceTest.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import javax.xml.xpath.XPathExpressionException; 6 | 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.xml.sax.InputSource; 10 | 11 | public class GangliaXmlConfigurationServiceTest { 12 | 13 | private final String ARGS1 = "host=localhost,port=8649,wireformat31x=true,config=etc/jmxetric.xml"; 14 | private final String ARGS1_EXPECTED = "GMetric host=localhost port=8649 mode=MULTICAST v31x=true spoof=null"; 15 | private final String ARGS2 = "host=info.ganglia.com,port=9999,wireformat31x=false,config=etc/config/jmxetric.xml"; 16 | private final String ARGS2_EXPECTED = "GMetric host=info.ganglia.com port=9999 mode=MULTICAST v31x=false spoof=null"; 17 | 18 | private final String BAD_PORT = "host=info.ganglia.com,port=abcd,wireformat31x=false,config=etc/config/jmxetric.xml"; 19 | 20 | private final String BAD_WIRE = "host=localhost,port=8649,wireformat31x=123,config=etc/jmxetric.xml"; 21 | private final String BAD_WIRE_EXPECTED = "GMetric host=localhost port=8649 mode=MULTICAST v31x=false spoof=null"; 22 | 23 | private InputSource inputSource; 24 | private CommandLineArgs args; 25 | private GangliaXmlConfigurationService g; 26 | 27 | @Before 28 | public void setUp() { 29 | inputSource = new InputSource("etc/jmxetric.xml"); 30 | } 31 | 32 | @Test 33 | public void testGetConfig_withNoCommandLineArguments() throws Exception { 34 | args = new CommandLineArgs(null); 35 | g = new GangliaXmlConfigurationService(inputSource, args); 36 | try { 37 | assertEquals(ARGS1_EXPECTED, g.getConfigString()); 38 | } catch (XPathExpressionException e) { 39 | e.printStackTrace(); 40 | } 41 | 42 | args = new CommandLineArgs(""); 43 | g = new GangliaXmlConfigurationService(inputSource, args); 44 | try { 45 | assertEquals(ARGS1_EXPECTED, g.getConfigString()); 46 | } catch (XPathExpressionException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | 51 | @Test 52 | public void testGetConfig_withValidArguments() { 53 | args = new CommandLineArgs(ARGS1); 54 | g = new GangliaXmlConfigurationService(inputSource, args); 55 | try { 56 | assertEquals(ARGS1_EXPECTED, g.getConfigString()); 57 | } catch (XPathExpressionException e) { 58 | e.printStackTrace(); 59 | } 60 | 61 | args = new CommandLineArgs(ARGS2); 62 | g = new GangliaXmlConfigurationService(inputSource, args); 63 | try { 64 | assertEquals(ARGS2_EXPECTED, g.getConfigString()); 65 | } catch (XPathExpressionException e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | 70 | @Test(expected=NumberFormatException.class) 71 | public void testGetConfig_withBadPort() throws Exception { 72 | args = new CommandLineArgs(BAD_PORT); 73 | g = new GangliaXmlConfigurationService(inputSource, args); 74 | try { 75 | assertEquals(ARGS2_EXPECTED, g.getConfigString()); 76 | } catch (XPathExpressionException e) { 77 | e.printStackTrace(); 78 | } 79 | } 80 | 81 | @Test 82 | public void testGetConfig_withBadWireFormat() throws Exception { 83 | args = new CommandLineArgs(BAD_WIRE); 84 | g = new GangliaXmlConfigurationService(inputSource, args); 85 | try { 86 | assertEquals(BAD_WIRE_EXPECTED, g.getConfigString()); 87 | } catch (XPathExpressionException e) { 88 | e.printStackTrace(); 89 | } 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/info/ganglia/jmxetric/JMXetricAgentIT.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | 6 | import info.ganglia.gmetric4j.gmetric.GMetricResult; 7 | import info.ganglia.jmxetric.JMXetricAgent; 8 | import info.ganglia.jmxetric.XMLConfigurationService; 9 | 10 | import java.lang.management.ManagementFactory; 11 | 12 | import javax.management.MBeanServer; 13 | import javax.management.ObjectName; 14 | 15 | import org.junit.Before; 16 | import org.junit.Test; 17 | /** 18 | * 19 | */ 20 | public class JMXetricAgentIT { 21 | 22 | @Before 23 | public void setUp() throws Exception { 24 | MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 25 | ObjectName name = new ObjectName("jmxetric:type=TestExample"); 26 | Example mbean = new Example(); 27 | mbs.registerMBean(mbean, name); 28 | } 29 | 30 | @Test 31 | public void testRun() { 32 | JMXetricAgent a = null ; 33 | try { 34 | a = new JMXetricAgent(); 35 | XMLConfigurationService.configure(a, "host=localhost,port=8649,wireformat31x=true,config=src/test/resources/jmxetric_test.xml"); 36 | a.start(); 37 | Thread.sleep(5000); 38 | GMetricResult.GMetricDetail floatResult = GMetricResult.getGMetric("ProcessName_TestExample_Float"); 39 | assertEquals( Float.toString( Example.FLOAT_VALUE) , floatResult.value); 40 | assertEquals( "both", floatResult.slope); 41 | // TODO Add asserts 42 | } catch ( Exception ex ) { 43 | ex.printStackTrace(); 44 | fail("Exception thrown"); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/info/ganglia/jmxetric/JMXetricXmlConfigurationServiceTest.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import static org.junit.Assert.*; 4 | import info.ganglia.gmetric4j.gmetric.GMetricType; 5 | 6 | import java.util.List; 7 | 8 | import javax.xml.xpath.XPath; 9 | import javax.xml.xpath.XPathConstants; 10 | import javax.xml.xpath.XPathExpressionException; 11 | import javax.xml.xpath.XPathFactory; 12 | 13 | import org.junit.Before; 14 | import org.junit.Test; 15 | import org.w3c.dom.Node; 16 | import org.w3c.dom.NodeList; 17 | import org.xml.sax.InputSource; 18 | 19 | public class JMXetricXmlConfigurationServiceTest { 20 | private JMXetricXmlConfigurationService jmxetric; 21 | private InputSource inputSource = new InputSource( 22 | "src/test/resources/jmxetric_test.xml"); 23 | private XPath xpath = XPathFactory.newInstance().newXPath(); 24 | @Before 25 | public void setUp() { 26 | jmxetric = new JMXetricXmlConfigurationService(null, inputSource, 27 | "TestProcessName"); 28 | } 29 | 30 | @Test 31 | public void testAll() throws XPathExpressionException { 32 | NodeList samples = (NodeList) xpath.evaluate("/jmxetric-config/sample", 33 | inputSource, XPathConstants.NODESET); 34 | Node firstSample = samples.item(0); 35 | Node secondSample = samples.item(1); 36 | 37 | NodeList firstMBeans = (NodeList) xpath.evaluate("mbean", firstSample, 38 | XPathConstants.NODESET); 39 | NodeList secondMBeans = (NodeList) xpath.evaluate("mbean", 40 | secondSample, XPathConstants.NODESET); 41 | 42 | Node mBeanWithCompositeAttributes = firstMBeans.item(0); 43 | Node mBeanWithSimpleAttributes = secondMBeans.item(1); 44 | Node mBeanWithBoth = secondMBeans.item(0); 45 | 46 | try { 47 | testMBeanWithSimpleAttributes(mBeanWithSimpleAttributes); 48 | testMBeanWithCompositeAttributes(mBeanWithCompositeAttributes); 49 | testMBeanWithBothKinds(mBeanWithBoth); 50 | } catch (Exception e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | 55 | private void testMBeanWithBothKinds(Node mBeanWithBoth) throws Exception { 56 | List attributes = jmxetric.getAttributesForMBean( 57 | mBeanWithBoth, "1"); 58 | 59 | assertEquals(9, attributes.size()); 60 | assertAttribute(attributes.get(0), "Int", "Int.null", 0, 61 | GMetricType.INT32, ""); 62 | assertAttribute(attributes.get(1), "Long", "Long.null", 0, 63 | GMetricType.INT32, ""); 64 | assertAttribute(attributes.get(6), "Composite", "Composite.date", 0, 65 | GMetricType.STRING, "bytes"); 66 | assertAttribute(attributes.get(7), "Composite", "Composite.integer", 4, 67 | GMetricType.INT32, "bytes"); 68 | assertAttribute(attributes.get(8), "Composite", "Composite.name", 0, 69 | GMetricType.INT32, "bytes"); 70 | 71 | } 72 | 73 | private void assertAttribute(MBeanAttribute mBeanAttribute, String name, 74 | String canonicalName, int dMax, GMetricType type, String units) { 75 | assertEquals(name, mBeanAttribute.getAttributeName()); 76 | assertEquals(canonicalName, mBeanAttribute.getCanonicalName()); 77 | assertEquals(dMax, mBeanAttribute.getDMax()); 78 | assertEquals(type, mBeanAttribute.getType()); 79 | assertEquals(units, mBeanAttribute.getUnits()); 80 | } 81 | 82 | private void testMBeanWithSimpleAttributes(Node mBeanWithSimpleAttributes) 83 | throws XPathExpressionException, Exception { 84 | List mbas = jmxetric.getAttributesForMBean( 85 | mBeanWithSimpleAttributes, ""); 86 | 87 | assertEquals(2, mbas.size()); 88 | assertAttribute(mbas.get(0), "ThreadCount", "ThreadCount.null", 0, 89 | GMetricType.INT16, ""); 90 | assertAttribute(mbas.get(1), "DaemonThreadCount", 91 | "DaemonThreadCount.null", 0, GMetricType.INT16, ""); 92 | } 93 | 94 | private void testMBeanWithCompositeAttributes( 95 | Node mBeanWithCompositeAttributes) throws XPathExpressionException, 96 | Exception { 97 | List mbas = jmxetric.getAttributesForMBean( 98 | mBeanWithCompositeAttributes, ""); 99 | assertEquals(8, mbas.size()); 100 | 101 | assertAttribute(mbas.get(0), "HeapMemoryUsage", "HeapMemoryUsage.init", 102 | 0, GMetricType.INT32, "bytes"); 103 | 104 | assertAttribute(mbas.get(0), "HeapMemoryUsage", "HeapMemoryUsage.init", 105 | 0, GMetricType.INT32, "bytes"); 106 | 107 | assertAttribute(mbas.get(1), "HeapMemoryUsage", 108 | "HeapMemoryUsage.committed", 0, GMetricType.INT32, "bytes"); 109 | 110 | assertAttribute(mbas.get(2), "HeapMemoryUsage", "HeapMemoryUsage.used", 111 | 0, GMetricType.INT32, "bytes"); 112 | 113 | assertAttribute(mbas.get(3), "HeapMemoryUsage", "HeapMemoryUsage.max", 114 | 0, GMetricType.INT32, "bytes"); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/test/java/info/ganglia/jmxetric/MBeanSamplerTest.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import info.ganglia.gmetric4j.Publisher; 6 | import info.ganglia.gmetric4j.gmetric.GMetricSlope; 7 | import info.ganglia.gmetric4j.gmetric.GMetricType; 8 | import info.ganglia.gmetric4j.gmetric.GangliaException; 9 | import info.ganglia.jmxetric.MBeanSampler; 10 | 11 | import java.lang.management.ManagementFactory; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | import javax.management.MBeanServer; 16 | import javax.management.ObjectName; 17 | 18 | import org.junit.After; 19 | import org.junit.Before; 20 | import org.junit.Test; 21 | 22 | /** 23 | * 24 | */ 25 | public class MBeanSamplerTest { 26 | 27 | public static String BEAN_NAME="jmxetric:type=Test.Example"; 28 | private class MyPublisher implements Publisher 29 | { 30 | Map results = new HashMap(); 31 | 32 | public void publish(String processName, String attributeName, 33 | String value, GMetricType type, GMetricSlope slope, String units) 34 | throws GangliaException { 35 | results.put(attributeName, value); 36 | } 37 | 38 | @Override 39 | public void publish(String processName, String attributeName, 40 | String value, GMetricType type, GMetricSlope slope, int delay, 41 | int lifetime, String units) throws GangliaException { 42 | results.put(attributeName, value); 43 | } 44 | 45 | public String getResult( String attributeName ) { 46 | return results.get(attributeName); 47 | } 48 | 49 | } 50 | @Before 51 | public void setUp() throws Exception { 52 | MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 53 | ObjectName name = new ObjectName(BEAN_NAME); 54 | Example mbean = new Example(); 55 | mbs.registerMBean(mbean, name); 56 | } 57 | @After 58 | public void tearDown() throws Exception { 59 | MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); 60 | ObjectName name = new ObjectName(BEAN_NAME); 61 | mbs.unregisterMBean(name); 62 | } 63 | /** 64 | * Test of attribute sample, type Long 65 | */ 66 | @Test 67 | public void sampleLong() throws Exception { 68 | MBeanSampler sampler = new MBeanSampler(0, 30000, "TEST") ; 69 | MyPublisher publisher = new MyPublisher() ; 70 | sampler.setPublisher(publisher); 71 | sampler.addMBeanAttribute(BEAN_NAME, "Long", GMetricType.INT32, 72 | "bytes", GMetricSlope.BOTH, "Longer"); 73 | sampler.run() ; 74 | String value = publisher.getResult("Longer"); 75 | assertEquals( Example.LONG_VALUE, Long.valueOf(value )); 76 | } 77 | /** 78 | * Test of attribute sample, type String 79 | */ 80 | @Test 81 | public void sampleComposite() throws Exception { 82 | MBeanSampler sampler = new MBeanSampler(0, 30000, "TEST") ; 83 | MyPublisher publisher = new MyPublisher() ; 84 | sampler.setPublisher(publisher); 85 | 86 | String compNamePublishName = "compName"; 87 | sampler.addMBeanAttribute(BEAN_NAME, 88 | "Composite", 89 | "name", 90 | GMetricType.STRING, 91 | "bytes", 92 | GMetricSlope.BOTH, 93 | compNamePublishName); 94 | 95 | String compIntPublishName = "compInt"; 96 | sampler.addMBeanAttribute(BEAN_NAME, 97 | "Composite", 98 | "integer", 99 | GMetricType.STRING, 100 | "bytes", 101 | GMetricSlope.BOTH, 102 | compIntPublishName); 103 | 104 | String compDatePublishName = "compDate"; 105 | sampler.addMBeanAttribute(BEAN_NAME, 106 | "Composite", 107 | "date", 108 | GMetricType.STRING, 109 | "bytes", 110 | GMetricSlope.BOTH, 111 | compDatePublishName); 112 | 113 | sampler.run(); 114 | 115 | assertEquals( ExampleComposite.STRING_VALUE, 116 | publisher.getResult(compNamePublishName) ); 117 | 118 | assertEquals( "" + ExampleComposite.INT_VALUE, 119 | publisher.getResult(compIntPublishName) ); 120 | 121 | assertEquals( "" + ExampleComposite.DATE_VALUE, 122 | publisher.getResult(compDatePublishName) ); 123 | } 124 | /** 125 | * Test of attribute sample, type int, slope positive 126 | */ 127 | @Test 128 | public void sampleCounter() throws Exception { 129 | MBeanSampler sampler = new MBeanSampler(0, 1, "TEST") ; 130 | MyPublisher publisher = new MyPublisher() ; 131 | sampler.setPublisher(publisher); 132 | sampler.addMBeanAttribute(BEAN_NAME, "Counter", GMetricType.INT32, 133 | "units", GMetricSlope.POSITIVE, "counter"); 134 | sampler.run() ; 135 | String value = publisher.getResult("counter"); 136 | assertTrue( Integer.valueOf(value) >= 0); 137 | } 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/test/java/info/ganglia/jmxetric/TestExampleMXBean.java: -------------------------------------------------------------------------------- 1 | package info.ganglia.jmxetric; 2 | 3 | /** 4 | * MXBean definition used for testing 5 | * @author humphrej 6 | * 7 | */ 8 | public interface TestExampleMXBean { 9 | 10 | final int INT_VALUE=1234 ; 11 | final long LONG_VALUE=43210L ; 12 | final double DOUBLE_VALUE=123.45D ; 13 | final float FLOAT_VALUE=543.21F ; 14 | final String STRING_VALUE="STRING" ; 15 | 16 | int getInt() ; 17 | long getLong() ; 18 | String getString() ; 19 | double getDouble() ; 20 | float getFloat() ; 21 | ExampleComposite getComposite(); 22 | int getCounter() ; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/test/resources/jmxetric_test.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | ]> 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | --------------------------------------------------------------------------------