PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/bindings/java/org/collectd/java/GenericJMXConfValue.java

http://github.com/octo/collectd
Java | 664 lines | 502 code | 75 blank | 87 comment | 122 complexity | 28ca2ed665e9b3193e419c42508528f3 MD5 | raw file
Possible License(s): GPL-2.0
  1. /**
  2. * collectd - bindings/java/org/collectd/java/GenericJMXConfValue.java
  3. * Copyright (C) 2009 Florian octo Forster
  4. *
  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:
  11. *
  12. * The above copyright notice and this permission notice shall be included in
  13. * all copies or substantial portions of the Software.
  14. *
  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.
  22. *
  23. * Authors:
  24. * Florian octo Forster <octo at collectd.org>
  25. */
  26. package org.collectd.java;
  27. import java.util.Arrays;
  28. import java.util.List;
  29. import java.util.Collection;
  30. import java.util.Set;
  31. import java.util.concurrent.atomic.AtomicInteger;
  32. import java.util.concurrent.atomic.AtomicLong;
  33. import java.util.Iterator;
  34. import java.util.ArrayList;
  35. import java.math.BigDecimal;
  36. import java.math.BigInteger;
  37. import javax.management.MBeanServerConnection;
  38. import javax.management.ObjectName;
  39. import javax.management.openmbean.OpenType;
  40. import javax.management.openmbean.CompositeData;
  41. import javax.management.openmbean.TabularData;
  42. import javax.management.openmbean.InvalidKeyException;
  43. import org.collectd.api.Collectd;
  44. import org.collectd.api.DataSet;
  45. import org.collectd.api.DataSource;
  46. import org.collectd.api.ValueList;
  47. import org.collectd.api.PluginData;
  48. import org.collectd.api.OConfigValue;
  49. import org.collectd.api.OConfigItem;
  50. /**
  51. * Representation of a &lt;value&nbsp;/&gt; block and query functionality.
  52. *
  53. * This class represents a &lt;value&nbsp;/&gt; block in the configuration. As
  54. * such, the constructor takes an {@link org.collectd.api.OConfigValue} to
  55. * construct an object of this class.
  56. *
  57. * The object can then be asked to query data from JMX and dispatch it to
  58. * collectd.
  59. *
  60. * @see GenericJMXConfMBean
  61. */
  62. class GenericJMXConfValue
  63. {
  64. private String _ds_name;
  65. private DataSet _ds;
  66. private List<String> _attributes;
  67. private String _instance_prefix;
  68. private List<String> _instance_from;
  69. private String _plugin_name;
  70. private boolean _is_table;
  71. /**
  72. * Converts a generic (OpenType) object to a number.
  73. *
  74. * Returns null if a conversion is not possible or not implemented.
  75. */
  76. private Number genericObjectToNumber (Object obj, int ds_type) /* {{{ */
  77. {
  78. if (obj instanceof String)
  79. {
  80. String str = (String) obj;
  81. try
  82. {
  83. if (ds_type == DataSource.TYPE_GAUGE)
  84. return (new Double (str));
  85. else
  86. return (new Long (str));
  87. }
  88. catch (NumberFormatException e)
  89. {
  90. return (null);
  91. }
  92. }
  93. else if (obj instanceof Byte)
  94. {
  95. return (new Byte ((Byte) obj));
  96. }
  97. else if (obj instanceof Short)
  98. {
  99. return (new Short ((Short) obj));
  100. }
  101. else if (obj instanceof Integer)
  102. {
  103. return (new Integer ((Integer) obj));
  104. }
  105. else if (obj instanceof Long)
  106. {
  107. return (new Long ((Long) obj));
  108. }
  109. else if (obj instanceof Float)
  110. {
  111. return (new Float ((Float) obj));
  112. }
  113. else if (obj instanceof Double)
  114. {
  115. return (new Double ((Double) obj));
  116. }
  117. else if (obj instanceof BigDecimal)
  118. {
  119. return (BigDecimal.ZERO.add ((BigDecimal) obj));
  120. }
  121. else if (obj instanceof BigInteger)
  122. {
  123. return (BigInteger.ZERO.add ((BigInteger) obj));
  124. }
  125. else if (obj instanceof AtomicInteger)
  126. {
  127. return (new Integer(((AtomicInteger) obj).get()));
  128. }
  129. else if (obj instanceof AtomicLong)
  130. {
  131. return (new Long(((AtomicLong) obj).get()));
  132. }
  133. return (null);
  134. } /* }}} Number genericObjectToNumber */
  135. /**
  136. * Converts a generic list to a list of numbers.
  137. *
  138. * Returns null if one or more objects could not be converted.
  139. */
  140. private List<Number> genericListToNumber (List<Object> objects) /* {{{ */
  141. {
  142. List<Number> ret = new ArrayList<Number> ();
  143. List<DataSource> dsrc = this._ds.getDataSources ();
  144. assert (objects.size () == dsrc.size ());
  145. for (int i = 0; i < objects.size (); i++)
  146. {
  147. Number n;
  148. n = genericObjectToNumber (objects.get (i), dsrc.get (i).getType ());
  149. if (n == null)
  150. return (null);
  151. ret.add (n);
  152. }
  153. return (ret);
  154. } /* }}} List<Number> genericListToNumber */
  155. /**
  156. * Converts a list of CompositeData to a list of numbers.
  157. *
  158. * From each <em>CompositeData </em> the key <em>key</em> is received and all
  159. * those values are converted to a number. If one of the
  160. * <em>CompositeData</em> doesn't have the specified key or one returned
  161. * object cannot converted to a number then the function will return null.
  162. */
  163. private List<Number> genericCompositeToNumber (List<CompositeData> cdlist, /* {{{ */
  164. String key)
  165. {
  166. List<Object> objects = new ArrayList<Object> ();
  167. for (int i = 0; i < cdlist.size (); i++)
  168. {
  169. CompositeData cd;
  170. Object value;
  171. cd = cdlist.get (i);
  172. try
  173. {
  174. value = cd.get (key);
  175. }
  176. catch (InvalidKeyException e)
  177. {
  178. return (null);
  179. }
  180. objects.add (value);
  181. }
  182. return (genericListToNumber (objects));
  183. } /* }}} List<Number> genericCompositeToNumber */
  184. private void submitTable (List<Object> objects, ValueList vl, /* {{{ */
  185. String instancePrefix)
  186. {
  187. List<CompositeData> cdlist;
  188. Set<String> keySet = null;
  189. Iterator<String> keyIter;
  190. cdlist = new ArrayList<CompositeData> ();
  191. for (int i = 0; i < objects.size (); i++)
  192. {
  193. Object obj;
  194. obj = objects.get (i);
  195. if (obj instanceof CompositeData)
  196. {
  197. CompositeData cd;
  198. cd = (CompositeData) obj;
  199. if (i == 0)
  200. keySet = cd.getCompositeType ().keySet ();
  201. cdlist.add (cd);
  202. }
  203. else
  204. {
  205. Collectd.logError ("GenericJMXConfValue: At least one of the "
  206. + "attributes was not of type `CompositeData', as required "
  207. + "when table is set to `true'.");
  208. return;
  209. }
  210. }
  211. assert (keySet != null);
  212. keyIter = keySet.iterator ();
  213. while (keyIter.hasNext ())
  214. {
  215. String key;
  216. List<Number> values;
  217. key = keyIter.next ();
  218. values = genericCompositeToNumber (cdlist, key);
  219. if (values == null)
  220. {
  221. Collectd.logError ("GenericJMXConfValue: Cannot build a list of "
  222. + "numbers for key " + key + ". Most likely not all attributes "
  223. + "have this key.");
  224. continue;
  225. }
  226. if (instancePrefix == null)
  227. vl.setTypeInstance (key);
  228. else
  229. vl.setTypeInstance (instancePrefix + key);
  230. vl.setValues (values);
  231. Collectd.dispatchValues (vl);
  232. }
  233. } /* }}} void submitTable */
  234. private void submitScalar (List<Object> objects, ValueList vl, /* {{{ */
  235. String instancePrefix)
  236. {
  237. List<Number> values;
  238. values = genericListToNumber (objects);
  239. if (values == null)
  240. {
  241. Collectd.logError ("GenericJMXConfValue: Cannot convert list of "
  242. + "objects to numbers.");
  243. return;
  244. }
  245. if (instancePrefix == null)
  246. vl.setTypeInstance ("");
  247. else
  248. vl.setTypeInstance (instancePrefix);
  249. vl.setValues (values);
  250. Collectd.dispatchValues (vl);
  251. } /* }}} void submitScalar */
  252. private Object queryAttributeRecursive (CompositeData parent, /* {{{ */
  253. List<String> attrName)
  254. {
  255. String key;
  256. Object value;
  257. key = attrName.remove (0);
  258. try
  259. {
  260. value = parent.get (key);
  261. }
  262. catch (InvalidKeyException e)
  263. {
  264. return (null);
  265. }
  266. if (attrName.size () == 0)
  267. {
  268. return (value);
  269. }
  270. else
  271. {
  272. if (value instanceof CompositeData)
  273. return (queryAttributeRecursive ((CompositeData) value, attrName));
  274. else if (value instanceof TabularData)
  275. return (queryAttributeRecursive ((TabularData) value, attrName));
  276. else
  277. return (null);
  278. }
  279. } /* }}} queryAttributeRecursive */
  280. private Object queryAttributeRecursive (TabularData parent, /* {{{ */
  281. List<String> attrName)
  282. {
  283. String key;
  284. Object value = null;
  285. key = attrName.remove (0);
  286. @SuppressWarnings("unchecked")
  287. Collection<CompositeData> table = (Collection<CompositeData>) parent.values();
  288. for (CompositeData compositeData : table)
  289. {
  290. if (key.equals(compositeData.get("key")))
  291. {
  292. value = compositeData.get("value");
  293. }
  294. }
  295. if (null == value)
  296. {
  297. return (null);
  298. }
  299. if (attrName.size () == 0)
  300. {
  301. return (value);
  302. }
  303. else
  304. {
  305. if (value instanceof CompositeData)
  306. return (queryAttributeRecursive ((CompositeData) value, attrName));
  307. else if (value instanceof TabularData)
  308. return (queryAttributeRecursive ((TabularData) value, attrName));
  309. else
  310. return (null);
  311. }
  312. } /* }}} queryAttributeRecursive */
  313. private Object queryAttribute (MBeanServerConnection conn, /* {{{ */
  314. ObjectName objName, String attrName)
  315. {
  316. List<String> attrNameList;
  317. String key;
  318. Object value;
  319. String[] attrNameArray;
  320. attrNameList = new ArrayList<String> ();
  321. attrNameArray = attrName.split ("\\.");
  322. key = attrNameArray[0];
  323. for (int i = 1; i < attrNameArray.length; i++)
  324. attrNameList.add (attrNameArray[i]);
  325. try
  326. {
  327. try
  328. {
  329. value = conn.getAttribute (objName, key);
  330. }
  331. catch (javax.management.AttributeNotFoundException e)
  332. {
  333. value = conn.invoke (objName, key, /* args = */ null, /* types = */ null);
  334. }
  335. }
  336. catch (Exception e)
  337. {
  338. Collectd.logError ("GenericJMXConfValue.query: getAttribute failed: "
  339. + e);
  340. return (null);
  341. }
  342. if (attrNameList.size () == 0)
  343. {
  344. return (value);
  345. }
  346. else
  347. {
  348. if (value instanceof CompositeData)
  349. return (queryAttributeRecursive((CompositeData) value, attrNameList));
  350. else if (value instanceof TabularData)
  351. return (queryAttributeRecursive((TabularData) value, attrNameList));
  352. else if (value instanceof OpenType)
  353. {
  354. OpenType ot = (OpenType) value;
  355. Collectd.logNotice ("GenericJMXConfValue: Handling of OpenType \""
  356. + ot.getTypeName () + "\" is not yet implemented.");
  357. return (null);
  358. }
  359. else
  360. {
  361. Collectd.logError ("GenericJMXConfValue: Received object of "
  362. + "unknown class. " + attrName + " " + ((value == null)?"null":value.getClass().getName()));
  363. return (null);
  364. }
  365. }
  366. } /* }}} Object queryAttribute */
  367. private String join (String separator, List<String> list) /* {{{ */
  368. {
  369. StringBuffer sb;
  370. sb = new StringBuffer ();
  371. for (int i = 0; i < list.size (); i++)
  372. {
  373. if (i > 0)
  374. sb.append ("-");
  375. sb.append (list.get (i));
  376. }
  377. return (sb.toString ());
  378. } /* }}} String join */
  379. private String getConfigString (OConfigItem ci) /* {{{ */
  380. {
  381. List<OConfigValue> values;
  382. OConfigValue v;
  383. values = ci.getValues ();
  384. if (values.size () != 1)
  385. {
  386. Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
  387. + " configuration option needs exactly one string argument.");
  388. return (null);
  389. }
  390. v = values.get (0);
  391. if (v.getType () != OConfigValue.OCONFIG_TYPE_STRING)
  392. {
  393. Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
  394. + " configuration option needs exactly one string argument.");
  395. return (null);
  396. }
  397. return (v.getString ());
  398. } /* }}} String getConfigString */
  399. private Boolean getConfigBoolean (OConfigItem ci) /* {{{ */
  400. {
  401. List<OConfigValue> values;
  402. OConfigValue v;
  403. Boolean b;
  404. values = ci.getValues ();
  405. if (values.size () != 1)
  406. {
  407. Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
  408. + " configuration option needs exactly one boolean argument.");
  409. return (null);
  410. }
  411. v = values.get (0);
  412. if (v.getType () != OConfigValue.OCONFIG_TYPE_BOOLEAN)
  413. {
  414. Collectd.logError ("GenericJMXConfValue: The " + ci.getKey ()
  415. + " configuration option needs exactly one boolean argument.");
  416. return (null);
  417. }
  418. return (new Boolean (v.getBoolean ()));
  419. } /* }}} String getConfigBoolean */
  420. /**
  421. * Constructs a new value with the configured properties.
  422. */
  423. public GenericJMXConfValue (OConfigItem ci) /* {{{ */
  424. throws IllegalArgumentException
  425. {
  426. List<OConfigItem> children;
  427. Iterator<OConfigItem> iter;
  428. this._ds_name = null;
  429. this._ds = null;
  430. this._attributes = new ArrayList<String> ();
  431. this._instance_prefix = null;
  432. this._instance_from = new ArrayList<String> ();
  433. this._plugin_name = null;
  434. this._is_table = false;
  435. /*
  436. * <Value>
  437. * Type "memory"
  438. * Table true|false
  439. * Attribute "HeapMemoryUsage"
  440. * Attribute "..."
  441. * :
  442. * # Type instance:
  443. * InstancePrefix "heap-"
  444. * </Value>
  445. */
  446. children = ci.getChildren ();
  447. iter = children.iterator ();
  448. while (iter.hasNext ())
  449. {
  450. OConfigItem child = iter.next ();
  451. if (child.getKey ().equalsIgnoreCase ("Type"))
  452. {
  453. String tmp = getConfigString (child);
  454. if (tmp != null)
  455. this._ds_name = tmp;
  456. }
  457. else if (child.getKey ().equalsIgnoreCase ("Table"))
  458. {
  459. Boolean tmp = getConfigBoolean (child);
  460. if (tmp != null)
  461. this._is_table = tmp.booleanValue ();
  462. }
  463. else if (child.getKey ().equalsIgnoreCase ("Attribute"))
  464. {
  465. String tmp = getConfigString (child);
  466. if (tmp != null)
  467. this._attributes.add (tmp);
  468. }
  469. else if (child.getKey ().equalsIgnoreCase ("InstancePrefix"))
  470. {
  471. String tmp = getConfigString (child);
  472. if (tmp != null)
  473. this._instance_prefix = tmp;
  474. }
  475. else if (child.getKey ().equalsIgnoreCase ("InstanceFrom"))
  476. {
  477. String tmp = getConfigString (child);
  478. if (tmp != null)
  479. this._instance_from.add (tmp);
  480. }
  481. else if (child.getKey ().equalsIgnoreCase ("PluginName"))
  482. {
  483. String tmp = getConfigString (child);
  484. if (tmp != null)
  485. this._plugin_name = tmp;
  486. }
  487. else
  488. throw (new IllegalArgumentException ("Unknown option: "
  489. + child.getKey ()));
  490. }
  491. if (this._ds_name == null)
  492. throw (new IllegalArgumentException ("No data set was defined."));
  493. else if (this._attributes.size () == 0)
  494. throw (new IllegalArgumentException ("No attribute was defined."));
  495. } /* }}} GenericJMXConfValue (OConfigItem ci) */
  496. /**
  497. * Query values via JMX according to the object's configuration and dispatch
  498. * them to collectd.
  499. *
  500. * @param conn Connection to the MBeanServer.
  501. * @param objName Object name of the MBean to query.
  502. * @param pd Preset naming components. The members host, plugin and
  503. * plugin instance will be used.
  504. */
  505. public void query (MBeanServerConnection conn, ObjectName objName, /* {{{ */
  506. PluginData pd)
  507. {
  508. ValueList vl;
  509. List<DataSource> dsrc;
  510. List<Object> values;
  511. List<String> instanceList;
  512. String instancePrefix;
  513. if (this._ds == null)
  514. {
  515. this._ds = Collectd.getDS (this._ds_name);
  516. if (this._ds == null)
  517. {
  518. Collectd.logError ("GenericJMXConfValue: Unknown type: "
  519. + this._ds_name);
  520. return;
  521. }
  522. }
  523. dsrc = this._ds.getDataSources ();
  524. if (dsrc.size () != this._attributes.size ())
  525. {
  526. Collectd.logError ("GenericJMXConfValue.query: The data set "
  527. + this._ds_name + " has " + this._ds.getDataSources ().size ()
  528. + " data sources, but there were " + this._attributes.size ()
  529. + " attributes configured. This doesn't match!");
  530. this._ds = null;
  531. return;
  532. }
  533. vl = new ValueList (pd);
  534. vl.setType (this._ds_name);
  535. if (this._plugin_name != null)
  536. {
  537. vl.setPlugin (this._plugin_name);
  538. }
  539. /*
  540. * Build the instnace prefix from the fixed string prefix and the
  541. * properties of the objName.
  542. */
  543. instanceList = new ArrayList<String> ();
  544. for (int i = 0; i < this._instance_from.size (); i++)
  545. {
  546. String propertyName;
  547. String propertyValue;
  548. propertyName = this._instance_from.get (i);
  549. propertyValue = objName.getKeyProperty (propertyName);
  550. if (propertyValue == null)
  551. {
  552. Collectd.logError ("GenericJMXConfMBean: "
  553. + "No such property in object name: " + propertyName);
  554. }
  555. else
  556. {
  557. instanceList.add (propertyValue);
  558. }
  559. }
  560. if (this._instance_prefix != null)
  561. instancePrefix = new String (this._instance_prefix
  562. + join ("-", instanceList));
  563. else
  564. instancePrefix = join ("-", instanceList);
  565. /*
  566. * Build a list of `Object's which is then passed to `submitTable' and
  567. * `submitScalar'.
  568. */
  569. values = new ArrayList<Object> ();
  570. assert (dsrc.size () == this._attributes.size ());
  571. for (int i = 0; i < this._attributes.size (); i++)
  572. {
  573. Object v;
  574. v = queryAttribute (conn, objName, this._attributes.get (i));
  575. if (v == null)
  576. {
  577. Collectd.logError ("GenericJMXConfValue.query: "
  578. + "Querying attribute " + this._attributes.get (i) + " failed.");
  579. return;
  580. }
  581. values.add (v);
  582. }
  583. if (this._is_table)
  584. submitTable (values, vl, instancePrefix);
  585. else
  586. submitScalar (values, vl, instancePrefix);
  587. } /* }}} void query */
  588. } /* class GenericJMXConfValue */
  589. /* vim: set sw=2 sts=2 et fdm=marker : */