1
18 package net.bull.javamelody.internal.model;
19
20 import java.lang.management.ManagementFactory;
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.LinkedHashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.Set;
31 import java.util.TreeMap;
32
33 import javax.management.Attribute;
34 import javax.management.InstanceNotFoundException;
35 import javax.management.JMException;
36 import javax.management.MBeanAttributeInfo;
37 import javax.management.MBeanInfo;
38 import javax.management.MBeanServer;
39 import javax.management.MBeanServerFactory;
40 import javax.management.ObjectName;
41 import javax.management.openmbean.CompositeData;
42 import javax.management.openmbean.TabularData;
43
44 import net.bull.javamelody.internal.common.I18N;
45 import net.bull.javamelody.internal.common.LOG;
46 import net.bull.javamelody.internal.model.MBeanNode.MBeanAttribute;
47
48
53 public final class MBeans {
54
57 public static final char ATTRIBUTES_SEPARATOR = '|';
58
59 private static final String JAVA_LANG_MBEAN_DESCRIPTION = "Information on the management interface of the MBean";
60 private static final Comparator<MBeanNode> NODE_COMPARATOR = new Comparator<MBeanNode>() {
61 @Override
62 public int compare(MBeanNode o1, MBeanNode o2) {
63 return o1.getName() != null ? o1.getName().compareTo(o2.getName()) : 0;
64 }
65 };
66 private static final Comparator<MBeanAttribute> ATTRIBUTE_COMPARATOR = new Comparator<MBeanAttribute>() {
67 @Override
68 public int compare(MBeanAttribute o1, MBeanAttribute o2) {
69 return o1.getName().compareTo(o2.getName());
70 }
71 };
72 private final MBeanServer mbeanServer;
73
74 MBeans() {
75 this(getPlatformMBeanServer());
76 }
77
78 private MBeans(MBeanServer mbeanServer) {
79 super();
80 this.mbeanServer = mbeanServer;
81 }
82
83 Object getAttribute(ObjectName name, String attribute) throws JMException {
84 return mbeanServer.getAttribute(name, attribute);
85 }
86
87 public static List<MBeanNode> getAllMBeanNodes() throws JMException {
88 initJRockitMBeansIfNeeded();
89
90 final List<MBeanNode> result = new ArrayList<>();
91 final MBeanServer platformMBeanServer = getPlatformMBeanServer();
92 final MBeanNode platformNode = new MBeanNode("");
93
94 final MBeans platformMBeans = new MBeans();
95 platformNode.getChildren().addAll(platformMBeans.getMBeanNodes());
96 result.add(platformNode);
97
98
99 for (final MBeanServer mbeanServer : getMBeanServers()) {
100 if (!mbeanServer.equals(platformMBeanServer)) {
101 final MBeanNode node = new MBeanNode(mbeanServer.getDefaultDomain());
102 final MBeans mbeans = new MBeans(mbeanServer);
103 node.getChildren().addAll(mbeans.getMBeanNodes());
104 result.add(node);
105 }
106 }
107 return result;
108 }
109
110 private static void initJRockitMBeansIfNeeded() {
111
112 if (System.getProperty("java.vendor").contains("BEA")) {
113 try {
114
115 try {
116 getPlatformMBeanServer().getMBeanInfo(
117 new ObjectName("bea.jrockit.management:type=JRockitConsole"));
118 } catch (final InstanceNotFoundException e1) {
119 getPlatformMBeanServer().createMBean("bea.jrockit.management.JRockitConsole",
120 null);
121 LOG.debug("JRockit MBeans initialized");
122 }
123 } catch (final JMException e) {
124 throw new IllegalStateException(e);
125 }
126 }
127 }
128
129 private List<MBeanNode> getMBeanNodes() throws JMException {
130 final List<MBeanNode> result = new ArrayList<>();
131 final Set<ObjectName> names = mbeanServer.queryNames(null, null);
132 for (final ObjectName name : names) {
133 final String domain = name.getDomain();
134 if ("jboss.deployment".equals(domain)) {
135
136 continue;
137 }
138 MBeanNode domainNode = getMBeanNodeFromList(result, domain);
139 if (domainNode == null) {
140 domainNode = new MBeanNode(domain);
141 result.add(domainNode);
142 }
143 final String keyPropertyListString = name.getKeyPropertyListString();
144 final String firstPropertyValue;
145 final int indexOf = keyPropertyListString.indexOf('=');
146 if (indexOf == -1) {
147
148 firstPropertyValue = null;
149 } else {
150 firstPropertyValue = name
151 .getKeyProperty(keyPropertyListString.substring(0, indexOf));
152 }
153 MBeanNode firstPropertyNode = getMBeanNodeFromList(domainNode.getChildren(),
154 firstPropertyValue);
155 if (firstPropertyNode == null) {
156 firstPropertyNode = new MBeanNode(firstPropertyValue);
157 domainNode.getChildren().add(firstPropertyNode);
158 }
159 try {
160 final MBeanNode mbean = getMBeanNode(name);
161 firstPropertyNode.getChildren().add(mbean);
162 } catch (final IllegalStateException e) {
163
164 continue;
165 }
166 }
167 sortMBeanNodes(result);
168 return result;
169 }
170
171 private void sortMBeanNodes(List<MBeanNode> nodes) {
172 if (nodes.size() > 1) {
173 Collections.sort(nodes, NODE_COMPARATOR);
174 }
175
176 for (final MBeanNode node : nodes) {
177 final List<MBeanNode> children = node.getChildren();
178 if (children != null) {
179 sortMBeanNodes(children);
180 }
181 final List<MBeanAttribute> attributes = node.getAttributes();
182 if (attributes != null && attributes.size() > 1) {
183 Collections.sort(attributes, ATTRIBUTE_COMPARATOR);
184 }
185 }
186 }
187
188 private static MBeanNode getMBeanNodeFromList(List<MBeanNode> list, String name) {
189 for (final MBeanNode node : list) {
190 if (node.getName().equals(name)) {
191 return node;
192 }
193 }
194 return null;
195 }
196
197 private MBeanNode getMBeanNode(ObjectName name) throws JMException {
198 final String mbeanName = name.toString();
199 final MBeanInfo mbeanInfo = mbeanServer.getMBeanInfo(name);
200 final String description = formatDescription(mbeanInfo.getDescription());
201 final MBeanAttributeInfo[] attributeInfos = mbeanInfo.getAttributes();
202 final List<MBeanAttribute> attributes = getAttributes(name, attributeInfos);
203
204 return new MBeanNode(mbeanName, description, attributes);
205 }
206
207 private List<MBeanAttribute> getAttributes(ObjectName name,
208 MBeanAttributeInfo[] attributeInfos) {
209 final List<String> attributeNames = new ArrayList<>(attributeInfos.length);
210 for (final MBeanAttributeInfo attribute : attributeInfos) {
211
212
213 if (attribute.isReadable() && !"password".equalsIgnoreCase(attribute.getName())) {
214 attributeNames.add(attribute.getName());
215 }
216 }
217 final String[] attributeNamesArray = attributeNames.toArray(new String[0]);
218 final List<MBeanAttribute> result = new ArrayList<>();
219 try {
220
221 final List<Object> attributes = mbeanServer.getAttributes(name, attributeNamesArray);
222 for (final Object object : attributes) {
223 final Attribute attribute = (Attribute) object;
224 final Object value = convertValueIfNeeded(attribute.getValue());
225 final String attributeDescription = getAttributeDescription(attribute.getName(),
226 attributeInfos);
227 final String formattedAttributeValue = formatAttributeValue(value);
228 final MBeanAttribute mbeanAttribute = new MBeanAttribute(attribute.getName(),
229 attributeDescription, formattedAttributeValue);
230 result.add(mbeanAttribute);
231 }
232 } catch (final Exception e) {
233
234 final MBeanAttribute mbeanAttribute = new MBeanAttribute("exception", null,
235 e.toString());
236 result.add(mbeanAttribute);
237 }
238 return result;
239 }
240
241 private String formatAttributeValue(Object attributeValue) {
242 try {
243 if (attributeValue instanceof List) {
244 final StringBuilder sb = new StringBuilder();
245 sb.append('[');
246 boolean first = true;
247 for (final Object value : (List<?>) attributeValue) {
248 if (first) {
249 first = false;
250 } else {
251 sb.append(",\n");
252 }
253 if (attributeValue instanceof Number) {
254 sb.append(I18N.createIntegerFormat().format(attributeValue));
255 } else {
256 sb.append(value);
257 }
258 }
259 sb.append(']');
260 return sb.toString();
261 } else if (attributeValue instanceof Map) {
262 @SuppressWarnings("unchecked")
263 final Map<Object, Object> map = (Map<Object, Object>) attributeValue;
264 final LinkedHashMap<Object, Object> mapToString = new LinkedHashMap<>();
265 for (final Entry<Object, Object> e : map.entrySet()) {
266 final Object v = e.getValue();
267 if (v instanceof Number) {
268 mapToString.put(e.getKey(), I18N.createIntegerFormat().format(v));
269 } else {
270 mapToString.put(e.getKey(), attributeValue);
271 }
272 }
273 return mapToString.toString();
274 } else if (attributeValue instanceof Number) {
275 return I18N.createIntegerFormat().format(attributeValue);
276 }
277 return String.valueOf(attributeValue);
278 } catch (final Exception e) {
279 return e.toString();
280 }
281 }
282
283 private String formatDescription(String description) {
284
285 if (description == null || JAVA_LANG_MBEAN_DESCRIPTION.equals(description)) {
286 return null;
287 }
288 int indexOf = description.indexOf(" ");
289 if (indexOf != -1) {
290
291 final StringBuilder sb = new StringBuilder(description);
292 while (indexOf != -1) {
293 sb.deleteCharAt(indexOf);
294 indexOf = sb.indexOf(" ");
295 }
296 return sb.toString();
297 }
298 return description;
299 }
300
301 private Object convertValueIfNeeded(Object value) {
302 if (value instanceof CompositeData) {
303 final CompositeData data = (CompositeData) value;
304 final Map<String, Object> values = new TreeMap<>();
305 for (final String key : data.getCompositeType().keySet()) {
306 values.put(key, convertValueIfNeeded(data.get(key)));
307 }
308 return values;
309 } else if (value instanceof CompositeData[]) {
310 final List<Object> list = new ArrayList<>();
311 for (final CompositeData data : (CompositeData[]) value) {
312 list.add(convertValueIfNeeded(data));
313 }
314 return list;
315 } else if (value instanceof Object[]) {
316 return Arrays.asList((Object[]) value);
317 } else if (value instanceof TabularData) {
318 final TabularData tabularData = (TabularData) value;
319 return convertValueIfNeeded(tabularData.values());
320 } else if (value instanceof Collection) {
321 final List<Object> list = new ArrayList<>();
322 for (final Object data : (Collection<?>) value) {
323 list.add(convertValueIfNeeded(data));
324 }
325 return list;
326 }
327 return convertJRockitValueIfNeeded(value);
328 }
329
330 private static Object convertJRockitValueIfNeeded(Object value) {
331 if (value instanceof double[]) {
332
333 final List<Double> list = new ArrayList<>();
334 for (final double data : (double[]) value) {
335 list.add(data);
336 }
337 return list;
338 } else if (value instanceof int[]) {
339
340 final List<Integer> list = new ArrayList<>();
341 for (final int data : (int[]) value) {
342 list.add(data);
343 }
344 return list;
345 }
346 return value;
347 }
348
349 private static List<Object> getConvertedAttributes(List<String> mbeanAttributes) {
350 initJRockitMBeansIfNeeded();
351
352 final List<Object> result = new ArrayList<>();
353 final List<MBeanServer> mBeanServers = getMBeanServers();
354 for (final String mbeansAttribute : mbeanAttributes) {
355 final int lastIndexOfPoint = mbeansAttribute.lastIndexOf('.');
356 if (lastIndexOfPoint <= 0) {
357 throw new IllegalArgumentException(mbeansAttribute);
358 }
359 final String name = mbeansAttribute.substring(0, lastIndexOfPoint);
360 final String attribute = mbeansAttribute.substring(lastIndexOfPoint + 1);
361
362
363 if ("password".equalsIgnoreCase(attribute)) {
364 throw new IllegalArgumentException(name + '.' + attribute);
365 }
366 InstanceNotFoundException instanceNotFoundException = null;
367 for (final MBeanServer mbeanServer : mBeanServers) {
368 try {
369 final MBeans mbeans = new MBeans(mbeanServer);
370 final Object jmxValue = mbeans.convertValueIfNeeded(
371 mbeans.getAttribute(new ObjectName(name), attribute));
372 result.add(jmxValue);
373 instanceNotFoundException = null;
374
375
376 break;
377 } catch (final InstanceNotFoundException e) {
378
379
380 instanceNotFoundException = e;
381 continue;
382 } catch (final JMException e) {
383 throw new IllegalArgumentException(name + '.' + attribute, e);
384 }
385 }
386 if (instanceNotFoundException != null) {
387 throw new IllegalArgumentException(name + '.' + attribute,
388 instanceNotFoundException);
389 }
390 }
391 return result;
392 }
393
394 public static String getConvertedAttributes(String jmxValueParameter) {
395 final List<String> mbeanAttributes = Arrays
396 .asList(jmxValueParameter.split("[" + ATTRIBUTES_SEPARATOR + ']'));
397 final List<Object> jmxValues = getConvertedAttributes(mbeanAttributes);
398 final StringBuilder sb = new StringBuilder();
399 boolean first = true;
400 for (final Object jmxValue : jmxValues) {
401 if (first) {
402 first = false;
403 } else {
404 sb.append(ATTRIBUTES_SEPARATOR);
405 }
406 sb.append(jmxValue);
407 }
408 return sb.toString();
409 }
410
411 private String getAttributeDescription(String name, MBeanAttributeInfo[] attributeInfos) {
412 for (final MBeanAttributeInfo attributeInfo : attributeInfos) {
413 if (name.equals(attributeInfo.getName())) {
414
415 final String attributeDescription = formatDescription(
416 attributeInfo.getDescription());
417 if (attributeDescription == null || name.equals(attributeDescription)
418 || attributeDescription.isEmpty()) {
419
420
421 return null;
422 }
423 return attributeDescription;
424 }
425 }
426 return null;
427 }
428
429
433 public static MBeanServer getPlatformMBeanServer() {
434 return ManagementFactory.getPlatformMBeanServer();
435
436
437
438
439
440
441
442
443 }
444
445
449 private static List<MBeanServer> getMBeanServers() {
450
451
452 return MBeanServerFactory.findMBeanServer(null);
453 }
454 }
455