├── .gitignore
├── .travis.yml
├── LICENSE.txt
├── README.md
├── lib
└── collectd-api.jar
├── pom.xml
└── src
└── main
└── java
└── com
└── e_gineering
└── collectd
├── Attribute.java
├── AttributePermutation.java
├── Connection.java
├── FastJMX.java
├── ReadCycleResult.java
├── SelfTuningCollectionExecutor.java
├── SynchronousConnectorAdapter.java
└── logging
└── CollectdLogHandler.java
/.gitignore:
--------------------------------------------------------------------------------
1 | #eclipse files
2 | .settings
3 | .classpath
4 | .project
5 |
6 | *.class
7 |
8 | # Mobile Tools for Java (J2ME)
9 | .mtj.tmp/
10 |
11 | # Package Files #
12 | *.jar
13 | *.war
14 | *.ear
15 |
16 | # Tools, IDEs, desktops
17 | *.iml
18 | .DS_Store
19 | .idea
20 | target
21 | nb*.xml
22 |
23 | # Include the stuff in /lib
24 | !lib/**
25 | dependency-reduced-pom.xml
26 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 |
3 | script: mvn verify
4 |
5 | jdk:
6 | - openjdk6
7 | - oraclejdk7
8 | - oraclejdk8
9 |
10 | cache:
11 | directories:
12 | - $HOME/.m2
13 |
14 | notifications:
15 | email:
16 | recipients:
17 | - eg.oss@e-gineering.com
18 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright 2014 E-Gineering, LLC
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Important Note : 1.0.0 has a new package...
2 |
3 | In the process of transferring this project to E-gineering, LLC (thanks, guys!) we've moved the maven coordinates a bit in order to start pushing
4 | artifacts to maven central.
5 |
6 | Note that all references to `org.collectd.FastJMX` have been changed to `com.e_gineering.collectd.FastJMX`, and the file name for jars has changed as
7 | well.
8 |
9 | On the bright side, you can now pull pre-built artifacts of FastJMX out of the interwebs, from the [Maven central repository](https://oss.sonatype.org/content/repositories/releases/com/e-gineering/collectd-fast-jmx/)!
10 |
11 | Or, for folks who want to use this some other way, here's the Maven coordinates:
12 | ```
13 |
14 | com.e-gineering
15 | collectd-fast-jmx
16 | 1.0.0
17 |
18 | ```
19 |
20 | ## FastJMX - Low-latency JMX collectd plugin [](https://travis-ci.org/egineering-llc/collectd-fast-jmx)
21 |
22 |
23 | The default GenericJMX plugin from collectd is great for basic collection of small numbers of metrics, but if you need to collect many metrics from one
24 | or more hosts, the latency to read the metrics can quickly exceed your interval time, and that's no fun. If you want to remotely collect metrics from
25 | multiple hosts you can forget about having short intervals, and some of the configuration settings aren't exactly obvious.
26 | Example: What do you mean I have to include the hostname? I gave you the serviceUrl!
27 |
28 | ### Introducing FastJMX!
29 |
30 | FastJMX does things differently than the GenericJMX plugin, but it does it in a manner that's configuration-compatible with the original plugin.
31 | (You read that right. There's just a few small tweaks to an existing configuration and FastJMX will take over)
32 |
33 | * FastJMX discovers all the matching beans when it first connects, then sets up listeners to the remote server so we get callbacks when any beans are added or removed from the server. This lets us identify all the permutations of the beans we need to read outside of the `read()` loop, which reduces `read()` latency, as well as internal GC stress and memory pressure.
34 | * Reconnections are attempted with increasing backoff sleep durations. Again, outside of the read loop, so that collecting metrics from connections which aren't failed continues to work.
35 | * Each attribute read from an mbean is it's own potential thread. The JDK 1.5 Concurrent packages are used to pool threads, inflict interval timeouts on the read cycle, and to make sure the queue is clear at the end of each `read()` invocation eliminating backlogged (lagged) metric reporting. If there isn't a metric polled in a timely manner, it's a dropped read.
36 | * Each `read()` cycle is timeslot protected (synchronized to the interval configured in collectd) so that old values and current values are *never* intermixed.
37 | * Each `` can define a custom `PluginName`, allowing segementation of reported metrics into different plugin buckets rather than everything being reported as "GenericJMX" or "FastJMX".
38 | * The port can be appended to the hostname using `IncludePortInHostname`. This is very helpful in separating data from multiple JVM instances on the same host without needing to specify an `InstancePrefix` on the ``.
39 | * Hostnames are automatically detected from the serviceURL. If the serviceURL is a complex type, like `service:jmx:rmi:///jndi/rmi://hostname:port/jmxrmi`, FastJMX will still properly parse the hostname and port. The `Hostname` property (part of the standard GenericJMX configuration) value is still respected if present.
40 | * FastJMX doesn't require connections be defined after the beans. `` (or ``, or just ``) and `` blocks can come in _any_ order.
41 |
42 | ### So how much faster is it?
43 |
44 | In real-world collection scenarios, large volume remote collections from multiple hosts over a VPN improved from ~2500ms to collect (with GenericJMX) to ~120ms.
45 |
46 | If you really want to know what FastJMX is doing, add `CollectInternal true` to the plugin configuration. This tells FastJMX to dispatch internal metrics (success, failure, error, latency, thread pool size) to collectd.
47 |
48 | ## Configuration
49 | ### Migrate from GenericJMX by...
50 |
51 | * Add the path to the fast-jmx jar in JVMARG
52 | * Include `LoadPlugin "com.e_gineering.collectd.FastJMX` in the `` block.
53 |
54 | ### Additional FastJMX Options:
55 |
56 | * Remove the `hostname` from the `` blocks. FastJMX will do it's best to detect it from the jmx URI if you don't include it. If parsing has an issue, you'll see a message in the log.
57 | * Asynch connection handling by default, but you can force synch by adding `Synchronous true` to a `` block. If the url contains `remoting-jmx` which is interpreted as [JBoss Remoting](https://github.com/jbossas/remoting-jmx) then the synchronous wrapper is auto-magic-ally enabled.
58 | * Single-attribute `` blocks can use the syntax ``. See the `` example below.
59 | * Include `PluginName` declarations in a `` block to change the plugin name it's reported as. Useful for grouping different MBeans as if they came from different applications, or subsystems.
60 | * Use `` or `` or ``.
61 | * `Composite` and `Table` can be used interchangeably within a `` block, and can be omitted (defaults to `false`).
62 | * `MaxThreads` can change the default maximum number of threads (512) to allow.
63 | * `CollectInternal` enables internal metrics FastJMX uses to be reported back to Collectd.
64 | * `TTL` can be used on a Connection to force a reconnect after `` many seconds have elapsed. This can be handy if your server isn't correctly maintining mbeans after redployments. Keep in mind this is seconds, so '43200' = 12 hours.
65 | * FastJMX can now traverse `TabularData` to pull out `CompositeData` values as tables, or track independent values.
66 |
67 | ```
68 | LoadPlugin java
69 |
70 | JVMARG "-Djava.class.path=/path/to/collectd-api.jar:/path/to/collectd-fast-jmx.jar"
71 |
72 | LoadPlugin "com.e_gineering.collectd.FastJMX"
73 |
74 |
75 |
76 | MaxThreads 256
77 | CollectInternal true
78 |
79 |
80 | ObjectName "java.lang:type=ClassLoading"
81 |
82 |
83 | Type "gauge"
84 | InstancePrefix "loaded_classes"
85 | PluginName "JVM"
86 |
87 |
88 |
89 | # Time spent by the JVM compiling or optimizing.
90 |
91 | ObjectName "java.lang:type=Compilation"
92 |
93 |
94 | Type "total_time_in_ms"
95 | InstancePrefix "compilation_time"
96 | PluginName "JVM"
97 |
98 |
99 |
100 | # Garbage collector information
101 |
102 | ObjectName "java.lang:type=GarbageCollector,*"
103 | InstancePrefix "gc-"
104 | InstanceFrom "name"
105 |
106 |
107 | Type "total_time_in_ms"
108 | InstancePrefix "collection_time"
109 | PluginName "JVM"
110 |
111 |
112 | # Reads the Par Eden Space data as a composite table
113 |
114 | Type "java_memory"
115 | Composite true
116 | InstancePrefix "pool-eden-after"
117 | PluginName "JVM"
118 |
119 |
120 | # Reads only the "used" portion of the Par Eden Space
121 |
122 | type "java_memory"
123 | InstancePrefix "pool-eden-after-used"
124 | PluginName "JVM"
125 |
126 |
127 |
128 | # Memory usage by memory pool.
129 |
130 | ObjectName "java.lang:type=MemoryPool,*"
131 | InstancePrefix "memory_pool-"
132 | InstanceFrom "name"
133 |
134 |
135 | Type "java_memory"
136 | Composite true
137 | PluginName "JVM"
138 |
139 |
140 |
141 |
142 |
143 | ServiceURL "service:jmx:rmi:///jndi/rmi://host1:8098/jmxrmi"
144 | IncludePortInHostname true
145 | Collect "classes"
146 | Collect "compilation"
147 | Collect "garbage_collector"
148 | Collect "memory_pool"
149 |
150 |
151 | ServiceURL "service:jmx:rmi:///jndi/rmi://host1:8198/jmxrmi"
152 | IncludePortInHostname true
153 | Collect "classes"
154 | Collect "compilation"
155 | Collect "garbage_collector"
156 | Collect "memory_pool"
157 |
158 |
159 | ServiceURL "service:jmx:rmi:///jndi/rmi://host2:8398/jmxrmi"
160 | IncludePortInHostname true
161 | Collect "classes"
162 | Collect "compilation"
163 | Collect "garbage_collector"
164 | Collect "memory_pool"
165 | # Force the connection to reset every 4 hours.
166 | TTL 14400
167 |
168 |
169 |
170 |
171 | ```
172 |
173 | ## Internal Metrics
174 | FastJMX collects some internal metrics that it uses to estimate an efficient pool size.
175 | If you enable internal metric collection (see above configuration options) and have the following types defined in types.db, the data will be submitted to collectd.
176 |
177 | ```
178 | fastjmx_cycle value:GAUGE:0:U
179 | fastjmx_latency value:GAUGE:0:U
180 | ```
181 |
182 | Once you've got collectd keeping your data, you may find these Collection3 graph configurations useful...
183 | ```
184 |
185 | Module GenericStacked
186 | DataSources value
187 | RRDTitle "FastJMX Reads ({plugin_instance})"
188 | RRDFormat "%6.1lf"
189 | DSName "cancelled Incomplete "
190 | DSName " success Success "
191 | DSName " failed Failed "
192 | DSName " weight Weight "
193 | Order success cancelled failed weight
194 | Color failed ff0000
195 | Color cancelled ffb000
196 | Color success 00e000
197 | Color weight 0000ff
198 | Stacking on
199 |
200 |
201 | Module GenericStacked
202 | DataSources value
203 | RRDTitle "FastJMX Latency ({plugin_instance})"
204 | RRDFormat "%6.1lf"
205 | DSName "interval Interval"
206 | DSName "duration Latency "
207 | Order interval duration
208 | Color duration ffb000
209 | Color interval 00e000
210 | Stacking off
211 |
212 | ```
213 |
214 | ## JBoss EAP 6.x, AS 7.x
215 | The JBoss remoting JMX provider has been tested with EAP 6.x, and should work properly with AS 7.x as well.
216 | As part of getting this to work, some 'workarounds' are included in the FastJMX code-base, which may also apply to other JMX protocol providers. In the case of the JBoss jmx remoting, appropriate bugs and feature requests have been filed.
217 |
218 | ### JBoss EAP 6 Classpath
219 | Here's an example JVMArg that works with jboss-eap-6.1
220 | ```
221 |
222 | JVMArg "-Djava.class.path=/usr/share/collectd/java/collectd-api.jar:/usr/lib/jvm/java-7-oracle/lib/jconsole.jar:/usr/lib/jvm/java-7-oracle/lib/tools.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/remoting-jmx/main/remoting-jmx-1.1.0.Final-redhat-1.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/remoting3/main/jboss-remoting-3.2.16.GA-redhat-1.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/logging/main/jboss-logging-3.1.2.GA-redhat-1.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/xnio/main/xnio-api-3.0.7.GA-redhat-1.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/xnio/nio/main/xnio-nio-3.0.7.GA-redhat-1.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/sasl/main/jboss-sasl-1.0.3.Final-redhat-1.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/marshalling/main/jboss-marshalling-1.3.18.GA-redhat-1.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/marshalling/river/main/jboss-marshalling-river-1.3.18.GA-redhat-1.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/as/cli/main/jboss-as-cli-7.2.1.Final-redhat-10.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/staxmapper/main/staxmapper-1.1.0.Final-redhat-2.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/as/protocol/main/jboss-as-protocol-7.2.1.Final-redhat-10.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/dmr/main/jboss-dmr-1.1.6.Final-redhat-1.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/as/controller-client/main/jboss-as-controller-client-7.2.1.Final-redhat-10.jar:/opt/appserver/jboss-eap-6.1/modules/system/layers/base/org/jboss/threads/main/jboss-threads-2.1.0.Final-redhat-1.jar:/usr/share/collectd/java/collectd-fast-jmx-1.0-SNAPSHOT.jar"
223 | LoadPlugin "com.e_gineering.collectd.FastJMX"
224 |
225 | ...
226 |
227 |
228 | ```
229 |
230 | To connect as an administrator you shoudln't need to change anything in the jboss configuration.
231 | The following Connection block should work in this scenario.
232 | ```
233 |
234 | ServiceURL "service:jmx:remoting-jmx://yourhostname:9999"
235 | Username "admin"
236 | Password "aR3allyStrongP@sswordThatOthersCanSee"
237 | ttl 300
238 | IncludePortInHostname false
239 | Collect "classes"
240 | ...
241 |
242 |
243 | ```
244 |
245 | To connect as a normal application user, and expose JMX over the 'remoting' port in EAP 6.1, your domain (or standalone) configuration should include `use-management-endpoint="false"`, like so:
246 | ```
247 |
248 |
249 |
250 |
251 |
252 | ```
253 |
254 | This changes the port from 9999 (the default management port) to 4447 (the remoting port) and requires an *application* user rather than an *administration* user.
255 |
256 | You can add the application user using the 'add-user' script from the JBoss Bin dir:
257 | ```
258 | $JBOSS_HOME/bin/add-user.sh --silent -a --user jmx --password
259 | ```
260 |
261 | Then in your Connection block, you can use:
262 | ```
263 |
264 | ServiceURL "service:jmx:remoting-jmx://yourhostname:4447"
265 | Username "jmx"
266 | Password "!amUnprivi1eged"
267 | ttl 300
268 | IncludePortInHostname false
269 | Collect "classes"
270 | ...
271 |
272 | ```
273 |
274 | Which exposes a non-privileged username / password.
275 |
276 | *WARNING WARNING WARNING* This Unprivileged user will be able to invoke MBeans via JMX. *WARNING WARNING WARNING*\
277 |
278 | ## Debugging & Troubleshooting
279 |
280 | There are a couple additional configuration options worth nothing, which are helpful if you're troubleshooting an issue.
281 |
282 | * `LogLevel` sets the Plugins internal Java log level. By default this is 'INFO'. Meaning any log message generated internall that's INFO or greater will be logged to Collectd at the approprate (corresponding) Collectd Log Level...
283 | * `ForceLoggingTo` Lets you override the normal behavior of mapping Java log levels to collectd log levels, and forces all java log output to be logged at this collectd level.
284 |
285 | So under normal operation, things logged in java as SEVERE are logged at ERROR in Collectd, etc.
286 |
287 | Setting `ForceLoggingTo "INFO"` will make all Java logging output log in Collectd at INFO.
288 |
289 | If your normal Collectd configuration sets the collectd log level to WARNING, but you want to get 'INFO' from the FastJMX plugin, you can do this:
290 |
291 | ```
292 |
293 | LogLevel "INFO"
294 | ForceLoggingTo "WARNING"
295 |
296 | ...
297 |
298 | ```
299 |
300 | If you'd like to see FINE logging from FastJMX use:
301 |
302 | ```
303 |
304 | LogLevel "FINE"
305 | ForceLoggingTo "WARNING"
306 |
307 | ```
308 |
309 | Basically, you're setting the java logger write any messages >= `FINE`, and to write those messages as Collectd `WARNING` messages.
310 | It gives a little more control over the verbosity of this single plugin.
311 |
312 |
--------------------------------------------------------------------------------
/lib/collectd-api.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/e-gineering/collectd-fast-jmx/2e2ffbcee3ff88c0b57449d36c9e3c5e91cf12c2/lib/collectd-api.jar
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 |
8 | 3.0.1
9 |
10 |
11 | com.e-gineering
12 | collectd-fast-jmx
13 | 1.0.0
14 |
15 | collectd FastJMX
16 | jar
17 | A collectd plugin for lower-latency JMX operations.
18 | http://github.com/egineering-llc/collectd-fast-jmx
19 |
20 |
21 | E-gineering, LLC.
22 | http://www.e-gineering.com
23 |
24 |
25 |
26 |
27 | Bryan Varner
28 | bryan.varner@e-gineering.com
29 |
30 |
31 |
32 |
33 | https://github.com/egineering-llc/collectd-fast-jmx/issues
34 | GitHub Issues
35 |
36 |
37 |
38 |
39 | Apache License, Version 2.0
40 | http://www.apache.org/licenses/LICENSE-2.0
41 | repo
42 |
43 |
44 |
45 |
46 | https://github.com/egineering-llc/collectd-fast-jmx
47 | scm:git:git://github.com/egineering-llc/collectd-fast-jmx.git
48 | scm:git:git@github.com:egineering-llc/collectd-fast-jmx.git
49 |
50 |
51 |
52 |
53 | 1.5
54 | 1.5
55 | UTF-8
56 |
57 |
58 |
59 |
60 | ossrh
61 | https://oss.sonatype.org/content/repositories/snapshots
62 |
63 |
64 | ossrh
65 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
66 |
67 |
68 |
69 |
70 |
71 | org.collectd
72 | collectd-api
73 | 1.0
74 | system
75 | ${basedir}/lib/collectd-api.jar
76 |
77 |
78 | org.apache.commons
79 | commons-math3
80 | 3.3
81 |
82 |
83 |
84 |
85 |
86 |
87 | org.apache.maven.plugins
88 | maven-javadoc-plugin
89 | 2.10.3
90 |
91 |
92 | attach-javadocs
93 |
94 | jar
95 |
96 |
97 |
98 |
99 |
100 | org.apache.maven.plugins
101 | maven-source-plugin
102 | 2.4
103 |
104 |
105 | attach-sources
106 |
107 | jar
108 |
109 |
110 |
111 |
112 |
113 | org.apache.maven.plugins
114 | maven-shade-plugin
115 | 2.3
116 |
117 |
118 |
119 | *:*
120 |
121 | org/collectd/api/**
122 |
123 |
124 |
125 |
126 |
127 |
128 | package
129 |
130 | shade
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 | eg.oss
141 |
142 | false
143 |
144 |
145 |
146 |
147 | org.apache.maven.plugins
148 | maven-gpg-plugin
149 | 1.5
150 |
151 |
152 | sign-artifacts
153 | verify
154 |
155 | sign
156 |
157 |
158 |
159 |
160 |
161 | org.sonatype.plugins
162 | nexus-staging-maven-plugin
163 | 1.6.3
164 | true
165 |
166 | ossrh
167 | https://oss.sonatype.org/
168 | false
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
--------------------------------------------------------------------------------
/src/main/java/com/e_gineering/collectd/Attribute.java:
--------------------------------------------------------------------------------
1 | package com.e_gineering.collectd;
2 |
3 | import org.collectd.api.DataSet;
4 |
5 | import javax.management.ObjectName;
6 | import java.util.ArrayList;
7 | import java.util.Arrays;
8 | import java.util.HashMap;
9 | import java.util.LinkedHashMap;
10 | import java.util.List;
11 |
12 | /**
13 | * Defines the parameters needed to build a set of AttributePermutations combined with a ConnectionDefinition.
14 | */
15 | public class Attribute {
16 | private String beanAlias;
17 | private String pluginName;
18 |
19 | private ObjectName findName;
20 | private String beanInstancePrefix;
21 | private List beanInstanceFrom = new ArrayList();
22 |
23 | private LinkedHashMap> attributes = new LinkedHashMap>();
24 | private DataSet dataset;
25 | private String valueInstancePrefix;
26 | private List valueInstanceFrom = new ArrayList();
27 | private boolean composite;
28 |
29 | public Attribute(final List attributes, final String pluginName, final DataSet dataset,
30 | final String valueInstancePrefix, final List valueInstanceFrom, final boolean composite,
31 | final String beanAlias, final ObjectName findName,
32 | final String beanInstancePrefix, final List beanInstanceFrom) {
33 | this.beanAlias = beanAlias;
34 | this.pluginName = pluginName;
35 | this.findName = findName;
36 | this.beanInstancePrefix = beanInstancePrefix;
37 | this.beanInstanceFrom = beanInstanceFrom;
38 | for (String attribute : attributes) {
39 | this.attributes.put(attribute, Arrays.asList(attribute.split("\\.")));
40 | }
41 | this.dataset = dataset;
42 | this.valueInstancePrefix = valueInstancePrefix;
43 | this.valueInstanceFrom = valueInstanceFrom;
44 | this.composite = composite;
45 | }
46 |
47 | public String getBeanAlias() {
48 | return beanAlias;
49 | }
50 |
51 | public ObjectName getObjectName() {
52 | return findName;
53 | }
54 |
55 | public String getPluginName() {
56 | return pluginName;
57 | }
58 |
59 | public List getBeanInstanceFrom() {
60 | return beanInstanceFrom;
61 | }
62 |
63 | public DataSet getDataSet() {
64 | return dataset;
65 | }
66 |
67 | public String getBeanInstancePrefix() {
68 | return beanInstancePrefix;
69 | }
70 |
71 | public List getValueInstanceFrom() {
72 | return valueInstanceFrom;
73 | }
74 |
75 | public String getValueInstancePrefix() {
76 | return valueInstancePrefix;
77 | }
78 |
79 | public HashMap> getAttributes() {
80 | return attributes;
81 | }
82 |
83 | public boolean isComposite() {
84 | return composite;
85 | }
86 |
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/com/e_gineering/collectd/AttributePermutation.java:
--------------------------------------------------------------------------------
1 | package com.e_gineering.collectd;
2 |
3 | import org.collectd.api.DataSource;
4 | import org.collectd.api.PluginData;
5 | import org.collectd.api.ValueList;
6 |
7 | import javax.management.AttributeNotFoundException;
8 | import javax.management.InstanceNotFoundException;
9 | import javax.management.MBeanServerConnection;
10 | import javax.management.ObjectName;
11 | import javax.management.openmbean.CompositeData;
12 | import javax.management.openmbean.OpenType;
13 | import javax.management.openmbean.TabularData;
14 | import java.io.IOException;
15 | import java.math.BigDecimal;
16 | import java.math.BigInteger;
17 | import java.util.ArrayList;
18 | import java.util.Collection;
19 | import java.util.List;
20 | import java.util.Map;
21 | import java.util.Set;
22 | import java.util.concurrent.Callable;
23 | import java.util.logging.Level;
24 | import java.util.logging.Logger;
25 |
26 | /**
27 | * Defines an actual permutation of an Attribute to be read from a Connection.
28 | */
29 | public class AttributePermutation implements Callable, Comparable {
30 | private static Logger logger = Logger.getLogger(AttributePermutation.class.getName());
31 |
32 | private ObjectName objectName;
33 | private Connection connection;
34 | private Attribute attribute;
35 | private PluginData pluginData;
36 | private ValueList valueList;
37 |
38 | private long lastRunDuration = 0l;
39 | private boolean interruptedOrFailed = false;
40 | private int consecutiveNotFounds = 0;
41 | private List dispatch = new ArrayList(1);
42 |
43 | private AttributePermutation(final ObjectName objectName, final Connection connection, final Attribute attribute, final PluginData pd, final ValueList vl) {
44 | this.objectName = objectName;
45 | this.connection = connection;
46 | this.attribute = attribute;
47 | this.pluginData = pd;
48 | this.valueList = vl;
49 | }
50 |
51 | public static List create(final ObjectName[] objectNames, final Connection connection, final Attribute context) {
52 | // This method takes into account the beanInstanceFrom and valueInstanceFrom properties to create many AttributePermutations.
53 | if (objectNames.length == 0) {
54 | logger.warning("No MBeans matched " + context.getObjectName() + " @ " + connection.getRawUrl());
55 | return new ArrayList(0);
56 | }
57 |
58 | List permutations = new ArrayList();
59 |
60 | PluginData pd = new PluginData();
61 | pd.setHost(connection.getHostname());
62 | if (context.getPluginName() != null) {
63 | pd.setPlugin(context.getPluginName());
64 | } else {
65 | pd.setPlugin("FastJMX");
66 | }
67 |
68 | for (ObjectName objName : objectNames) {
69 | // Issue #16, possibly issue #6 - Attempt to get the MBeanInfo prior to adding the permutation to be collected.
70 | // Based on Pull Request #17, but relocated to the 'initialization' code for a permutation.
71 | // If we're unable to obtain the MBeanInfo, the permutation is not added.
72 | try {
73 | connection.getServerConnection().getMBeanInfo(objName); // If this doesn't work, we won't add permutations to collect the bean info.
74 |
75 | PluginData permutationPD = new PluginData(pd);
76 | List beanInstanceList = new ArrayList();
77 | StringBuilder beanInstance = new StringBuilder();
78 |
79 | for (String propertyName : context.getBeanInstanceFrom()) {
80 | String propertyValue = objName.getKeyProperty(propertyName);
81 |
82 | if (propertyValue == null) {
83 | logger.severe("No such property [" + propertyName + "] in ObjectName [" + objName + "] for bean instance creation.");
84 | } else {
85 | beanInstanceList.add(propertyValue);
86 | }
87 | }
88 |
89 | if (connection.getConnectionInstancePrefix() != null) {
90 | beanInstance.append(connection.getConnectionInstancePrefix());
91 | }
92 |
93 | if (context.getBeanInstancePrefix() != null) {
94 | if (beanInstance.length() > 0) {
95 | beanInstance.append("-");
96 | }
97 | beanInstance.append(context.getBeanInstancePrefix());
98 | }
99 |
100 | for (int i = 0; i < beanInstanceList.size(); i++) {
101 | if (i > 0) {
102 | beanInstance.append("-");
103 | }
104 | beanInstance.append(beanInstanceList.get(i));
105 | }
106 | permutationPD.setPluginInstance(beanInstance.toString());
107 |
108 | ValueList vl = new ValueList(permutationPD);
109 | vl.setType(context.getDataSet().getType());
110 |
111 | List attributeInstanceList = new ArrayList();
112 | for (String propertyName : context.getValueInstanceFrom()) {
113 | String propertyValue = objName.getKeyProperty(propertyName);
114 | if (propertyValue == null) {
115 | logger.severe("no such property [" + propertyName + "] in ObjectName [" + objName + "] for attribute instance creation.");
116 | } else {
117 | attributeInstanceList.add(propertyValue);
118 | }
119 | }
120 |
121 | StringBuilder attributeInstance = new StringBuilder();
122 | if (context.getValueInstancePrefix() != null) {
123 | attributeInstance.append(context.getValueInstancePrefix());
124 | }
125 |
126 | for (int i = 0; i < attributeInstanceList.size(); i++) {
127 | if (i > 0) {
128 | attributeInstance.append("-");
129 | }
130 | attributeInstance.append(attributeInstanceList.get(i));
131 | }
132 | vl.setTypeInstance(attributeInstance.toString());
133 |
134 | permutations.add(new AttributePermutation(objName, connection, context, permutationPD, vl));
135 | } catch (Exception ex) {
136 | logger.warning("Unable to obtain MBeanInfo for " + objName + " @ " + connection.getRawUrl() );
137 | }
138 | }
139 |
140 | return permutations;
141 | }
142 |
143 | public Connection getConnection() {
144 | return connection;
145 | }
146 |
147 | public ObjectName getObjectName() {
148 | return objectName;
149 | }
150 |
151 | public List getValues() {
152 | return dispatch;
153 | }
154 |
155 | /**
156 | * Implements Comparable, allowing for a natural sort ordering of previous successful execution duration.
157 | *
158 | * Executions previously cancelled or failed will be treated as 'not run', and have a duration of '0', making them
159 | * 'less than' by comparison. If both objects being compared have a run duration of 0, they are sorted according to
160 | * the computed hashCode() values.
161 | *
162 | * @param o The other AttributePermutation to compare.
163 | * @return Lexical comparison result.
164 | */
165 | public int compareTo(final AttributePermutation o) {
166 |
167 | int i = -1 * Long.valueOf(getLastRunDuration()).compareTo(Long.valueOf(o.getLastRunDuration()));
168 | if (i != 0) {
169 | return i;
170 | }
171 |
172 | return Integer.valueOf(hashCode()).compareTo(Integer.valueOf(o.hashCode()));
173 | }
174 |
175 | @Override
176 | public boolean equals(Object obj) {
177 | if (this == obj) {
178 | return true;
179 | } else if (obj instanceof AttributePermutation) {
180 | return hashCode() == obj.hashCode();
181 | } else {
182 | return false;
183 | }
184 | }
185 |
186 | @Override
187 | public int hashCode() {
188 | return (connection.getHostname() + connection.getRawUrl() + objectName.toString() + pluginData.getSource() + valueList.getType()).hashCode();
189 | }
190 |
191 | /**
192 | * Reads the attribute from the JMX Connection and submits it back to Collectd.
193 | */
194 | public AttributePermutation call() throws Exception {
195 | long start = System.nanoTime();
196 | dispatch.clear();
197 | // Snapshot the value list for this 'call', Value lists are built at the end of the call(),
198 | // and if another thread call()s while this one is still running, it could trounce the interval
199 | // and report back duplicates to collectd.
200 | ValueList callVal = new ValueList(this.valueList);
201 | interruptedOrFailed = true;
202 | try {
203 | MBeanServerConnection mbs = connection.getServerConnection();
204 |
205 | List