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
214 if (attribute.isReadable() && !"password".equalsIgnoreCase(attribute.getName())
215 && !"configurationAsProperties".equalsIgnoreCase(attribute.getName())) {
216 attributeNames.add(attribute.getName());
217 }
218 }
219 final String[] attributeNamesArray = attributeNames.toArray(new String[0]);
220 final List<MBeanAttribute> result = new ArrayList<>();
221 try {
222
223 final List<Object> attributes = mbeanServer.getAttributes(name, attributeNamesArray);
224 for (final Object object : attributes) {
225 final Attribute attribute = (Attribute) object;
226 final Object value = convertValueIfNeeded(attribute.getValue());
227 final String attributeDescription = getAttributeDescription(attribute.getName(),
228 attributeInfos);
229 final String formattedAttributeValue = formatAttributeValue(value);
230 final MBeanAttribute mbeanAttribute = new MBeanAttribute(attribute.getName(),
231 attributeDescription, formattedAttributeValue);
232 result.add(mbeanAttribute);
233 }
234 } catch (final Exception e) {
235
236 final MBeanAttribute mbeanAttribute = new MBeanAttribute("exception", null,
237 e.toString());
238 result.add(mbeanAttribute);
239 }
240 return result;
241 }
242
243 private String formatAttributeValue(Object attributeValue) {
244 try {
245 if (attributeValue instanceof List) {
246 final StringBuilder sb = new StringBuilder();
247 sb.append('[');
248 boolean first = true;
249 for (final Object value : (List<?>) attributeValue) {
250 if (first) {
251 first = false;
252 } else {
253 sb.append(",\n");
254 }
255 if (attributeValue instanceof Number) {
256 sb.append(I18N.createIntegerFormat().format(attributeValue));
257 } else {
258 sb.append(value);
259 }
260 }
261 sb.append(']');
262 return sb.toString();
263 } else if (attributeValue instanceof Map) {
264 @SuppressWarnings("unchecked")
265 final Map<Object, Object> map = (Map<Object, Object>) attributeValue;
266 final LinkedHashMap<Object, Object> mapToString = new LinkedHashMap<>();
267 for (final Entry<Object, Object> e : map.entrySet()) {
268 final Object v = e.getValue();
269 if (v instanceof Number) {
270 mapToString.put(e.getKey(), I18N.createIntegerFormat().format(v));
271 } else {
272 mapToString.put(e.getKey(), attributeValue);
273 }
274 }
275 return mapToString.toString();
276 } else if (attributeValue instanceof Number) {
277 return I18N.createIntegerFormat().format(attributeValue);
278 }
279 return String.valueOf(attributeValue);
280 } catch (final Exception e) {
281 return e.toString();
282 }
283 }
284
285 private String formatDescription(String description) {
286
287 if (description == null || JAVA_LANG_MBEAN_DESCRIPTION.equals(description)) {
288 return null;
289 }
290 int indexOf = description.indexOf(" ");
291 if (indexOf != -1) {
292
293 final StringBuilder sb = new StringBuilder(description);
294 while (indexOf != -1) {
295 sb.deleteCharAt(indexOf);
296 indexOf = sb.indexOf(" ");
297 }
298 return sb.toString();
299 }
300 return description;
301 }
302
303 private Object convertValueIfNeeded(Object value) {
304 if (value instanceof CompositeData) {
305 final CompositeData data = (CompositeData) value;
306 final Map<String, Object> values = new TreeMap<>();
307 for (final String key : data.getCompositeType().keySet()) {
308 values.put(key, convertValueIfNeeded(data.get(key)));
309 }
310 return values;
311 } else if (value instanceof CompositeData[]) {
312 final List<Object> list = new ArrayList<>();
313 for (final CompositeData data : (CompositeData[]) value) {
314 list.add(convertValueIfNeeded(data));
315 }
316 return list;
317 } else if (value instanceof Object[]) {
318 return Arrays.asList((Object[]) value);
319 } else if (value instanceof TabularData) {
320 final TabularData tabularData = (TabularData) value;
321 return convertValueIfNeeded(tabularData.values());
322 } else if (value instanceof Collection) {
323 final List<Object> list = new ArrayList<>();
324 for (final Object data : (Collection<?>) value) {
325 list.add(convertValueIfNeeded(data));
326 }
327 return list;
328 }
329 return convertJRockitValueIfNeeded(value);
330 }
331
332 private static Object convertJRockitValueIfNeeded(Object value) {
333 if (value instanceof double[]) {
334
335 final List<Double> list = new ArrayList<>();
336 for (final double data : (double[]) value) {
337 list.add(data);
338 }
339 return list;
340 } else if (value instanceof int[]) {
341
342 final List<Integer> list = new ArrayList<>();
343 for (final int data : (int[]) value) {
344 list.add(data);
345 }
346 return list;
347 }
348 return value;
349 }
350
351 private static List<Object> getConvertedAttributes(List<String> mbeanAttributes) {
352 initJRockitMBeansIfNeeded();
353
354 final List<Object> result = new ArrayList<>();
355 final List<MBeanServer> mBeanServers = getMBeanServers();
356 for (final String mbeansAttribute : mbeanAttributes) {
357 final int lastIndexOfPoint = mbeansAttribute.lastIndexOf('.');
358 if (lastIndexOfPoint <= 0) {
359 throw new IllegalArgumentException(mbeansAttribute);
360 }
361 final String name = mbeansAttribute.substring(0, lastIndexOfPoint);
362 final String attribute = mbeansAttribute.substring(lastIndexOfPoint + 1);
363
364
365 if ("password".equalsIgnoreCase(attribute)) {
366 throw new IllegalArgumentException(name + '.' + attribute);
367 }
368 InstanceNotFoundException instanceNotFoundException = null;
369 for (final MBeanServer mbeanServer : mBeanServers) {
370 try {
371 final MBeans mbeans = new MBeans(mbeanServer);
372 final Object jmxValue = mbeans.convertValueIfNeeded(
373 mbeans.getAttribute(new ObjectName(name), attribute));
374 result.add(jmxValue);
375 instanceNotFoundException = null;
376
377
378 break;
379 } catch (final InstanceNotFoundException e) {
380
381
382 instanceNotFoundException = e;
383 continue;
384 } catch (final JMException e) {
385 throw new IllegalArgumentException(name + '.' + attribute, e);
386 }
387 }
388 if (instanceNotFoundException != null) {
389 throw new IllegalArgumentException(name + '.' + attribute,
390 instanceNotFoundException);
391 }
392 }
393 return result;
394 }
395
396 public static String getConvertedAttributes(String jmxValueParameter) {
397 final List<String> mbeanAttributes = Arrays
398 .asList(jmxValueParameter.split("[" + ATTRIBUTES_SEPARATOR + ']'));
399 final List<Object> jmxValues = getConvertedAttributes(mbeanAttributes);
400 final StringBuilder sb = new StringBuilder();
401 boolean first = true;
402 for (final Object jmxValue : jmxValues) {
403 if (first) {
404 first = false;
405 } else {
406 sb.append(ATTRIBUTES_SEPARATOR);
407 }
408 sb.append(jmxValue);
409 }
410 return sb.toString();
411 }
412
413 private String getAttributeDescription(String name, MBeanAttributeInfo[] attributeInfos) {
414 for (final MBeanAttributeInfo attributeInfo : attributeInfos) {
415 if (name.equals(attributeInfo.getName())) {
416
417 final String attributeDescription = formatDescription(
418 attributeInfo.getDescription());
419 if (attributeDescription == null || name.equals(attributeDescription)
420 || attributeDescription.isEmpty()) {
421
422
423 return null;
424 }
425 return attributeDescription;
426 }
427 }
428 return null;
429 }
430
431
435 public static MBeanServer getPlatformMBeanServer() {
436 return ManagementFactory.getPlatformMBeanServer();
437
438
439
440
441
442
443
444
445 }
446
447
451 private static List<MBeanServer> getMBeanServers() {
452
453
454 return MBeanServerFactory.findMBeanServer(null);
455 }
456 }
457