/
JMXCollector.java
858 lines (750 loc) · 37 KB
/
JMXCollector.java
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
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
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
/*******************************************************************************
* This file is part of OpenNMS(R).
*
* Copyright (C) 2006-2012 The OpenNMS Group, Inc.
* OpenNMS(R) is Copyright (C) 1999-2012 The OpenNMS Group, Inc.
*
* OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc.
*
* OpenNMS(R) is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* OpenNMS(R) is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenNMS(R). If not, see:
* http://www.gnu.org/licenses/
*
* For more information contact:
* OpenNMS(R) Licensing <license@opennms.org>
* http://www.opennms.org/
* http://www.opennms.com/
*******************************************************************************/
package org.opennms.netmgt.collectd;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeData;
import org.opennms.core.db.DataSourceFactory;
import org.opennms.core.utils.AlphaNumeric;
import org.opennms.core.utils.InetAddressUtils;
import org.opennms.core.utils.ParameterMap;
import org.opennms.netmgt.config.BeanInfo;
import org.opennms.netmgt.config.JMXDataCollectionConfigFactory;
import org.opennms.netmgt.config.collectd.jmx.Attrib;
import org.opennms.netmgt.config.collector.AbstractCollectionSet;
import org.opennms.netmgt.config.collector.AttributeGroupType;
import org.opennms.netmgt.config.collector.CollectionAttribute;
import org.opennms.netmgt.config.collector.CollectionAttributeType;
import org.opennms.netmgt.config.collector.CollectionResource;
import org.opennms.netmgt.config.collector.CollectionSet;
import org.opennms.netmgt.config.collector.CollectionSetVisitor;
import org.opennms.netmgt.config.collector.Persister;
import org.opennms.netmgt.config.collector.ServiceParameters.ParameterName;
import org.opennms.netmgt.model.RrdRepository;
import org.opennms.netmgt.model.events.EventProxy;
import org.opennms.protocols.jmx.connectors.ConnectionWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class performs the collection and storage of data. The derived class
* manages the connection and configuration. The SNMPCollector class was used
* as the starting point for this class so anyone familiar with it should be
* able to easily understand it.
* <p>
* The jmx-datacollection-config.xml defines a list of MBeans and attributes
* that may be monitored. This class retrieves the list of MBeans for the
* specified service name (currently jboss and jsr160) and queries the remote
* server for the attributes. The values are then stored in RRD files.
* </p>
* <p>
* Two types of MBeans may be specified in the jmx-datacollection-config.xml
* file. Standard MBeans which consist of and ObjectName and their attributes,
* and WildCard MBeans which performs a query to retrieve MBeans based on a
* criteria. The current implementation looks like: jboss:a=b,c=d,* Future
* versions may permit enhanced queries. In either case multiple MBeans may be
* returned and these MBeans would then be queried to obtain their attributes.
* There are some important issues then using the wild card approach:
* </p>
* <p>
* <ol>
* <li>Since multiple MBeans will have the same attribute name there needs to
* be a way to differentiate them. To handle this situation you need to
* specify which field in the ObjectName should be used. This is defined as
* the key-field.</li>
* <li>The version of RRD that is used is limited to 19 characters. If this
* limit is exceeded then the data will not be saved. The name is defined as:
* keyField_attributeName.rrd Since the keyfield is defined in the Object Name
* and may be too long, you may define an alias for it. The key-alias
* parameter permit you to define a list of names to be substituted. Only
* exact matches are handled. An example is:
* <code>key-alias="this-name-is-long|thisIsNot,name-way-2-long,goodName"</code></li>
* <li>If there are keyfields that you want to exclude (exact matches) you
* may use a comma separated list like:
* <code>exclude="name1,name2,name3"</code></li>
* <li>Unlike the Standard MBeans there is no way (currently) to pre-define
* graphs for them in the snmp-graph.properties file. The only way you can
* create graphs is to create a custom graph in the Report section. The wild
* card approach needs to be carefully considered before using it but it can
* cut down on the amount of work necessary to define what to save.</li>
* </ol>
* </p>
*
* @author <a href="mailto:mike@opennms.org">Mike Jamison</a>
* @author <a href="http://www.opennms.org/">OpenNMS</a>
*/
public abstract class JMXCollector implements ServiceCollector {
private static final Logger LOG = LoggerFactory.getLogger(JMXCollector.class);
/**
* Interface attribute key used to store the map of IfInfo objects which
* hold data about each interface on a particular node.
*/
static String IF_MAP_KEY = "org.opennms.netmgt.collectd.JBossCollector.ifMap";
/**
* RRD data source name max length.
*/
private static final int MAX_DS_NAME_LENGTH = 19;
/**
* In some circumstances there may be many instances of a given service
* but running on different ports. Rather than using the port as the
* identifier users may define a more meaningful name.
*/
private boolean useFriendlyName = false;
/**
* Interface attribute key used to store a JMXNodeInfo object which holds
* data about the node being polled.
*/
static String NODE_INFO_KEY =
"org.opennms.netmgt.collectd.JMXCollector.nodeInfo";
/**
* The service name is provided by the derived class
*/
private String serviceName = null;
/**
* <p>
* Returns the name of the service that the plug-in collects ("JMX").
* </p>
*
* @return The service that the plug-in collects.
*/
public String serviceName() {
return serviceName.toUpperCase();
}
/**
* <p>Setter for the field <code>serviceName</code>.</p>
*
* @param name a {@link java.lang.String} object.
*/
public void setServiceName(String name) {
serviceName = name;
}
/**
* {@inheritDoc}
*
* <p>
* Initialize the service collector.
* </p>
* <p>
* During initialization the JMX collector: - Initializes various
* configuration factories. - Verifies access to the database - Verifies
* access to RRD file repository - Verifies access to JNI RRD shared
* library - Determines if JMX to be stored for only the node's primary
* interface or for all interfaces.
* </p>
* @exception RuntimeException
* Thrown if an unrecoverable error occurs that prevents
* the plug-in from functioning.
*/
@Override
public void initialize(Map<String, String> parameters) {
// Initialize the JMXDataCollectionConfigFactory
try {
// XXX was reload(), which isn't test-friendly
JMXDataCollectionConfigFactory.init();
} catch (Throwable e) {
LOG.error("initialize: Failed to load data collection configuration", e);
throw new UndeclaredThrowableException(e);
}
// Make sure we can connect to the database
java.sql.Connection ctest = null;
try {
ctest = DataSourceFactory.getInstance().getConnection();
} catch (final Exception e) {
LOG.error("initialize: failed to get a database connection", e);
throw new UndeclaredThrowableException(e);
} finally {
if (ctest != null) {
try {
ctest.close();
} catch (final Throwable t) {
LOG.debug("initialize: an exception occured while closing the JDBC connection");
}
}
}
// Save local reference to singleton instance
LOG.debug("initialize: successfully instantiated JNI interface to RRD.");
}
/**
* Responsible for freeing up any resources held by the collector.
*/
@Override
public void release() {
// Nothing to release...
}
/**
* {@inheritDoc}
*
* Responsible for performing all necessary initialization for the
* specified interface in preparation for data collection.
*/
@Override
public void initialize(CollectionAgent agent, Map<String, Object> parameters) {
InetAddress ipAddr = agent.getAddress();
int nodeID = agent.getNodeId();
// Retrieve the name of the JMX data collector
String collectionName = ParameterMap.getKeyedString(parameters, ParameterName.COLLECTION.toString(), serviceName);
final String hostAddress = InetAddressUtils.str(ipAddr);
LOG.debug("initialize: InetAddress={}, collectionName={}", hostAddress, collectionName);
JMXNodeInfo nodeInfo = new JMXNodeInfo(nodeID);
LOG.debug("nodeInfo: {} {} {}", hostAddress, nodeID, agent);
/*
* Retrieve list of MBean objects to be collected from the
* remote agent which are to be stored in the node-level RRD file.
* These objects pertain to the node itself not any individual
* interfaces.
*/
Map<String, List<Attrib>> attrMap = JMXDataCollectionConfigFactory.getInstance().getAttributeMap(collectionName, serviceName, hostAddress);
nodeInfo.setAttributeMap(attrMap);
Map<String, JMXDataSource> dsList = buildDataSourceList(collectionName, attrMap);
nodeInfo.setDsMap(dsList);
nodeInfo.setMBeans(JMXDataCollectionConfigFactory.getInstance().getMBeanInfo(collectionName));
// Add the JMXNodeInfo object as an attribute of the interface
agent.setAttribute(NODE_INFO_KEY, nodeInfo);
agent.setAttribute("collectionName", collectionName);
}
/**
* {@inheritDoc}
*
* Responsible for releasing any resources associated with the specified
* interface.
*/
@Override
public void release(CollectionAgent agent) {
// Nothing to release...
}
/**
* <p>getMBeanServerConnection</p>
*
* @param map a {@link java.util.Map} object.
* @param address a {@link java.net.InetAddress} object.
* @return a {@link org.opennms.protocols.jmx.connectors.ConnectionWrapper} object.
*/
public abstract ConnectionWrapper getMBeanServerConnection(Map<String, Object> map, InetAddress address);
/**
* {@inheritDoc}
*
* Perform data collection.
*/
@Override
public CollectionSet collect(CollectionAgent agent, EventProxy eproxy, Map<String, Object> map) {
InetAddress ipaddr = agent.getAddress();
JMXNodeInfo nodeInfo = agent.getAttribute(NODE_INFO_KEY);
Map<String, BeanInfo> mbeans = nodeInfo.getMBeans();
String collDir = serviceName;
boolean useMbeanForRrds = ParameterMap.getKeyedBoolean(map, ParameterName.USE_MBEAN_NAME_FOR_RRDS.toString(), false);
String port = ParameterMap.getKeyedString(map, ParameterName.PORT.toString(), null);
String friendlyName = ParameterMap.getKeyedString(map, ParameterName.FRIENDLY_NAME.toString(), port);
if (useFriendlyName) {
collDir = friendlyName;
}
JMXCollectionSet collectionSet=new JMXCollectionSet(agent,collDir);
collectionSet.setCollectionTimestamp(new Date());
JMXCollectionResource collectionResource=collectionSet.getResource();
ConnectionWrapper connection = null;
LOG.debug("collecting {} on node ID {}", InetAddressUtils.str(ipaddr), nodeInfo.getNodeId());
try {
connection = getMBeanServerConnection(map, ipaddr);
if (connection == null) {
return collectionSet;
}
MBeanServerConnection mbeanServer = connection.getMBeanServer();
int retry = ParameterMap.getKeyedInteger(map, ParameterName.RETRY.toString(), 3);
for (int attempts = 0; attempts <= retry; attempts++) {
try {
/*
* Iterate over the mbeans, for each object name perform a
* getAttributes, the update the RRD.
*/
for (Iterator<BeanInfo> iter = mbeans.values().iterator(); iter.hasNext();) {
BeanInfo beanInfo = iter.next();
String mbeanName = beanInfo.getMbeanName();
String objectName = beanInfo.getObjectName();
String excludeList = beanInfo.getExcludes();
//All JMX collected values are per node
String obj = useMbeanForRrds ? mbeanName : objectName;
AttributeGroupType attribGroupType=new AttributeGroupType(fixGroupName(obj),"all");
List<String> attribNames = beanInfo.getAttributeNames();
List<String> compAttribNames = beanInfo.getCompositeAttributeNames();
for (String compAttribName : compAttribNames) {
if (attribNames.contains(compAttribName) ) {
attribNames.remove(compAttribName);
String[] ac = compAttribName.split("\\|", -1);
String attrName = ac[0];
if (!attribNames.contains(attrName)) {
attribNames.add(attrName);
}
}
}
//LOG.debug(" JMXCollector: processed the following attributes: {}", attribNames);
//LOG.debug(" JMXCollector: processed the following Composite Attributes: {}", compAttribNames);
String[] attrNames = attribNames.toArray(new String[attribNames.size()]);
if (objectName.indexOf('*') == -1) {
LOG.debug("{} Collector - getAttributes: {}, # attributes: {}, # composite attribute members: {}", serviceName, objectName, attrNames.length, compAttribNames.size());
try {
ObjectName oName = new ObjectName(objectName);
if (mbeanServer.isRegistered(oName)) {
AttributeList attrList = mbeanServer.getAttributes(oName, attrNames);
Map<String, JMXDataSource> dsMap = nodeInfo.getDsMap();
for(Object attribute : attrList) {
List<String> compositeMemberKeys = new ArrayList<String>();
Boolean isComposite = false;
Attribute attrib=(Attribute)attribute;
for (String compAttrName : compAttribNames ) {
String[] attribKeys = compAttrName.split("\\|", -1);
if (attrib.getName().equals(attribKeys[0])) {
compositeMemberKeys.add(attribKeys[1]);
isComposite = true;
}
}
if (isComposite) {
try {
CompositeData cd = (CompositeData)attrib.getValue();
for (String key : compositeMemberKeys) {
/*
value = cd.get(key);
log.debug(" JMXCollector - got CompositeData: " +
objectName + "|" + attrib.getName() + "|" + key + " |-> " + cd.get(key).toString());
*/
JMXDataSource ds = dsMap.get(objectName + "|" + attrib.getName() + "|" + key);
JMXCollectionAttributeType attribType=new JMXCollectionAttributeType(ds, null, null, attribGroupType);
collectionResource.setAttributeValue(attribType, cd.get(key).toString());
}
} catch (final ClassCastException cce) {
LOG.debug("{} Collection - getAttributes (try CompositeData) - ERROR: Failed to cast attribute value to type CompositeData!", serviceName, cce);
}
}
else {
// this is a normal attribute, so fallback to default handler
JMXDataSource ds = dsMap.get(objectName + "|" + attrib.getName());
JMXCollectionAttributeType attribType=new JMXCollectionAttributeType(ds, null, null, attribGroupType);
collectionResource.setAttributeValue(attribType, attrib.getValue().toString());
}
}
}
} catch (final InstanceNotFoundException e) {
LOG.error("Unable to retrieve attributes from {}", objectName, e);
}
} else {
/*
* This section is for ObjectNames that use the
* '*' wildcard
*/
Set<ObjectName> mbeanSet = getObjectNames(mbeanServer, objectName);
for (Iterator<ObjectName> objectNameIter = mbeanSet.iterator(); objectNameIter.hasNext(); ) {
ObjectName oName = objectNameIter.next();
LOG.debug("{} Collector - getAttributesWC: {}, # attributes: {}, alias: {}", serviceName, oName, attrNames.length, beanInfo.getKeyAlias());
try {
if (excludeList == null) {
// the exclude list doesn't apply
if (mbeanServer.isRegistered(oName)) {
AttributeList attrList = mbeanServer.getAttributes(oName,
attrNames);
Map<String, JMXDataSource> dsMap = nodeInfo.getDsMap();
for(Object attribute : attrList) {
Attribute attrib=(Attribute)attribute;
JMXDataSource ds = dsMap.get(objectName + "|"
+ attrib.getName());
JMXCollectionAttributeType attribType=
new JMXCollectionAttributeType(ds,
oName.getKeyProperty(beanInfo.getKeyField()),
beanInfo.getKeyAlias(),
attribGroupType);
collectionResource.setAttributeValue(attribType, attrib.getValue().toString());
}
}
} else {
/*
* filter out calls if the key field
* matches an entry in the exclude
* list
*/
String keyName = oName.getKeyProperty(beanInfo.getKeyField());
boolean found = false;
StringTokenizer st = new StringTokenizer(
excludeList,
",");
while (st.hasMoreTokens()) {
if (keyName.equals(st.nextToken())) {
found = true;
break;
}
}
if (!found) {
if (mbeanServer.isRegistered(oName)) {
AttributeList attrList = mbeanServer.getAttributes(oName, attrNames);
Map<String, JMXDataSource> dsMap = nodeInfo.getDsMap();
for(Object attribute : attrList) {
Attribute attrib=(Attribute)attribute;
JMXDataSource ds = dsMap.get(objectName + "|" + attrib.getName());
JMXCollectionAttributeType attribType = new JMXCollectionAttributeType(ds,
oName.getKeyProperty(beanInfo.getKeyField()),
beanInfo.getKeyAlias(),
attribGroupType);
collectionResource.setAttributeValue(attribType, attrib.getValue().toString());
}
}
}
}
} catch (final InstanceNotFoundException e) {
LOG.error("Error retrieving attributes for {}", oName, e);
}
}
}
}
break;
} catch (final Exception e) {
LOG.debug("{} Collector.collect: IOException while collecting address: {}", serviceName, agent.getAddress(), e);
}
}
} catch (final Exception e) {
LOG.error("Error getting MBeanServer", e);
} finally {
if (connection != null) {
connection.close();
}
}
collectionSet.setStatus(ServiceCollector.COLLECTION_SUCCEEDED);
return collectionSet;
}
private static Set<ObjectName> getObjectNames(MBeanServerConnection mbeanServer, String objectName) throws IOException,
MalformedObjectNameException {
return mbeanServer.queryNames(new ObjectName(objectName), null);
}
/**
* This method removes characters from an object name that are
* potentially illegal in a file or directory name, returning a
* name that is appropriate for use with the storeByGroup persistence
* method.
*
* @param objectName
* @return
*/
private static String fixGroupName(String objectName) {
if (objectName == null) {
return "NULL";
}
return AlphaNumeric.parseAndReplace(objectName, '_');
}
/*
* This method strips out the illegal character '/' and attempts to keep
* the length of the key plus ds name to 19 or less characters. The slash
* character cannot be in the name since it is an illegal character in
* file names.
*/
private static String fixKey(String key, String attrName, String substitutions) {
String newKey = key;
if (key.startsWith(File.separator)) {
newKey = key.substring(1);
}
if (substitutions != null && substitutions.length() > 0) {
StringTokenizer st = new StringTokenizer(substitutions, ",");
while (st.hasMoreTokens()) {
String token = st.nextToken();
int index = token.indexOf('|');
if (newKey.equals(token.substring(0, index))) {
newKey = token.substring(index + 1);
}
}
}
return newKey;
}
/**
* This method is responsible for building a list of RRDDataSource objects
* from the provided list of MBeanObject objects.
*
* @param collectionName
* Collection name
* @param oidList
* List of MBeanObject objects defining the oid's to be
* collected via JMX.
* @return list of RRDDataSource objects
*/
protected static Map<String, JMXDataSource> buildDataSourceList(String collectionName, Map<String, List<Attrib>> attributeMap) {
LOG.debug("buildDataSourceList - ***");
/*
* Retrieve the RRD expansion data source list which contains all
* the expansion data source's. Use this list as a basis
* for building a data source list for the current interface.
*/
HashMap<String, JMXDataSource> dsList = new HashMap<String, JMXDataSource>();
/*
* Loop through the MBean object list to be collected for this
* interface and add a corresponding RRD data source object. In this
* manner each interface will have RRD files create which reflect only
* the data sources pertinent to it.
*/
LOG.debug("attributeMap size: {}", attributeMap.size());
Iterator<String> objNameIter = attributeMap.keySet().iterator();
while (objNameIter.hasNext()) {
String objectName = objNameIter.next().toString();
List<Attrib> list = attributeMap.get(objectName);
LOG.debug("ObjectName: {}, Attributes: {}", objectName, list.size());
Iterator<Attrib> iter = list.iterator();
while (iter.hasNext()) {
Attrib attr = iter.next();
JMXDataSource ds = null;
/*
* Verify that this object has an appropriate "integer" data
* type which can be stored in an RRD database file (must map to
* one of the supported RRD data source types: COUNTER or GAUGE).
* */
String ds_type = JMXDataSource.mapType(attr.getType());
if (ds_type != null) {
/*
* Passed!! Create new data source instance for this MBean
* object.
* Assign heartbeat using formula (2 * step) and hard code
* min & max values to "U" ("unknown").
*/
ds = new JMXDataSource();
ds.setHeartbeat(2 * JMXDataCollectionConfigFactory.getInstance().getStep(
collectionName));
// For completeness, adding a minval option to the variable.
String ds_minval = attr.getMinval();
if (ds_minval == null) {
ds_minval = "U";
}
ds.setMax(ds_minval);
/*
* In order to handle counter wraps, we need to set a max
* value for the variable.
*/
String ds_maxval = attr.getMaxval();
if (ds_maxval == null) {
ds_maxval = "U";
}
ds.setMax(ds_maxval);
ds.setInstance(collectionName);
/*
* Truncate MBean object name/alias if it exceeds 19 char
* max for RRD data source names.
*/
String ds_name = attr.getAlias();
if (ds_name.length() > MAX_DS_NAME_LENGTH) {
LOG.warn("buildDataSourceList: alias '{}' exceeds 19 char maximum for RRD data source names, truncating.", attr.getAlias());
char[] temp = ds_name.toCharArray();
ds_name = String.copyValueOf(temp, 0,
MAX_DS_NAME_LENGTH);
}
ds.setName(ds_name);
// Map MBean object data type to RRD data type
ds.setType(ds_type);
/*
* Assign the data source object identifier and instance
* ds.setName(attr.getName());
*/
ds.setOid(attr.getName());
LOG.debug("buildDataSourceList: ds_name: {} ds_oid: {}.{} ds_max: {} ds_min: {}", ds.getName(), ds.getOid(), ds.getInstance(), ds.getMax(), ds.getMin());
// Add the new data source to the list
dsList.put(objectName + "|" + attr.getName(), ds);
} else {
LOG.warn("buildDataSourceList: Data type '{}' not supported. Only integer-type data may be stored in RRD. MBean object '{}' will not be mapped to RRD data source.", attr.getType(), attr.getAlias());
}
}
}
return dsList;
}
/**
* <p>Setter for the field <code>useFriendlyName</code>.</p>
*
* @param useFriendlyName a boolean.
*/
public void setUseFriendlyName(boolean useFriendlyName) {
this.useFriendlyName = useFriendlyName;
}
private static class JMXCollectionAttributeType implements CollectionAttributeType {
private final JMXDataSource m_dataSource;
private final AttributeGroupType m_groupType;
private final String m_name;
public JMXCollectionAttributeType(JMXDataSource dataSource, String key, String substitutions, AttributeGroupType groupType) {
m_groupType=groupType;
m_dataSource=dataSource;
m_name=createName(key,substitutions);
}
private String createName(String key, String substitutions) {
String name=m_dataSource.getName();
if(key!=null && !key.equals("")) {
name=fixKey(key, m_dataSource.getName(),substitutions)+"_"+name;
}
return name;
}
@Override
public AttributeGroupType getGroupType() {
return m_groupType;
}
@Override
public void storeAttribute(CollectionAttribute attribute, Persister persister) {
//Only numeric data comes back from JMX in data collection
persister.persistNumericAttribute(attribute);
}
@Override
public String getName() {
return m_name;
}
@Override
public String getType() {
return m_dataSource.getType();
}
}
private static class JMXCollectionAttribute extends AbstractCollectionAttribute {
private final String m_alias;
private final String m_value;
private final JMXCollectionResource m_resource;
private final CollectionAttributeType m_attribType;
JMXCollectionAttribute(JMXCollectionResource resource, CollectionAttributeType attribType, String alias, String value) {
super();
m_resource=resource;
m_attribType=attribType;
m_alias = alias;
m_value = value;
}
@Override
public CollectionAttributeType getAttributeType() {
return m_attribType;
}
@Override
public String getName() {
return m_alias;
}
@Override
public String getNumericValue() {
return m_value;
}
@Override
public CollectionResource getResource() {
return m_resource;
}
@Override
public String getStringValue() {
return m_value;
}
@Override
public String getType() {
return m_attribType.getType();
}
@Override
public String toString() {
return "alias " + m_alias + ", value " + m_value + ", resource "
+ m_resource + ", attributeType " + m_attribType;
}
@Override
public String getMetricIdentifier() {
String metricId = m_attribType.getGroupType().getName();
metricId = metricId.replace("_type_", ":type=");
metricId = metricId.replace("_", ".");
metricId = metricId.concat(".");
metricId = metricId.concat(getName());
return "JMX_".concat(metricId);
}
}
public static class JMXCollectionResource extends AbstractCollectionResource {
private final String m_resourceName;
private final int m_nodeId;
public JMXCollectionResource(CollectionAgent agent, String resourceName) {
super(agent);
m_resourceName=resourceName;
m_nodeId = agent.getNodeId();
}
@Override
public String toString() {
return "node["+m_nodeId+']';
}
@Override
public int getType() {
return -1; //Is this correct?
}
public void setAttributeValue(CollectionAttributeType type, String value) {
JMXCollectionAttribute attr = new JMXCollectionAttribute(this, type, type.getName(), value);
addAttribute(attr);
}
@Override
public File getResourceDir(RrdRepository repository) {
return new File(repository.getRrdBaseDir(), getParent() + File.separator + m_resourceName);
}
@Override
public String getResourceTypeName() {
return "node"; //All node resources for JMX; nothing of interface or "indexed resource" type
}
@Override
public String getInstance() {
return null; //For node type resources, use the default instance
}
}
public static class JMXCollectionSet extends AbstractCollectionSet {
private int m_status;
private Date m_timestamp;
private final JMXCollectionResource m_collectionResource;
public JMXCollectionSet(CollectionAgent agent, String resourceName) {
m_status=ServiceCollector.COLLECTION_FAILED;
m_collectionResource=new JMXCollectionResource(agent, resourceName);
}
public JMXCollectionResource getResource() {
return m_collectionResource;
}
public void setStatus(int status) {
m_status=status;
}
@Override
public int getStatus() {
return m_status;
}
@Override
public void visit(CollectionSetVisitor visitor) {
visitor.visitCollectionSet(this);
m_collectionResource.visit(visitor);
visitor.completeCollectionSet(this);
}
@Override
public Date getCollectionTimestamp() {
return m_timestamp;
}
public void setCollectionTimestamp(Date timestamp) {
this.m_timestamp = timestamp;
}
}
/** {@inheritDoc} */
@Override
public RrdRepository getRrdRepository(String collectionName) {
return JMXDataCollectionConfigFactory.getInstance().getRrdRepository(collectionName);
}
}