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