0fcf31aad02c0ead7a9cad9a167580e132338572
1 /*
2 * collectd/java - org/collectd/java/GenericJMXConfValue.java
3 * Copyright (C) 2009 Florian octo Forster
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; only version 2 of the License is applicable.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Authors:
19 * Florian octo Forster <octo at verplant.org>
20 */
22 package org.collectd.java;
24 import java.util.Arrays;
25 import java.util.List;
26 import java.util.Set;
27 import java.util.Iterator;
28 import java.util.ArrayList;
30 import javax.management.MBeanServerConnection;
31 import javax.management.ObjectName;
32 import javax.management.openmbean.CompositeData;
33 import javax.management.openmbean.InvalidKeyException;
35 import org.collectd.api.Collectd;
36 import org.collectd.api.DataSet;
37 import org.collectd.api.DataSource;
38 import org.collectd.api.ValueList;
39 import org.collectd.api.PluginData;
40 import org.collectd.api.OConfigValue;
41 import org.collectd.api.OConfigItem;
43 /**
44 * Representation of a <value /> block and query functionality.
45 *
46 * This class represents a <value /> block in the configuration. As
47 * such, the constructor takes an {@link org.collectd.api.OConfigValue} to
48 * construct an object of this class.
49 *
50 * The object can then be asked to query data from JMX and dispatch it to
51 * collectd.
52 *
53 * @see GenericJMXConfMBean
54 */
55 class GenericJMXConfValue
56 {
57 private String ds_name;
58 private DataSet _ds;
59 private List<String> _attributes;
60 private String _instance_prefix;
61 private boolean _is_table;
63 private Number genericObjectToNumber (Object obj, int ds_type) /* {{{ */
64 {
65 if (obj instanceof String)
66 {
67 String str = (String) obj;
69 try
70 {
71 if (ds_type == DataSource.TYPE_GAUGE)
72 return (new Double (str));
73 else
74 return (new Long (str));
75 }
76 catch (NumberFormatException e)
77 {
78 return (null);
79 }
80 }
81 else if (obj instanceof Integer)
82 {
83 return (new Integer ((Integer) obj));
84 }
85 else if (obj instanceof Long)
86 {
87 return (new Long ((Long) obj));
88 }
89 else if (obj instanceof Double)
90 {
91 return (new Double ((Double) obj));
92 }
94 return (null);
95 } /* }}} Number genericObjectToNumber */
97 private List<Number> genericListToNumber (List<Object> objects) /* {{{ */
98 {
99 List<Number> ret = new ArrayList<Number> ();
100 List<DataSource> dsrc = this._ds.getDataSources ();
102 assert (objects.size () == dsrc.size ());
104 for (int i = 0; i < objects.size (); i++)
105 {
106 Number n;
108 n = genericObjectToNumber (objects.get (i), dsrc.get (i).getType ());
109 if (n == null)
110 return (null);
111 ret.add (n);
112 }
114 return (ret);
115 } /* }}} List<Number> genericListToNumber */
117 private List<Number> genericCompositeToNumber (List<CompositeData> cdlist, /* {{{ */
118 String key)
119 {
120 List<Object> objects = new ArrayList<Object> ();
122 for (int i = 0; i < cdlist.size (); i++)
123 {
124 CompositeData cd;
125 Object value;
127 cd = cdlist.get (i);
128 try
129 {
130 value = cd.get (key);
131 }
132 catch (InvalidKeyException e)
133 {
134 return (null);
135 }
136 objects.add (value);
137 }
139 return (genericListToNumber (objects));
140 } /* }}} List<Number> genericCompositeToNumber */
142 private void submitTable (List<Object> objects, ValueList vl) /* {{{ */
143 {
144 List<CompositeData> cdlist;
145 Set<String> keySet = null;
146 Iterator<String> keyIter;
148 cdlist = new ArrayList<CompositeData> ();
149 for (int i = 0; i < objects.size (); i++)
150 {
151 Object obj;
153 obj = objects.get (i);
154 if (obj instanceof CompositeData)
155 {
156 CompositeData cd;
158 cd = (CompositeData) obj;
160 if (i == 0)
161 keySet = cd.getCompositeType ().keySet ();
163 cdlist.add (cd);
164 }
165 else
166 {
167 Collectd.logError ("GenericJMXConfValue: At least one of the "
168 + "attributes was not of type `CompositeData', as required "
169 + "when table is set to `true'.");
170 return;
171 }
172 }
174 assert (keySet != null);
176 keyIter = keySet.iterator ();
177 while (keyIter.hasNext ())
178 {
179 String key;
180 List<Number> values;
182 key = keyIter.next ();
183 values = genericCompositeToNumber (cdlist, key);
184 if (values == null)
185 {
186 Collectd.logError ("GenericJMXConfValue: Cannot build a list of "
187 + "numbers for key " + key + ". Most likely not all attributes "
188 + "have this key.");
189 continue;
190 }
192 if (this._instance_prefix == null)
193 vl.setTypeInstance (key);
194 else
195 vl.setTypeInstance (this._instance_prefix + key);
196 vl.setValues (values);
198 Collectd.dispatchValues (vl);
199 }
200 } /* }}} void submitTable */
202 private void submitScalar (List<Object> objects, ValueList vl) /* {{{ */
203 {
204 List<Number> values;
206 values = genericListToNumber (objects);
207 if (values == null)
208 {
209 Collectd.logError ("GenericJMXConfValue: Cannot convert list of "
210 + "objects to numbers.");
211 return;
212 }
214 if (this._instance_prefix == null)
215 vl.setTypeInstance ("");
216 else
217 vl.setTypeInstance (this._instance_prefix);
218 vl.setValues (values);
220 Collectd.dispatchValues (vl);
221 } /* }}} void submitScalar */
223 private Object queryAttributeRecursive (CompositeData parent, /* {{{ */
224 List<String> attrName)
225 {
226 String key;
227 Object value;
229 key = attrName.remove (0);
231 try
232 {
233 value = parent.get (key);
234 }
235 catch (InvalidKeyException e)
236 {
237 return (null);
238 }
240 if (attrName.size () == 0)
241 {
242 return (value);
243 }
244 else
245 {
246 if (value instanceof CompositeData)
247 return (queryAttributeRecursive ((CompositeData) value, attrName));
248 else
249 return (null);
250 }
251 } /* }}} queryAttributeRecursive */
253 private Object queryAttribute (MBeanServerConnection conn, /* {{{ */
254 ObjectName objName, String attrName)
255 {
256 List<String> attrNameList;
257 String key;
258 Object value;
259 String[] attrNameArray;
261 attrNameList = new ArrayList<String> ();
263 attrNameArray = attrName.split ("\\.");
264 key = attrNameArray[0];
265 for (int i = 1; i < attrNameArray.length; i++)
266 attrNameList.add (attrNameArray[i]);
268 try
269 {
270 value = conn.getAttribute (objName, key);
271 }
272 catch (Exception e)
273 {
274 Collectd.logError ("GenericJMXConfValue.query: getAttribute failed: "
275 + e);
276 return (null);
277 }
279 if (attrNameList.size () == 0)
280 {
281 return (value);
282 }
283 else
284 {
285 if (value instanceof CompositeData)
286 return (queryAttributeRecursive ((CompositeData) value, attrNameList));
287 else
288 return (null);
289 }
290 } /* }}} Object queryAttribute */
292 private String getConfigString (OConfigItem ci) /* {{{ */
293 {
294 List<OConfigValue> values;
295 OConfigValue v;
297 values = ci.getValues ();
298 if (values.size () != 1)
299 {
300 Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
301 + " configuration option needs exactly one string argument.");
302 return (null);
303 }
305 v = values.get (0);
306 if (v.getType () != OConfigValue.OCONFIG_TYPE_STRING)
307 {
308 Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
309 + " configuration option needs exactly one string argument.");
310 return (null);
311 }
313 return (v.getString ());
314 } /* }}} String getConfigString */
316 private Boolean getConfigBoolean (OConfigItem ci) /* {{{ */
317 {
318 List<OConfigValue> values;
319 OConfigValue v;
320 Boolean b;
322 values = ci.getValues ();
323 if (values.size () != 1)
324 {
325 Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
326 + " configuration option needs exactly one boolean argument.");
327 return (null);
328 }
330 v = values.get (0);
331 if (v.getType () != OConfigValue.OCONFIG_TYPE_BOOLEAN)
332 {
333 Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
334 + " configuration option needs exactly one boolean argument.");
335 return (null);
336 }
338 return (new Boolean (v.getBoolean ()));
339 } /* }}} String getConfigBoolean */
341 /**
342 * Constructs a new value with the configured properties.
343 */
344 public GenericJMXConfValue (OConfigItem ci) /* {{{ */
345 throws IllegalArgumentException
346 {
347 List<OConfigItem> children;
348 Iterator<OConfigItem> iter;
350 this.ds_name = null;
351 this._ds = null;
352 this._attributes = new ArrayList<String> ();
353 this._instance_prefix = null;
354 this._is_table = false;
356 /*
357 * <Value>
358 * Type "memory"
359 * Table true|false
360 * Attribute "HeapMemoryUsage"
361 * Attribute "..."
362 * :
363 * # Type instance:
364 * InstancePrefix "heap-"
365 * </Value>
366 */
367 children = ci.getChildren ();
368 iter = children.iterator ();
369 while (iter.hasNext ())
370 {
371 OConfigItem child = iter.next ();
373 if (child.getKey ().equalsIgnoreCase ("Type"))
374 {
375 String tmp = getConfigString (child);
376 if (tmp != null)
377 this.ds_name = tmp;
378 }
379 else if (child.getKey ().equalsIgnoreCase ("Table"))
380 {
381 Boolean tmp = getConfigBoolean (child);
382 if (tmp != null)
383 this._is_table = tmp.booleanValue ();
384 }
385 else if (child.getKey ().equalsIgnoreCase ("Attribute"))
386 {
387 String tmp = getConfigString (child);
388 if (tmp != null)
389 this._attributes.add (tmp);
390 }
391 else if (child.getKey ().equalsIgnoreCase ("InstancePrefix"))
392 {
393 String tmp = getConfigString (child);
394 if (tmp != null)
395 this._instance_prefix = tmp;
396 }
397 else
398 throw (new IllegalArgumentException ("Unknown option: "
399 + child.getKey ()));
400 }
402 if (this.ds_name == null)
403 throw (new IllegalArgumentException ("No data set was defined."));
404 else if (this._attributes.size () == 0)
405 throw (new IllegalArgumentException ("No attribute was defined."));
406 } /* }}} GenericJMXConfValue (OConfigItem ci) */
408 /**
409 * Query values via JMX according to the object's configuration and dispatch
410 * them to collectd.
411 *
412 * @param conn Connection to the MBeanServer.
413 * @param objName Object name of the MBean to query.
414 * @param pd Preset naming components. The members host, plugin and
415 * plugin instance will be used.
416 */
417 public void query (MBeanServerConnection conn, ObjectName objName, /* {{{ */
418 PluginData pd)
419 {
420 ValueList vl;
421 List<DataSource> dsrc;
422 List<Object> values;
424 if (this._ds == null)
425 {
426 this._ds = Collectd.getDS (this.ds_name);
427 if (this._ds == null)
428 {
429 Collectd.logError ("GenericJMXConfValue: Unknown type: "
430 + this.ds_name);
431 return;
432 }
433 }
435 dsrc = this._ds.getDataSources ();
436 if (dsrc.size () != this._attributes.size ())
437 {
438 Collectd.logError ("GenericJMXConfValue.query: The data set "
439 + ds_name + " has " + this._ds.getDataSources ().size ()
440 + " data sources, but there were " + this._attributes.size ()
441 + " attributes configured. This doesn't match!");
442 this._ds = null;
443 return;
444 }
446 vl = new ValueList (pd);
447 vl.setType (this.ds_name);
448 vl.setTypeInstance (this._instance_prefix);
450 values = new ArrayList<Object> ();
452 assert (dsrc.size () == this._attributes.size ());
453 for (int i = 0; i < this._attributes.size (); i++)
454 {
455 Object v;
457 v = queryAttribute (conn, objName, this._attributes.get (i));
458 if (v == null)
459 {
460 Collectd.logError ("GenericJMXConfValue.query: "
461 + "Querying attribute " + this._attributes.get (i) + " failed.");
462 return;
463 }
465 values.add (v);
466 }
468 if (this._is_table)
469 submitTable (values, vl);
470 else
471 submitScalar (values, vl);
472 } /* }}} void query */
473 }
475 /* vim: set sw=2 sts=2 et fdm=marker : */