1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.jmx;
15
16 import java.lang.reflect.Array;
17 import java.lang.reflect.Constructor;
18 import java.lang.reflect.InvocationTargetException;
19 import java.lang.reflect.Method;
20 import java.lang.reflect.Modifier;
21 import java.util.Collection;
22 import java.util.Enumeration;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.Locale;
27 import java.util.Map;
28 import java.util.MissingResourceException;
29 import java.util.ResourceBundle;
30 import java.util.Set;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33
34 import javax.management.Attribute;
35 import javax.management.AttributeList;
36 import javax.management.AttributeNotFoundException;
37 import javax.management.DynamicMBean;
38 import javax.management.InvalidAttributeValueException;
39 import javax.management.MBeanAttributeInfo;
40 import javax.management.MBeanConstructorInfo;
41 import javax.management.MBeanException;
42 import javax.management.MBeanInfo;
43 import javax.management.MBeanNotificationInfo;
44 import javax.management.MBeanOperationInfo;
45 import javax.management.MBeanParameterInfo;
46 import javax.management.ObjectName;
47 import javax.management.ReflectionException;
48 import javax.management.modelmbean.ModelMBean;
49
50 import org.eclipse.jetty.util.LazyList;
51 import org.eclipse.jetty.util.Loader;
52 import org.eclipse.jetty.util.TypeUtil;
53 import org.eclipse.jetty.util.log.Log;
54 import org.eclipse.jetty.util.log.Logger;
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public class ObjectMBean implements DynamicMBean
70 {
71 private static final Logger LOG = Log.getLogger(ObjectMBean.class);
72
73 private static Class[] OBJ_ARG = new Class[]{Object.class};
74
75 protected Object _managed;
76 private MBeanInfo _info;
77 private Map _getters=new HashMap();
78 private Map _setters=new HashMap();
79 private Map _methods=new HashMap();
80 private Set _convert=new HashSet();
81 private ClassLoader _loader;
82 private MBeanContainer _mbeanContainer;
83
84 private static String OBJECT_NAME_CLASS = ObjectName.class.getName();
85 private static String OBJECT_NAME_ARRAY_CLASS = ObjectName[].class.getName();
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 public static Object mbeanFor(Object o)
107 {
108 try
109 {
110 Class oClass = o.getClass();
111 Object mbean = null;
112
113 while (mbean == null && oClass != null)
114 {
115 String pName = oClass.getPackage().getName();
116 String cName = oClass.getName().substring(pName.length() + 1);
117 String mName = pName + ".jmx." + cName + "MBean";
118
119
120 try
121 {
122 Class mClass = (Object.class.equals(oClass))?oClass=ObjectMBean.class:Loader.loadClass(oClass,mName,true);
123 if (LOG.isDebugEnabled())
124 LOG.debug("mbeanFor " + o + " mClass=" + mClass);
125
126 try
127 {
128 Constructor constructor = mClass.getConstructor(OBJ_ARG);
129 mbean=constructor.newInstance(new Object[]{o});
130 }
131 catch(Exception e)
132 {
133 LOG.ignore(e);
134 if (ModelMBean.class.isAssignableFrom(mClass))
135 {
136 mbean=mClass.newInstance();
137 ((ModelMBean)mbean).setManagedResource(o, "objectReference");
138 }
139 }
140
141 if (LOG.isDebugEnabled())
142 LOG.debug("mbeanFor " + o + " is " + mbean);
143 return mbean;
144 }
145 catch (ClassNotFoundException e)
146 {
147
148
149
150
151 if (e.getMessage().contains(mName))
152 LOG.ignore(e);
153 else
154 LOG.warn(e);
155 }
156 catch (Error e)
157 {
158 LOG.warn(e);
159 mbean = null;
160 }
161 catch (Exception e)
162 {
163 LOG.warn(e);
164 mbean = null;
165 }
166
167 oClass = oClass.getSuperclass();
168 }
169 }
170 catch (Exception e)
171 {
172 LOG.ignore(e);
173 }
174 return null;
175 }
176
177
178 public ObjectMBean(Object managedObject)
179 {
180 _managed = managedObject;
181 _loader = Thread.currentThread().getContextClassLoader();
182 }
183
184 public Object getManagedObject()
185 {
186 return _managed;
187 }
188
189 public ObjectName getObjectName()
190 {
191 return null;
192 }
193
194 public String getObjectContextBasis()
195 {
196 return null;
197 }
198
199 public String getObjectNameBasis()
200 {
201 return null;
202 }
203
204 protected void setMBeanContainer(MBeanContainer container)
205 {
206 this._mbeanContainer = container;
207 }
208
209 public MBeanContainer getMBeanContainer ()
210 {
211 return this._mbeanContainer;
212 }
213
214
215 public MBeanInfo getMBeanInfo()
216 {
217 try
218 {
219 if (_info==null)
220 {
221
222 String desc=null;
223 Object attributes=null;
224 Object constructors=null;
225 Object operations=null;
226 Object notifications=null;
227
228
229 Class o_class=_managed.getClass();
230 Object influences = findInfluences(null, _managed.getClass());
231
232
233 Set defined=new HashSet();
234
235
236 for (int i=0;i<LazyList.size(influences);i++)
237 {
238 Class oClass = (Class)LazyList.get(influences, i);
239
240
241 if (Object.class.equals(oClass))
242 oClass=ObjectMBean.class;
243 String pName = oClass.getPackage().getName();
244 String cName = oClass.getName().substring(pName.length() + 1);
245 String rName = pName.replace('.', '/') + "/jmx/" + cName+"-mbean";
246
247 try
248 {
249 LOG.debug(rName);
250 ResourceBundle bundle = Loader.getResourceBundle(o_class, rName,true,Locale.getDefault());
251
252
253
254 Enumeration e = bundle.getKeys();
255 while (e.hasMoreElements())
256 {
257 String key = (String)e.nextElement();
258 String value = bundle.getString(key);
259
260
261 if (key.equals(cName))
262 {
263
264 if (desc==null)
265 desc=value;
266 }
267 else if (key.indexOf('(')>0)
268 {
269
270 if (!defined.contains(key) && key.indexOf('[')<0)
271 {
272 defined.add(key);
273 operations=LazyList.add(operations,defineOperation(key, value, bundle));
274 }
275 }
276 else
277 {
278
279 if (!defined.contains(key))
280 {
281 defined.add(key);
282 MBeanAttributeInfo info=defineAttribute(key, value);
283 if (info!=null)
284 attributes=LazyList.add(attributes,info);
285 }
286 }
287 }
288 }
289 catch(MissingResourceException e)
290 {
291 LOG.ignore(e);
292 }
293 }
294
295 _info = new MBeanInfo(o_class.getName(),
296 desc,
297 (MBeanAttributeInfo[])LazyList.toArray(attributes, MBeanAttributeInfo.class),
298 (MBeanConstructorInfo[])LazyList.toArray(constructors, MBeanConstructorInfo.class),
299 (MBeanOperationInfo[])LazyList.toArray(operations, MBeanOperationInfo.class),
300 (MBeanNotificationInfo[])LazyList.toArray(notifications, MBeanNotificationInfo.class));
301 }
302 }
303 catch(RuntimeException e)
304 {
305 LOG.warn(e);
306 throw e;
307 }
308 return _info;
309 }
310
311
312
313 public Object getAttribute(String name) throws AttributeNotFoundException, MBeanException, ReflectionException
314 {
315 Method getter = (Method) _getters.get(name);
316 if (getter == null)
317 throw new AttributeNotFoundException(name);
318 try
319 {
320 Object o = _managed;
321 if (getter.getDeclaringClass().isInstance(this))
322 o = this;
323
324
325 Object r=getter.invoke(o, (java.lang.Object[]) null);
326
327
328 if (r!=null && _convert.contains(name))
329 {
330 if (r.getClass().isArray())
331 {
332 ObjectName[] on = new ObjectName[Array.getLength(r)];
333 for (int i=0;i<on.length;i++)
334 on[i]=_mbeanContainer.findMBean(Array.get(r, i));
335 r=on;
336 }
337 else if (r instanceof Collection<?>)
338 {
339 Collection<Object> c = (Collection<Object>)r;
340 ObjectName[] on = new ObjectName[c.size()];
341 int i=0;
342 for (Object obj :c)
343 on[i++]=_mbeanContainer.findMBean(obj);
344 r=on;
345 }
346 else
347 {
348 ObjectName mbean = _mbeanContainer.findMBean(r);
349 if (mbean==null)
350 return null;
351 r=mbean;
352 }
353 }
354 return r;
355 }
356 catch (IllegalAccessException e)
357 {
358 LOG.warn(Log.EXCEPTION, e);
359 throw new AttributeNotFoundException(e.toString());
360 }
361 catch (InvocationTargetException e)
362 {
363 LOG.warn(Log.EXCEPTION, e);
364 throw new ReflectionException(new Exception(e.getCause()));
365 }
366 }
367
368
369 public AttributeList getAttributes(String[] names)
370 {
371 AttributeList results = new AttributeList(names.length);
372 for (int i = 0; i < names.length; i++)
373 {
374 try
375 {
376 results.add(new Attribute(names[i], getAttribute(names[i])));
377 }
378 catch (Exception e)
379 {
380 LOG.warn(Log.EXCEPTION, e);
381 }
382 }
383 return results;
384 }
385
386
387 public void setAttribute(Attribute attr) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException
388 {
389 if (attr == null)
390 return;
391
392 if (LOG.isDebugEnabled())
393 LOG.debug("setAttribute " + _managed + ":" +attr.getName() + "=" + attr.getValue());
394 Method setter = (Method) _setters.get(attr.getName());
395 if (setter == null)
396 throw new AttributeNotFoundException(attr.getName());
397 try
398 {
399 Object o = _managed;
400 if (setter.getDeclaringClass().isInstance(this))
401 o = this;
402
403
404 Object value = attr.getValue();
405
406
407 if (value!=null && _convert.contains(attr.getName()))
408 {
409 if (value.getClass().isArray())
410 {
411 Class t=setter.getParameterTypes()[0].getComponentType();
412 Object na = Array.newInstance(t,Array.getLength(value));
413 for (int i=Array.getLength(value);i-->0;)
414 Array.set(na, i, _mbeanContainer.findBean((ObjectName)Array.get(value, i)));
415 value=na;
416 }
417 else
418 value=_mbeanContainer.findBean((ObjectName)value);
419 }
420
421
422 setter.invoke(o, new Object[]{ value });
423 }
424 catch (IllegalAccessException e)
425 {
426 LOG.warn(Log.EXCEPTION, e);
427 throw new AttributeNotFoundException(e.toString());
428 }
429 catch (InvocationTargetException e)
430 {
431 LOG.warn(Log.EXCEPTION, e);
432 throw new ReflectionException(new Exception(e.getCause()));
433 }
434 }
435
436
437 public AttributeList setAttributes(AttributeList attrs)
438 {
439 LOG.debug("setAttributes");
440
441 AttributeList results = new AttributeList(attrs.size());
442 Iterator iter = attrs.iterator();
443 while (iter.hasNext())
444 {
445 try
446 {
447 Attribute attr = (Attribute) iter.next();
448 setAttribute(attr);
449 results.add(new Attribute(attr.getName(), getAttribute(attr.getName())));
450 }
451 catch (Exception e)
452 {
453 LOG.warn(Log.EXCEPTION, e);
454 }
455 }
456 return results;
457 }
458
459
460 public Object invoke(String name, Object[] params, String[] signature) throws MBeanException, ReflectionException
461 {
462 if (LOG.isDebugEnabled())
463 LOG.debug("invoke " + name);
464
465 String methodKey = name + "(";
466 if (signature != null)
467 for (int i = 0; i < signature.length; i++)
468 methodKey += (i > 0 ? "," : "") + signature[i];
469 methodKey += ")";
470
471 ClassLoader old_loader=Thread.currentThread().getContextClassLoader();
472 try
473 {
474 Thread.currentThread().setContextClassLoader(_loader);
475 Method method = (Method) _methods.get(methodKey);
476 if (method == null)
477 throw new NoSuchMethodException(methodKey);
478
479 Object o = _managed;
480 if (method.getDeclaringClass().isInstance(this))
481 o = this;
482 return method.invoke(o, params);
483 }
484 catch (NoSuchMethodException e)
485 {
486 LOG.warn(Log.EXCEPTION, e);
487 throw new ReflectionException(e);
488 }
489 catch (IllegalAccessException e)
490 {
491 LOG.warn(Log.EXCEPTION, e);
492 throw new MBeanException(e);
493 }
494 catch (InvocationTargetException e)
495 {
496 LOG.warn(Log.EXCEPTION, e);
497 throw new ReflectionException(new Exception(e.getCause()));
498 }
499 finally
500 {
501 Thread.currentThread().setContextClassLoader(old_loader);
502 }
503 }
504
505 private static Object findInfluences(Object influences, Class aClass)
506 {
507 if (aClass!=null)
508 {
509
510 influences=LazyList.add(influences,aClass);
511
512
513 influences=findInfluences(influences,aClass.getSuperclass());
514
515
516 Class[] ifs = aClass.getInterfaces();
517 for (int i=0;ifs!=null && i<ifs.length;i++)
518 influences=findInfluences(influences,ifs[i]);
519 }
520 return influences;
521 }
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539 public MBeanAttributeInfo defineAttribute(String name, String metaData)
540 {
541 String description = "";
542 boolean writable = true;
543 boolean onMBean = false;
544 boolean convert = false;
545
546 if (metaData!= null)
547 {
548 String[] tokens = metaData.split(":", 3);
549 for (int t=0;t<tokens.length-1;t++)
550 {
551 tokens[t]=tokens[t].trim();
552 if ("RO".equals(tokens[t]))
553 writable=false;
554 else
555 {
556 onMBean=("MMBean".equalsIgnoreCase(tokens[t]) || "MBean".equalsIgnoreCase(tokens[t]));
557 convert=("MMBean".equalsIgnoreCase(tokens[t]) || "MObject".equalsIgnoreCase(tokens[t]));
558 }
559 }
560 description=tokens[tokens.length-1];
561 }
562
563
564 String uName = name.substring(0, 1).toUpperCase() + name.substring(1);
565 Class oClass = onMBean ? this.getClass() : _managed.getClass();
566
567 if (LOG.isDebugEnabled())
568 LOG.debug("defineAttribute "+name+" "+onMBean+":"+writable+":"+oClass+":"+description);
569
570 Class type = null;
571 Method getter = null;
572 Method setter = null;
573 Method[] methods = oClass.getMethods();
574 for (int m = 0; m < methods.length; m++)
575 {
576 if ((methods[m].getModifiers() & Modifier.PUBLIC) == 0)
577 continue;
578
579
580 if (methods[m].getName().equals("get" + uName) && methods[m].getParameterTypes().length == 0)
581 {
582 if (getter != null)
583 {
584 LOG.warn("Multiple mbean getters for attr " + name+ " in "+oClass);
585 continue;
586 }
587 getter = methods[m];
588 if (type != null && !type.equals(methods[m].getReturnType()))
589 {
590 LOG.warn("Type conflict for mbean attr " + name+ " in "+oClass);
591 continue;
592 }
593 type = methods[m].getReturnType();
594 }
595
596
597 if (methods[m].getName().equals("is" + uName) && methods[m].getParameterTypes().length == 0)
598 {
599 if (getter != null)
600 {
601 LOG.warn("Multiple mbean getters for attr " + name+ " in "+oClass);
602 continue;
603 }
604 getter = methods[m];
605 if (type != null && !type.equals(methods[m].getReturnType()))
606 {
607 LOG.warn("Type conflict for mbean attr " + name+ " in "+oClass);
608 continue;
609 }
610 type = methods[m].getReturnType();
611 }
612
613
614 if (writable && methods[m].getName().equals("set" + uName) && methods[m].getParameterTypes().length == 1)
615 {
616 if (setter != null)
617 {
618 LOG.warn("Multiple setters for mbean attr " + name+ " in "+oClass);
619 continue;
620 }
621 setter = methods[m];
622 if (type != null && !type.equals(methods[m].getParameterTypes()[0]))
623 {
624 LOG.warn("Type conflict for mbean attr " + name+ " in "+oClass);
625 continue;
626 }
627 type = methods[m].getParameterTypes()[0];
628 }
629 }
630
631 if (convert)
632 {
633 if (type==null)
634 {
635 LOG.warn("No mbean type for " + name+" on "+_managed.getClass());
636 return null;
637 }
638
639 if (type.isPrimitive() && !type.isArray())
640 {
641 LOG.warn("Cannot convert mbean primative " + name);
642 return null;
643 }
644 }
645
646 if (getter == null && setter == null)
647 {
648 LOG.warn("No mbean getter or setters found for " + name+ " in "+oClass);
649 return null;
650 }
651
652 try
653 {
654
655 _getters.put(name, getter);
656 _setters.put(name, setter);
657
658 MBeanAttributeInfo info=null;
659 if (convert)
660 {
661 _convert.add(name);
662 if (type.isArray())
663 info= new MBeanAttributeInfo(name,OBJECT_NAME_ARRAY_CLASS,description,getter!=null,setter!=null,getter!=null&&getter.getName().startsWith("is"));
664
665 else
666 info= new MBeanAttributeInfo(name,OBJECT_NAME_CLASS,description,getter!=null,setter!=null,getter!=null&&getter.getName().startsWith("is"));
667 }
668 else
669 info= new MBeanAttributeInfo(name,description,getter,setter);
670
671 return info;
672 }
673 catch (Exception e)
674 {
675 LOG.warn(name+": "+metaData, e);
676 throw new IllegalArgumentException(e.toString());
677 }
678 }
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694 private MBeanOperationInfo defineOperation(String signature, String metaData, ResourceBundle bundle)
695 {
696 String[] tokens=metaData.split(":",3);
697 int i=tokens.length-1;
698 String description=tokens[i--];
699 String impact_name = i<0?"UNKNOWN":tokens[i--].trim();
700 if (i==0)
701 tokens[0]=tokens[0].trim();
702 boolean onMBean= i==0 && ("MBean".equalsIgnoreCase(tokens[0])||"MMBean".equalsIgnoreCase(tokens[0]));
703 boolean convert= i==0 && ("MObject".equalsIgnoreCase(tokens[0])||"MMBean".equalsIgnoreCase(tokens[0]));
704
705 if (LOG.isDebugEnabled())
706 LOG.debug("defineOperation "+signature+" "+onMBean+":"+impact_name+":"+description);
707
708 Class oClass = onMBean ? this.getClass() : _managed.getClass();
709
710 try
711 {
712
713 int impact=MBeanOperationInfo.UNKNOWN;
714 if (impact_name==null || impact_name.equals("UNKNOWN"))
715 impact=MBeanOperationInfo.UNKNOWN;
716 else if (impact_name.equals("ACTION"))
717 impact=MBeanOperationInfo.ACTION;
718 else if (impact_name.equals("INFO"))
719 impact=MBeanOperationInfo.INFO;
720 else if (impact_name.equals("ACTION_INFO"))
721 impact=MBeanOperationInfo.ACTION_INFO;
722 else
723 LOG.warn("Unknown impact '"+impact_name+"' for "+signature);
724
725
726
727 String[] parts=signature.split("[\\(\\)]");
728 String method_name=parts[0];
729 String arguments=parts.length==2?parts[1]:null;
730 String[] args=arguments==null?new String[0]:arguments.split(" *, *");
731
732
733 Class[] types = new Class[args.length];
734 MBeanParameterInfo[] pInfo = new MBeanParameterInfo[args.length];
735 signature=method_name;
736 for (i = 0; i < args.length; i++)
737 {
738 Class type = TypeUtil.fromName(args[i]);
739 if (type == null)
740 type = Thread.currentThread().getContextClassLoader().loadClass(args[i]);
741 types[i] = type;
742 args[i] = type.isPrimitive() ? TypeUtil.toName(type) : args[i];
743 signature+=(i>0?",":"(")+args[i];
744 }
745 signature+=(i>0?")":"()");
746
747
748 for (i = 0; i < args.length; i++)
749 {
750 String param_desc = bundle.getString(signature + "[" + i + "]");
751 parts=param_desc.split(" *: *",2);
752 if (LOG.isDebugEnabled())
753 LOG.debug(parts[0]+": "+parts[1]);
754 pInfo[i] = new MBeanParameterInfo(parts[0].trim(), args[i], parts[1].trim());
755 }
756
757
758 Method method = oClass.getMethod(method_name, types);
759 Class returnClass = method.getReturnType();
760 _methods.put(signature, method);
761 if (convert)
762 _convert.add(signature);
763
764 return new MBeanOperationInfo(method_name, description, pInfo, returnClass.isPrimitive() ? TypeUtil.toName(returnClass) : (returnClass.getName()), impact);
765 }
766 catch (Exception e)
767 {
768 LOG.warn("Operation '"+signature+"'", e);
769 throw new IllegalArgumentException(e.toString());
770 }
771
772 }
773
774 }