1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package ch.elca.el4j.services.monitoring.jmx;
19
20 import java.beans.IntrospectionException;
21 import java.beans.Introspector;
22 import java.beans.PropertyDescriptor;
23 import java.lang.reflect.Method;
24 import java.util.Iterator;
25
26 import javax.management.InstanceAlreadyExistsException;
27 import javax.management.MBeanRegistrationException;
28 import javax.management.MBeanServer;
29 import javax.management.MalformedObjectNameException;
30 import javax.management.NotCompliantMBeanException;
31 import javax.management.ObjectName;
32
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35 import org.springframework.aop.framework.Advised;
36 import org.springframework.beans.MutablePropertyValues;
37 import org.springframework.beans.PropertyValue;
38 import org.springframework.beans.factory.BeanFactory;
39 import org.springframework.beans.factory.config.BeanDefinition;
40 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
41 import org.springframework.context.ApplicationContext;
42
43 import ch.elca.el4j.core.exceptions.BaseException;
44 import ch.elca.el4j.core.exceptions.BaseRTException;
45 import ch.elca.el4j.services.monitoring.jmx.display.DisplayManager;
46 import ch.elca.el4j.services.monitoring.jmx.display.HtmlDisplayManager;
47 import ch.elca.el4j.services.monitoring.jmx.display.HtmlTabulator;
48 import ch.elca.el4j.services.monitoring.jmx.display.Section;
49 import ch.elca.el4j.services.monitoring.jmx.util.PropertyReflector;
50 import ch.elca.el4j.services.monitoring.notification.CoreNotificationHelper;
51 import ch.elca.el4j.util.codingsupport.annotations.FindBugsSuppressWarnings;
52
53
54
55
56
57
58
59
60
61 public class SpringBeanMB implements SpringBeanMBMBean {
62
63
64
65
66
67 public static final String SPRING_BEAN_DOMAIN = "SpringBean";
68
69
70
71
72 public static final String MBEAN_DOMAIN = "MBean";
73
74
75
76
77 public static final String MBEAN_KEY = "name";
78
79
80
81
82 private static Logger s_logger = LoggerFactory.getLogger(SpringBeanMB.class);
83
84
85
86
87 protected ApplicationContext m_applicationContext;
88
89
90
91
92 protected ApplicationContextMB m_applicationContextMB;
93
94
95
96
97 protected Class m_class;
98
99
100
101
102 protected BeanFactory m_beanFactory;
103
104
105
106
107 private MBeanServer m_mBeanServer;
108
109
110
111
112 private String m_name;
113
114
115
116
117 private ObjectName m_objectName;
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133 public SpringBeanMB(String name, ApplicationContextMB acMB,
134 ApplicationContext ac, BeanFactory beanFactory,
135 MBeanServer mBeanServer) {
136
137 this.m_name = name;
138 this.m_applicationContextMB = acMB;
139 this.m_applicationContext = ac;
140 this.m_mBeanServer = mBeanServer;
141 this.m_beanFactory = beanFactory;
142 try {
143 this.m_class = ac.getType(name);
144 } catch (NullPointerException e) {
145
146
147 this.m_class = null;
148 }
149
150 }
151
152
153
154
155
156
157
158 public void init() throws BaseException {
159
160
161 setObjectName();
162
163
164 registerSpringBean();
165
166 }
167
168
169
170
171 public void setObjectName() {
172
173 String name = SPRING_BEAN_DOMAIN
174 + m_applicationContextMB.getInstanceCounter() + ":name="
175 + getName();
176
177 try {
178 m_objectName = new ObjectName(name);
179 } catch (MalformedObjectNameException e) {
180 CoreNotificationHelper.notifyMisconfiguration(
181 "The string passed as a parameter does not have"
182 + " the right format.");
183 }
184 }
185
186
187
188
189
190
191 public ObjectName getObjectName() {
192 return m_objectName;
193 }
194
195
196
197
198
199
200
201 protected void registerSpringBean() throws BaseException {
202
203 if (getObjectName() == null) {
204 String message = "The object name of the SpringBeanMB '"
205 + this.toString() + "' should not be null.";
206 s_logger.error(message);
207 throw new BaseRTException(message, (Object[]) null);
208 } else {
209 try {
210 m_mBeanServer.registerMBean(this, getObjectName());
211 } catch (InstanceAlreadyExistsException e) {
212 String message = "The MBean is already under the "
213 + "control of the MBean server.";
214 s_logger.error(message);
215 throw new BaseException(message, e);
216 } catch (MBeanRegistrationException e) {
217 String message = "The MBean will not be registered.";
218 s_logger.error(message);
219 throw new BaseException(message, e);
220 } catch (NotCompliantMBeanException e) {
221 String message = "This object is not a JMX compliant"
222 + " MBean.";
223 s_logger.error(message);
224 throw new BaseException(message, e);
225 }
226 }
227 }
228
229
230
231
232
233
234 public MBeanServer getMBeanServer() {
235 return m_mBeanServer;
236 }
237
238
239
240
241 public String getName() {
242 return m_name;
243 }
244
245
246
247
248 private BeanDefinition getDefinition() {
249 DefaultListableBeanFactory dlbf
250 = (DefaultListableBeanFactory) m_beanFactory;
251 return dlbf.getBeanDefinition(getName());
252 }
253
254
255
256
257 public String[] getConfiguration() {
258
259
260 MutablePropertyValues mpv = getDefinition().getPropertyValues();
261
262 PropertyValue[] pv = mpv.getPropertyValues();
263 String[] result = new String[pv.length];
264
265 for (int i = 0; i < pv.length; i++) {
266 result[i] = pv[i].getName() + " = " + pv[i].getValue().toString();
267 }
268
269 return result;
270 }
271
272
273
274
275 public ObjectName getApplicationContextMB() {
276 return m_applicationContextMB.getObjectName();
277 }
278
279
280
281
282 public ObjectName[] getRegisteredMBean() {
283
284 Loader loader = new Loader();
285
286 return loader.getObjectNames(getMBeanServer(), MBEAN_DOMAIN, MBEAN_KEY,
287 getName());
288 }
289
290
291
292
293 public String getClassName() {
294 return m_class.toString();
295 }
296
297
298
299
300 public boolean getIsSingleton() {
301 return m_applicationContext.isSingleton(getName());
302 }
303
304
305
306
307 public String[] getInterceptors() {
308 String[] interceptorNames = null;
309
310 Object target = m_applicationContext.getBean(m_name);
311 if (target instanceof Advised) {
312 Advised advised = (Advised) target;
313 interceptorNames = new String[advised.getAdvisors().length];
314 for (int i = 0; i < advised.getAdvisors().length; i++) {
315 interceptorNames[i]
316 = advised.getAdvisors()[i].getAdvice().getClass().getName();
317 }
318 }
319
320 return interceptorNames;
321 }
322
323
324
325
326 public boolean getIsProxied() {
327 Object target = m_applicationContext.getBean(m_name);
328 if (target instanceof Advised) {
329 return true;
330 }
331 return false;
332 }
333
334
335 public String getResourceDescription() {
336 return getDefinition().getResourceDescription();
337 }
338
339
340
341
342 public String[] getMethods() {
343 MethodReflector r = new MethodReflector(m_class);
344 String[] result = new String[r.countMethods()];
345 while (r.hasNext()) {
346 r.next();
347 result[r.getPosition()] = r.getCurrentAsString();
348 }
349 return result;
350 }
351
352
353 @FindBugsSuppressWarnings(value = "REC_CATCH_EXCEPTION",
354 justification = "A runtime exception is thrown in any case.")
355 public String[] getProperties() {
356 Class<?> target = m_class;
357
358
359 if (getIsProxied()) {
360 try {
361 Method getTarget = m_class.getMethod("getTargetClass",
362 new Class[0]);
363 Object bean = m_beanFactory.getBean(m_name);
364 target = (Class<?>) getTarget.invoke(bean, (Object[]) null);
365 } catch (Exception e) {
366 throw new RuntimeException("Exception looking up target.");
367 }
368 }
369
370 try {
371 PropertyDescriptor[] pd = Introspector.getBeanInfo(target)
372 .getPropertyDescriptors();
373 String[] properties = new String[pd.length];
374 for (int i = 0; i < pd.length; i++) {
375 PropertyReflector r = new PropertyReflector(pd[i]);
376 properties[i] = MethodReflector.className(r.getType())
377 + " " + r.getName() + " (" + r.getRW() + ")";
378 }
379 return properties;
380 } catch (IntrospectionException e) {
381 throw new RuntimeException("Introspection Exception");
382 }
383 }
384
385
386 public String readProperty(String property) {
387 final String err = "The property could not be read, because ";
388 String result = "";
389
390
391 String propName = property.substring(0, 1).toUpperCase()
392 + property.substring(1);
393
394 try {
395 Object bean = m_beanFactory.getBean(m_name);
396 Method m = null;
397 try {
398 m = bean.getClass()
399 .getMethod("get" + propName, new Class<?>[0]);
400 } catch (NoSuchMethodException e) {
401
402 try {
403 m = bean.getClass().getMethod("is" + propName,
404 new Class<?>[0]);
405 } catch (NoSuchMethodException e2) {
406
407 return err + "no get/is method was found.";
408 }
409 }
410
411
412 Object o = m.invoke(bean, new Object[0]);
413 result = MethodReflector.className(o.getClass().toString())
414 + " " + property + " = " + o.toString();
415 } catch (Exception e) {
416 result = err + "an excpetion occurred: "
417 + e.toString();
418 }
419 return result;
420 }
421
422
423
424
425
426
427 private String propertyTable(PropertyDescriptor[] pd, boolean makeLinks) {
428 String result = "";
429
430 HtmlTabulator table = new HtmlTabulator(
431 "Name", "Type", "RW", "readMethod", "writeMethod");
432
433 for (PropertyDescriptor current : pd) {
434 PropertyReflector r = new PropertyReflector(current);
435
436 String readLink = "";
437 if (r.isReadable()) {
438
439 String readUrl = "/InvokeAction//"
440 + m_objectName.getCanonicalName() + "/action=readProperty"
441 + "?action=readProperty&p1%2Bjava.lang.String="
442 + r.getName();
443
444
445
446
447
448 readLink = makeLinks
449 ? "<a href=\"" + readUrl + "\">"
450 + r.getReadMethod() + "</a>"
451 : r.getReadMethod();
452 }
453
454 table.addRow(r.getName(),
455 MethodReflector.className(r.getType()),
456 "<code>" + r.getRW() + "</code>",
457 readLink,
458 r.isWritable() ? r.getWriteMethod() : "");
459 }
460
461 result += table.tabulate();
462 return result;
463 }
464
465
466
467
468
469 public void displayProperties(DisplayManager manager) {
470
471 Section section = new Section("Properties");
472
473 PropertyDescriptor[] pd;
474 Class<?> target = m_class;
475
476
477 if (getIsProxied()) {
478 section.addLine("This bean is proxied");
479 try {
480 Method getTarget = m_class.getMethod("getTargetClass",
481 new Class[0]);
482 Object bean = m_beanFactory.getBean(m_name);
483 target = (Class<?>) getTarget.invoke(bean, (Object[]) null);
484 } catch (Exception e) {
485 section.addWarning("Could not find target class, "
486 + e.toString());
487 }
488 }
489
490 try {
491 pd = Introspector.getBeanInfo(target).getPropertyDescriptors();
492
493
494 section.add(propertyTable(pd, !getIsProxied()));
495
496 } catch (IntrospectionException e) {
497 section.addWarning("Introspection Exception.");
498 }
499 manager.addSection(section);
500
501 if (getIsProxied()) {
502
503 Section proxySection = new Section("Proxy Properties");
504 try {
505 proxySection.add(propertyTable(Introspector.getBeanInfo(m_class)
506 .getPropertyDescriptors(), true));
507 } catch (IntrospectionException e) {
508 proxySection.addWarning(
509 "Exception looking up proxy properties.");
510 }
511 manager.addSection(proxySection);
512 }
513 }
514
515
516
517
518
519 public void displayConfiguration(DisplayManager manager) {
520 Section section = new Section("Loading Properties");
521
522 HtmlTabulator table = new HtmlTabulator("Name", "Value");
523 for (String current : getConfiguration()) {
524
525
526 int position = current.indexOf("=");
527
528 String key = current.substring(0, position - 1);
529 String value = current.substring(position + 1);
530 table.addRow(key, value);
531 }
532 section.add(table.tabulate());
533 manager.addSection(section);
534 }
535
536
537
538
539 public String introspect() {
540
541 DisplayManager page = new HtmlDisplayManager();
542
543 page.setTitle("Results of introspection on bean "
544 + MethodReflector.className(m_class.toString()));
545
546 Section infoSection = new Section("Bean");
547
548 HtmlTabulator beanInfo = new HtmlTabulator("Item", "Value");
549 beanInfo.addRow("Name", getName());
550 beanInfo.addRow("Proxied", Boolean.toString(getIsProxied()));
551 beanInfo.addRow("Singleton", Boolean.toString(getIsSingleton()));
552 beanInfo.addRow("Defined in", (getResourceDescription() != null)
553 ? getResourceDescription() : "not available");
554
555 infoSection.add(beanInfo.tabulate());
556 infoSection.addLine("");
557
558
559 HtmlTabulator iTable = new HtmlTabulator("Interceptors");
560 String[] interceptors = getInterceptors();
561 if (interceptors == null || interceptors.length == 0) {
562 iTable.addRow("none defined");
563 } else {
564 for (String i : interceptors) {
565 iTable.addRow(i);
566 }
567 }
568 infoSection.add(iTable.tabulate());
569
570 page.addSection(infoSection);
571
572 displayProperties(page);
573
574 displayMethods(page);
575
576 displayConfiguration(page);
577
578 return page.getPage();
579 }
580
581
582
583
584
585 public void displayMethods(DisplayManager manager) {
586 Section section = new Section("Methods");
587 HtmlTabulator table = new HtmlTabulator(
588 "Return", "Name", "Parameters", "Throws");
589
590 MethodReflector r = new MethodReflector(m_class);
591 while (r.hasNext()) {
592 r.next();
593 table.addRow(r.getReturns(), r.getName(),
594 r.getParameters(), r.getThrows());
595 }
596 section.add(table.tabulate());
597 manager.addSection(section);
598 }
599
600
601
602
603 static class MethodReflector implements Iterator<Method> {
604
605
606
607
608 private Class<?> m_target;
609
610
611
612
613 private int m_current = -1;
614
615
616
617
618
619
620 private Method m_method = null;
621
622
623
624
625
626 public MethodReflector(Class<?> target) {
627 this.m_target = target;
628 }
629
630
631
632
633 public int countMethods() {
634 return m_target.getMethods().length;
635 }
636
637
638
639
640 public int getPosition() {
641 return m_current;
642 }
643
644
645 public boolean hasNext() {
646 return (m_current < countMethods() - 1);
647 }
648
649
650 public Method next() {
651 if (!hasNext()) {
652 throw new IndexOutOfBoundsException();
653 }
654 m_current++;
655 m_method = m_target.getMethods()[m_current];
656 return m_method;
657 }
658
659
660
661
662
663 public void remove() {
664 throw new RuntimeException("Cannot remove methods.");
665 }
666
667
668
669
670 public String getReturns() {
671 return className(m_method.getReturnType().toString());
672 }
673
674
675
676
677 public String getName() {
678 return m_method.getName();
679 }
680
681
682
683
684
685
686 public static String className(String name) {
687 final String[] prefixes = new String[] {"class", "interface"};
688 String result = name;
689
690 for (String current : prefixes) {
691 if (result.startsWith(current)) {
692 result = result.substring(current.length() + 1);
693 }
694 }
695 return result;
696 }
697
698
699
700
701
702 public static String getClassNames(Class<?>[] classes) {
703 String result = "";
704 for (Class<?> p : classes) {
705 String param = className(p.toString());
706
707 result += param + ", ";
708 }
709 if (result.endsWith(", ")) {
710 result = result.substring(0, result.length() - 2);
711 }
712 return result;
713 }
714
715
716
717
718 public String getParameters() {
719 return getClassNames(m_method.getParameterTypes());
720 }
721
722
723
724
725 public String getThrows() {
726 return getClassNames(m_method.getExceptionTypes());
727 }
728
729
730
731
732 public String getCurrentAsString() {
733
734 return getReturns() + " " + getName()
735 + "(" + getParameters() + ")"
736 + ((getThrows().equals(""))
737 ? ("") : (" throws " + getThrows()));
738 }
739
740 }
741 }