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 attributes=LazyList.add(attributes,defineAttribute(key, value));
268 }
269 }
270 }
271
272 }
273 catch(MissingResourceException e)
274 {
275 Log.ignore(e);
276 }
277 }
278
279 _info = new MBeanInfo(o_class.getName(),
280 desc,
281 (MBeanAttributeInfo[])LazyList.toArray(attributes, MBeanAttributeInfo.class),
282 (MBeanConstructorInfo[])LazyList.toArray(constructors, MBeanConstructorInfo.class),
283 (MBeanOperationInfo[])LazyList.toArray(operations, MBeanOperationInfo.class),
284 (MBeanNotificationInfo[])LazyList.toArray(notifications, MBeanNotificationInfo.class));
285 }
286 }
287 catch(RuntimeException e)
288 {
289 Log.warn(e);
290 throw e;
291 }
292 return _info;
293 }
294
295
296
297 public Object getAttribute(String name) throws AttributeNotFoundException, MBeanException, ReflectionException
298 {
299 Method getter = (Method) _getters.get(name);
300 if (getter == null)
301 throw new AttributeNotFoundException(name);
302 try
303 {
304 Object o = _managed;
305 if (getter.getDeclaringClass().isInstance(this))
306 o = this;
307
308
309 Object r=getter.invoke(o, (java.lang.Object[]) null);
310
311
312 if (r!=null && _convert.contains(name))
313 {
314 if (r.getClass().isArray())
315 {
316 ObjectName[] on = new ObjectName[Array.getLength(r)];
317 for (int i=0;i<on.length;i++)
318 on[i]=_mbeanContainer.findMBean(Array.get(r, i));
319 r=on;
320 }
321 else
322 {
323 ObjectName mbean = _mbeanContainer.findMBean(r);
324 if (mbean==null)
325 return null;
326 r=mbean;
327 }
328 }
329 return r;
330 }
331 catch (IllegalAccessException e)
332 {
333 Log.warn(Log.EXCEPTION, e);
334 throw new AttributeNotFoundException(e.toString());
335 }
336 catch (InvocationTargetException e)
337 {
338 Log.warn(Log.EXCEPTION, e);
339 throw new ReflectionException((Exception) e.getTargetException());
340 }
341 }
342
343
344 public AttributeList getAttributes(String[] names)
345 {
346 AttributeList results = new AttributeList(names.length);
347 for (int i = 0; i < names.length; i++)
348 {
349 try
350 {
351 results.add(new Attribute(names[i], getAttribute(names[i])));
352 }
353 catch (Exception e)
354 {
355 Log.warn(Log.EXCEPTION, e);
356 }
357 }
358 return results;
359 }
360
361
362 public void setAttribute(Attribute attr) throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException, ReflectionException
363 {
364 if (attr == null)
365 return;
366
367 if (Log.isDebugEnabled())
368 Log.debug("setAttribute " + _managed + ":" +attr.getName() + "=" + attr.getValue());
369 Method setter = (Method) _setters.get(attr.getName());
370 if (setter == null)
371 throw new AttributeNotFoundException(attr.getName());
372 try
373 {
374 Object o = _managed;
375 if (setter.getDeclaringClass().isInstance(this))
376 o = this;
377
378
379 Object value = attr.getValue();
380
381
382 if (value!=null && _convert.contains(attr.getName()))
383 {
384 if (value.getClass().isArray())
385 {
386 Class t=setter.getParameterTypes()[0].getComponentType();
387 Object na = Array.newInstance(t,Array.getLength(value));
388 for (int i=Array.getLength(value);i-->0;)
389 Array.set(na, i, _mbeanContainer.findBean((ObjectName)Array.get(value, i)));
390 value=na;
391 }
392 else
393 value=_mbeanContainer.findBean((ObjectName)value);
394 }
395
396
397 setter.invoke(o, new Object[]{ value });
398 }
399 catch (IllegalAccessException e)
400 {
401 Log.warn(Log.EXCEPTION, e);
402 throw new AttributeNotFoundException(e.toString());
403 }
404 catch (InvocationTargetException e)
405 {
406 Log.warn(Log.EXCEPTION, e);
407 throw new ReflectionException((Exception) e.getTargetException());
408 }
409 }
410
411
412 public AttributeList setAttributes(AttributeList attrs)
413 {
414 Log.debug("setAttributes");
415
416 AttributeList results = new AttributeList(attrs.size());
417 Iterator iter = attrs.iterator();
418 while (iter.hasNext())
419 {
420 try
421 {
422 Attribute attr = (Attribute) iter.next();
423 setAttribute(attr);
424 results.add(new Attribute(attr.getName(), getAttribute(attr.getName())));
425 }
426 catch (Exception e)
427 {
428 Log.warn(Log.EXCEPTION, e);
429 }
430 }
431 return results;
432 }
433
434
435 public Object invoke(String name, Object[] params, String[] signature) throws MBeanException, ReflectionException
436 {
437 if (Log.isDebugEnabled())
438 Log.debug("invoke " + name);
439
440 String methodKey = name + "(";
441 if (signature != null)
442 for (int i = 0; i < signature.length; i++)
443 methodKey += (i > 0 ? "," : "") + signature[i];
444 methodKey += ")";
445
446 ClassLoader old_loader=Thread.currentThread().getContextClassLoader();
447 try
448 {
449 Thread.currentThread().setContextClassLoader(_loader);
450 Method method = (Method) _methods.get(methodKey);
451 if (method == null)
452 throw new NoSuchMethodException(methodKey);
453
454 Object o = _managed;
455 if (method.getDeclaringClass().isInstance(this))
456 o = this;
457 return method.invoke(o, params);
458 }
459 catch (NoSuchMethodException e)
460 {
461 Log.warn(Log.EXCEPTION, e);
462 throw new ReflectionException(e);
463 }
464 catch (IllegalAccessException e)
465 {
466 Log.warn(Log.EXCEPTION, e);
467 throw new MBeanException(e);
468 }
469 catch (InvocationTargetException e)
470 {
471 Log.warn(Log.EXCEPTION, e);
472 throw new ReflectionException((Exception) e.getTargetException());
473 }
474 finally
475 {
476 Thread.currentThread().setContextClassLoader(old_loader);
477 }
478 }
479
480 private static Object findInfluences(Object influences, Class aClass)
481 {
482 if (aClass!=null)
483 {
484
485 influences=LazyList.add(influences,aClass);
486
487
488 influences=findInfluences(influences,aClass.getSuperclass());
489
490
491 Class[] ifs = aClass.getInterfaces();
492 for (int i=0;ifs!=null && i<ifs.length;i++)
493 influences=findInfluences(influences,ifs[i]);
494 }
495 return influences;
496 }
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514 public MBeanAttributeInfo defineAttribute(String name, String metaData)
515 {
516 String description = "";
517 boolean writable = true;
518 boolean onMBean = false;
519 boolean convert = false;
520
521 if (metaData!= null)
522 {
523 String[] tokens = metaData.split(":", 3);
524 for (int t=0;t<tokens.length-1;t++)
525 {
526 tokens[t]=tokens[t].trim();
527 if ("RO".equals(tokens[t]))
528 writable=false;
529 else
530 {
531 onMBean=("MMBean".equalsIgnoreCase(tokens[t]) || "MBean".equalsIgnoreCase(tokens[t]));
532 convert=("MMBean".equalsIgnoreCase(tokens[t]) || "MObject".equalsIgnoreCase(tokens[t]));
533 }
534 }
535 description=tokens[tokens.length-1];
536 }
537
538
539 String uName = name.substring(0, 1).toUpperCase() + name.substring(1);
540 Class oClass = onMBean ? this.getClass() : _managed.getClass();
541
542 if (Log.isDebugEnabled())
543 Log.debug("defineAttribute "+name+" "+onMBean+":"+writable+":"+oClass+":"+description);
544
545 Class type = null;
546 Method getter = null;
547 Method setter = null;
548 Method[] methods = oClass.getMethods();
549 for (int m = 0; m < methods.length; m++)
550 {
551 if ((methods[m].getModifiers() & Modifier.PUBLIC) == 0)
552 continue;
553
554
555 if (methods[m].getName().equals("get" + uName) && methods[m].getParameterTypes().length == 0)
556 {
557 if (getter != null)
558 throw new IllegalArgumentException("Multiple getters for attr " + name+ " in "+oClass);
559 getter = methods[m];
560 if (type != null && !type.equals(methods[m].getReturnType()))
561 throw new IllegalArgumentException("Type conflict for attr " + name+ " in "+oClass);
562 type = methods[m].getReturnType();
563 }
564
565
566 if (methods[m].getName().equals("is" + uName) && methods[m].getParameterTypes().length == 0)
567 {
568 if (getter != null)
569 throw new IllegalArgumentException("Multiple getters for attr " + name+ " in "+oClass);
570 getter = methods[m];
571 if (type != null && !type.equals(methods[m].getReturnType()))
572 throw new IllegalArgumentException("Type conflict for attr " + name+ " in "+oClass);
573 type = methods[m].getReturnType();
574 }
575
576
577 if (writable && methods[m].getName().equals("set" + uName) && methods[m].getParameterTypes().length == 1)
578 {
579 if (setter != null)
580 throw new IllegalArgumentException("Multiple setters for attr " + name+ " in "+oClass);
581 setter = methods[m];
582 if (type != null && !type.equals(methods[m].getParameterTypes()[0]))
583 throw new IllegalArgumentException("Type conflict for attr " + name+ " in "+oClass);
584 type = methods[m].getParameterTypes()[0];
585 }
586 }
587
588 if (convert)
589 {
590 if (type==null)
591 throw new IllegalArgumentException("No type for " + name+" on "+_managed.getClass());
592
593 if (type.isPrimitive() && !type.isArray())
594 throw new IllegalArgumentException("Cannot convert primative " + name);
595 }
596
597 if (getter == null && setter == null)
598 throw new IllegalArgumentException("No getter or setters found for " + name+ " in "+oClass);
599
600 try
601 {
602
603 _getters.put(name, getter);
604 _setters.put(name, setter);
605
606
607
608 MBeanAttributeInfo info=null;
609 if (convert)
610 {
611 _convert.add(name);
612 if (type.isArray())
613 info= new MBeanAttributeInfo(name,OBJECT_NAME_ARRAY_CLASS,description,getter!=null,setter!=null,getter!=null&&getter.getName().startsWith("is"));
614
615 else
616 info= new MBeanAttributeInfo(name,OBJECT_NAME_CLASS,description,getter!=null,setter!=null,getter!=null&&getter.getName().startsWith("is"));
617 }
618 else
619 info= new MBeanAttributeInfo(name,description,getter,setter);
620
621 return info;
622 }
623 catch (Exception e)
624 {
625 Log.warn(Log.EXCEPTION, e);
626 throw new IllegalArgumentException(e.toString());
627 }
628 }
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644 private MBeanOperationInfo defineOperation(String signature, String metaData, ResourceBundle bundle)
645 {
646 String[] tokens=metaData.split(":",3);
647 int i=tokens.length-1;
648 String description=tokens[i--];
649 String impact_name = i<0?"UNKNOWN":tokens[i--].trim();
650 if (i==0)
651 tokens[0]=tokens[0].trim();
652 boolean onMBean= i==0 && ("MBean".equalsIgnoreCase(tokens[0])||"MMBean".equalsIgnoreCase(tokens[0]));
653 boolean convert= i==0 && ("MObject".equalsIgnoreCase(tokens[0])||"MMBean".equalsIgnoreCase(tokens[0]));
654
655 if (Log.isDebugEnabled())
656 Log.debug("defineOperation "+signature+" "+onMBean+":"+impact_name+":"+description);
657
658 Class oClass = onMBean ? this.getClass() : _managed.getClass();
659
660 try
661 {
662
663 int impact=MBeanOperationInfo.UNKNOWN;
664 if (impact_name==null || impact_name.equals("UNKNOWN"))
665 impact=MBeanOperationInfo.UNKNOWN;
666 else if (impact_name.equals("ACTION"))
667 impact=MBeanOperationInfo.ACTION;
668 else if (impact_name.equals("INFO"))
669 impact=MBeanOperationInfo.INFO;
670 else if (impact_name.equals("ACTION_INFO"))
671 impact=MBeanOperationInfo.ACTION_INFO;
672 else
673 Log.warn("Unknown impact '"+impact_name+"' for "+signature);
674
675
676
677 String[] parts=signature.split("[\\(\\)]");
678 String method_name=parts[0];
679 String arguments=parts.length==2?parts[1]:null;
680 String[] args=arguments==null?new String[0]:arguments.split(" *, *");
681
682
683 Class[] types = new Class[args.length];
684 MBeanParameterInfo[] pInfo = new MBeanParameterInfo[args.length];
685 signature=method_name;
686 for (i = 0; i < args.length; i++)
687 {
688 Class type = TypeUtil.fromName(args[i]);
689 if (type == null)
690 type = Thread.currentThread().getContextClassLoader().loadClass(args[i]);
691 types[i] = type;
692 args[i] = type.isPrimitive() ? TypeUtil.toName(type) : args[i];
693 signature+=(i>0?",":"(")+args[i];
694 }
695 signature+=(i>0?")":"()");
696
697
698 for (i = 0; i < args.length; i++)
699 {
700 String param_desc = bundle.getString(signature + "[" + i + "]");
701 parts=param_desc.split(" *: *",2);
702 if (Log.isDebugEnabled())
703 Log.debug(parts[0]+": "+parts[1]);
704 pInfo[i] = new MBeanParameterInfo(parts[0].trim(), args[i], parts[1].trim());
705 }
706
707
708 Method method = oClass.getMethod(method_name, types);
709 Class returnClass = method.getReturnType();
710 _methods.put(signature, method);
711 if (convert)
712 _convert.add(signature);
713
714 return new MBeanOperationInfo(method_name, description, pInfo, returnClass.isPrimitive() ? TypeUtil.toName(returnClass) : (returnClass.getName()), impact);
715 }
716 catch (Exception e)
717 {
718 Log.warn("Operation '"+signature+"'", e);
719 throw new IllegalArgumentException(e.toString());
720 }
721
722 }
723
724 }