2 * collectd - bindings/java/org/collectd/java/GenericJMXConfValue.java
3 * Copyright (C) 2009 Florian octo Forster
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
24 * Florian octo Forster <octo at collectd.org>
27 package org.collectd.java;
29 import java.util.Arrays;
30 import java.util.List;
31 import java.util.Collection;
33 import java.util.concurrent.atomic.AtomicInteger;
34 import java.util.concurrent.atomic.AtomicLong;
35 import java.util.Iterator;
36 import java.util.ArrayList;
38 import java.math.BigDecimal;
39 import java.math.BigInteger;
41 import javax.management.MBeanServerConnection;
42 import javax.management.ObjectName;
43 import javax.management.openmbean.OpenType;
44 import javax.management.openmbean.CompositeData;
45 import javax.management.openmbean.TabularData;
46 import javax.management.openmbean.InvalidKeyException;
48 import org.collectd.api.Collectd;
49 import org.collectd.api.DataSet;
50 import org.collectd.api.DataSource;
51 import org.collectd.api.ValueList;
52 import org.collectd.api.PluginData;
53 import org.collectd.api.OConfigValue;
54 import org.collectd.api.OConfigItem;
57 * Representation of a <value /> block and query functionality.
59 * This class represents a <value /> block in the configuration. As
60 * such, the constructor takes an {@link org.collectd.api.OConfigValue} to
61 * construct an object of this class.
63 * The object can then be asked to query data from JMX and dispatch it to
66 * @see GenericJMXConfMBean
68 class GenericJMXConfValue
70 private String _ds_name;
72 private List<String> _attributes;
73 private String _instance_prefix;
74 private List<String> _instance_from;
75 private String _plugin_name;
76 private boolean _is_table;
79 * Converts a generic (OpenType) object to a number.
81 * Returns null if a conversion is not possible or not implemented.
83 private Number genericObjectToNumber (Object obj, int ds_type) /* {{{ */
85 if (obj instanceof String)
87 String str = (String) obj;
91 if (ds_type == DataSource.TYPE_GAUGE)
92 return (new Double (str));
94 return (new Long (str));
96 catch (NumberFormatException e)
101 else if (obj instanceof Byte)
103 return (new Byte ((Byte) obj));
105 else if (obj instanceof Short)
107 return (new Short ((Short) obj));
109 else if (obj instanceof Integer)
111 return (new Integer ((Integer) obj));
113 else if (obj instanceof Long)
115 return (new Long ((Long) obj));
117 else if (obj instanceof Float)
119 return (new Float ((Float) obj));
121 else if (obj instanceof Double)
123 return (new Double ((Double) obj));
125 else if (obj instanceof BigDecimal)
127 return (BigDecimal.ZERO.add ((BigDecimal) obj));
129 else if (obj instanceof BigInteger)
131 return (BigInteger.ZERO.add ((BigInteger) obj));
133 else if (obj instanceof AtomicInteger)
135 return (new Integer(((AtomicInteger) obj).get()));
137 else if (obj instanceof AtomicLong)
139 return (new Long(((AtomicLong) obj).get()));
143 } /* }}} Number genericObjectToNumber */
146 * Converts a generic list to a list of numbers.
148 * Returns null if one or more objects could not be converted.
150 private List<Number> genericListToNumber (List<Object> objects) /* {{{ */
152 List<Number> ret = new ArrayList<Number> ();
153 List<DataSource> dsrc = this._ds.getDataSources ();
155 assert (objects.size () == dsrc.size ());
157 for (int i = 0; i < objects.size (); i++)
161 n = genericObjectToNumber (objects.get (i), dsrc.get (i).getType ());
168 } /* }}} List<Number> genericListToNumber */
171 * Converts a list of CompositeData to a list of numbers.
173 * From each <em>CompositeData </em> the key <em>key</em> is received and all
174 * those values are converted to a number. If one of the
175 * <em>CompositeData</em> doesn't have the specified key or one returned
176 * object cannot converted to a number then the function will return null.
178 private List<Number> genericCompositeToNumber (List<CompositeData> cdlist, /* {{{ */
181 List<Object> objects = new ArrayList<Object> ();
183 for (int i = 0; i < cdlist.size (); i++)
191 value = cd.get (key);
193 catch (InvalidKeyException e)
200 return (genericListToNumber (objects));
201 } /* }}} List<Number> genericCompositeToNumber */
203 private void submitTable (List<Object> objects, ValueList vl, /* {{{ */
204 String instancePrefix)
206 List<CompositeData> cdlist;
207 Set<String> keySet = null;
208 Iterator<String> keyIter;
210 cdlist = new ArrayList<CompositeData> ();
211 for (int i = 0; i < objects.size (); i++)
215 obj = objects.get (i);
216 if (obj instanceof CompositeData)
220 cd = (CompositeData) obj;
223 keySet = cd.getCompositeType ().keySet ();
229 Collectd.logError ("GenericJMXConfValue: At least one of the "
230 + "attributes was not of type `CompositeData', as required "
231 + "when table is set to `true'.");
236 assert (keySet != null);
238 keyIter = keySet.iterator ();
239 while (keyIter.hasNext ())
244 key = keyIter.next ();
245 values = genericCompositeToNumber (cdlist, key);
248 Collectd.logError ("GenericJMXConfValue: Cannot build a list of "
249 + "numbers for key " + key + ". Most likely not all attributes "
254 if (instancePrefix == null)
255 vl.setTypeInstance (key);
257 vl.setTypeInstance (instancePrefix + key);
258 vl.setValues (values);
260 Collectd.dispatchValues (vl);
262 } /* }}} void submitTable */
264 private void submitScalar (List<Object> objects, ValueList vl, /* {{{ */
265 String instancePrefix)
269 values = genericListToNumber (objects);
272 Collectd.logError ("GenericJMXConfValue: Cannot convert list of "
273 + "objects to numbers.");
277 if (instancePrefix == null)
278 vl.setTypeInstance ("");
280 vl.setTypeInstance (instancePrefix);
281 vl.setValues (values);
283 Collectd.dispatchValues (vl);
284 } /* }}} void submitScalar */
286 private Object queryAttributeRecursive (CompositeData parent, /* {{{ */
287 List<String> attrName)
292 key = attrName.remove (0);
296 value = parent.get (key);
298 catch (InvalidKeyException e)
303 if (attrName.size () == 0)
309 if (value instanceof CompositeData)
310 return (queryAttributeRecursive ((CompositeData) value, attrName));
311 else if (value instanceof TabularData)
312 return (queryAttributeRecursive ((TabularData) value, attrName));
316 } /* }}} queryAttributeRecursive */
318 private Object queryAttributeRecursive (TabularData parent, /* {{{ */
319 List<String> attrName)
324 key = attrName.remove (0);
326 @SuppressWarnings("unchecked")
327 Collection<CompositeData> table = (Collection<CompositeData>) parent.values();
328 for (CompositeData compositeData : table)
330 if (key.equals(compositeData.get("key")))
332 value = compositeData.get("value");
340 if (attrName.size () == 0)
346 if (value instanceof CompositeData)
347 return (queryAttributeRecursive ((CompositeData) value, attrName));
348 else if (value instanceof TabularData)
349 return (queryAttributeRecursive ((TabularData) value, attrName));
353 } /* }}} queryAttributeRecursive */
355 private Object queryAttribute (MBeanServerConnection conn, /* {{{ */
356 ObjectName objName, String attrName)
358 List<String> attrNameList;
361 String[] attrNameArray;
363 attrNameList = new ArrayList<String> ();
365 attrNameArray = attrName.split ("\\.");
366 key = attrNameArray[0];
367 for (int i = 1; i < attrNameArray.length; i++)
368 attrNameList.add (attrNameArray[i]);
374 value = conn.getAttribute (objName, key);
376 catch (javax.management.AttributeNotFoundException e)
378 value = conn.invoke (objName, key, /* args = */ null, /* types = */ null);
383 Collectd.logError ("GenericJMXConfValue.query: getAttribute failed: "
388 if (attrNameList.size () == 0)
394 if (value instanceof CompositeData)
395 return (queryAttributeRecursive((CompositeData) value, attrNameList));
396 else if (value instanceof TabularData)
397 return (queryAttributeRecursive((TabularData) value, attrNameList));
398 else if (value instanceof OpenType)
400 OpenType ot = (OpenType) value;
401 Collectd.logNotice ("GenericJMXConfValue: Handling of OpenType \""
402 + ot.getTypeName () + "\" is not yet implemented.");
407 Collectd.logError ("GenericJMXConfValue: Received object of "
408 + "unknown class. " + attrName + " " + ((value == null)?"null":value.getClass().getName()));
412 } /* }}} Object queryAttribute */
414 private String join (String separator, List<String> list) /* {{{ */
418 sb = new StringBuffer ();
420 for (int i = 0; i < list.size (); i++)
424 sb.append (list.get (i));
427 return (sb.toString ());
428 } /* }}} String join */
430 private String getConfigString (OConfigItem ci) /* {{{ */
432 List<OConfigValue> values;
435 values = ci.getValues ();
436 if (values.size () != 1)
438 Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
439 + " configuration option needs exactly one string argument.");
444 if (v.getType () != OConfigValue.OCONFIG_TYPE_STRING)
446 Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
447 + " configuration option needs exactly one string argument.");
451 return (v.getString ());
452 } /* }}} String getConfigString */
454 private Boolean getConfigBoolean (OConfigItem ci) /* {{{ */
456 List<OConfigValue> values;
460 values = ci.getValues ();
461 if (values.size () != 1)
463 Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
464 + " configuration option needs exactly one boolean argument.");
469 if (v.getType () != OConfigValue.OCONFIG_TYPE_BOOLEAN)
471 Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
472 + " configuration option needs exactly one boolean argument.");
476 return (new Boolean (v.getBoolean ()));
477 } /* }}} String getConfigBoolean */
480 * Constructs a new value with the configured properties.
482 public GenericJMXConfValue (OConfigItem ci) /* {{{ */
483 throws IllegalArgumentException
485 List<OConfigItem> children;
486 Iterator<OConfigItem> iter;
488 this._ds_name = null;
490 this._attributes = new ArrayList<String> ();
491 this._instance_prefix = null;
492 this._instance_from = new ArrayList<String> ();
493 this._plugin_name = null;
494 this._is_table = false;
500 * Attribute "HeapMemoryUsage"
504 * InstancePrefix "heap-"
507 children = ci.getChildren ();
508 iter = children.iterator ();
509 while (iter.hasNext ())
511 OConfigItem child = iter.next ();
513 if (child.getKey ().equalsIgnoreCase ("Type"))
515 String tmp = getConfigString (child);
519 else if (child.getKey ().equalsIgnoreCase ("Table"))
521 Boolean tmp = getConfigBoolean (child);
523 this._is_table = tmp.booleanValue ();
525 else if (child.getKey ().equalsIgnoreCase ("Attribute"))
527 String tmp = getConfigString (child);
529 this._attributes.add (tmp);
531 else if (child.getKey ().equalsIgnoreCase ("InstancePrefix"))
533 String tmp = getConfigString (child);
535 this._instance_prefix = tmp;
537 else if (child.getKey ().equalsIgnoreCase ("InstanceFrom"))
539 String tmp = getConfigString (child);
541 this._instance_from.add (tmp);
543 else if (child.getKey ().equalsIgnoreCase ("PluginName"))
545 String tmp = getConfigString (child);
547 this._plugin_name = tmp;
550 throw (new IllegalArgumentException ("Unknown option: "
554 if (this._ds_name == null)
555 throw (new IllegalArgumentException ("No data set was defined."));
556 else if (this._attributes.size () == 0)
557 throw (new IllegalArgumentException ("No attribute was defined."));
558 } /* }}} GenericJMXConfValue (OConfigItem ci) */
561 * Query values via JMX according to the object's configuration and dispatch
564 * @param conn Connection to the MBeanServer.
565 * @param objName Object name of the MBean to query.
566 * @param pd Preset naming components. The members host, plugin and
567 * plugin instance will be used.
569 public void query (MBeanServerConnection conn, ObjectName objName, /* {{{ */
573 List<DataSource> dsrc;
575 List<String> instanceList;
576 String instancePrefix;
578 if (this._ds == null)
580 this._ds = Collectd.getDS (this._ds_name);
581 if (this._ds == null)
583 Collectd.logError ("GenericJMXConfValue: Unknown type: "
589 dsrc = this._ds.getDataSources ();
590 if (dsrc.size () != this._attributes.size ())
592 Collectd.logError ("GenericJMXConfValue.query: The data set "
593 + this._ds_name + " has " + this._ds.getDataSources ().size ()
594 + " data sources, but there were " + this._attributes.size ()
595 + " attributes configured. This doesn't match!");
600 vl = new ValueList (pd);
601 vl.setType (this._ds_name);
602 if (this._plugin_name != null)
604 vl.setPlugin (this._plugin_name);
608 * Build the instnace prefix from the fixed string prefix and the
609 * properties of the objName.
611 instanceList = new ArrayList<String> ();
612 for (int i = 0; i < this._instance_from.size (); i++)
615 String propertyValue;
617 propertyName = this._instance_from.get (i);
618 propertyValue = objName.getKeyProperty (propertyName);
619 if (propertyValue == null)
621 Collectd.logError ("GenericJMXConfMBean: "
622 + "No such property in object name: " + propertyName);
626 instanceList.add (propertyValue);
630 if (this._instance_prefix != null)
631 instancePrefix = new String (this._instance_prefix
632 + join ("-", instanceList));
634 instancePrefix = join ("-", instanceList);
637 * Build a list of `Object's which is then passed to `submitTable' and
640 values = new ArrayList<Object> ();
641 assert (dsrc.size () == this._attributes.size ());
642 for (int i = 0; i < this._attributes.size (); i++)
646 v = queryAttribute (conn, objName, this._attributes.get (i));
649 Collectd.logError ("GenericJMXConfValue.query: "
650 + "Querying attribute " + this._attributes.get (i) + " failed.");
658 submitTable (values, vl, instancePrefix);
660 submitScalar (values, vl, instancePrefix);
661 } /* }}} void query */
662 } /* class GenericJMXConfValue */
664 /* vim: set sw=2 sts=2 et fdm=marker : */