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