1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.mina.integration.jmx;
18
19 import java.beans.IntrospectionException;
20 import java.beans.Introspector;
21 import java.beans.PropertyDescriptor;
22 import java.beans.PropertyEditor;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.net.SocketAddress;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Date;
29 import java.util.HashMap;
30 import java.util.Iterator;
31 import java.util.LinkedHashMap;
32 import java.util.LinkedHashSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.concurrent.ConcurrentHashMap;
37 import java.util.concurrent.ThreadPoolExecutor;
38
39 import javax.management.Attribute;
40 import javax.management.AttributeChangeNotification;
41 import javax.management.AttributeList;
42 import javax.management.AttributeNotFoundException;
43 import javax.management.InstanceNotFoundException;
44 import javax.management.ListenerNotFoundException;
45 import javax.management.MBeanException;
46 import javax.management.MBeanInfo;
47 import javax.management.MBeanNotificationInfo;
48 import javax.management.MBeanParameterInfo;
49 import javax.management.MBeanRegistration;
50 import javax.management.MBeanServer;
51 import javax.management.Notification;
52 import javax.management.NotificationFilter;
53 import javax.management.NotificationListener;
54 import javax.management.ObjectName;
55 import javax.management.ReflectionException;
56 import javax.management.RuntimeOperationsException;
57 import javax.management.modelmbean.InvalidTargetObjectTypeException;
58 import javax.management.modelmbean.ModelMBean;
59 import javax.management.modelmbean.ModelMBeanAttributeInfo;
60 import javax.management.modelmbean.ModelMBeanConstructorInfo;
61 import javax.management.modelmbean.ModelMBeanInfo;
62 import javax.management.modelmbean.ModelMBeanInfoSupport;
63 import javax.management.modelmbean.ModelMBeanNotificationInfo;
64 import javax.management.modelmbean.ModelMBeanOperationInfo;
65
66 import ognl.ExpressionSyntaxException;
67 import ognl.InappropriateExpressionException;
68 import ognl.NoSuchPropertyException;
69 import ognl.Ognl;
70 import ognl.OgnlContext;
71 import ognl.OgnlException;
72 import ognl.OgnlRuntime;
73 import ognl.TypeConverter;
74
75 import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
76 import org.apache.mina.core.filterchain.IoFilter;
77 import org.apache.mina.core.filterchain.IoFilterChain;
78 import org.apache.mina.core.filterchain.IoFilterChainBuilder;
79 import org.apache.mina.core.service.IoAcceptor;
80 import org.apache.mina.core.service.IoHandler;
81 import org.apache.mina.core.service.IoService;
82 import org.apache.mina.core.service.TransportMetadata;
83 import org.apache.mina.core.session.IoSession;
84 import org.apache.mina.core.session.IoSessionDataStructureFactory;
85 import org.apache.mina.filter.executor.ExecutorFilter;
86 import org.apache.mina.integration.beans.CollectionEditor;
87 import org.apache.mina.integration.beans.ListEditor;
88 import org.apache.mina.integration.beans.MapEditor;
89 import org.apache.mina.integration.beans.PropertyEditorFactory;
90 import org.apache.mina.integration.beans.SetEditor;
91 import org.apache.mina.integration.ognl.IoFilterPropertyAccessor;
92 import org.apache.mina.integration.ognl.IoServicePropertyAccessor;
93 import org.apache.mina.integration.ognl.IoSessionPropertyAccessor;
94 import org.apache.mina.integration.ognl.PropertyTypeConverter;
95 import org.slf4j.Logger;
96 import org.slf4j.LoggerFactory;
97
98
99
100
101
102
103
104
105
106 public class ObjectMBean<T> implements ModelMBean, MBeanRegistration {
107
108 private static final Map<ObjectName, Object> sources =
109 new ConcurrentHashMap<ObjectName, Object>();
110
111 public static Object getSource(ObjectName oname) {
112 return sources.get(oname);
113 }
114
115 static {
116 OgnlRuntime.setPropertyAccessor(IoService.class, new IoServicePropertyAccessor());
117 OgnlRuntime.setPropertyAccessor(IoSession.class, new IoSessionPropertyAccessor());
118 OgnlRuntime.setPropertyAccessor(IoFilter.class, new IoFilterPropertyAccessor());
119 }
120
121 protected final Logger logger = LoggerFactory.getLogger(getClass());
122
123 private final T source;
124 private final TransportMetadata transportMetadata;
125 private final MBeanInfo info;
126 private final Map<String, PropertyDescriptor> propertyDescriptors =
127 new HashMap<String, PropertyDescriptor>();
128 private final TypeConverter typeConverter = new OgnlTypeConverter();
129
130 private volatile MBeanServer server;
131 private volatile ObjectName name;
132
133
134
135
136 public ObjectMBean(T source) {
137 if (source == null) {
138 throw new NullPointerException("source");
139 }
140
141 this.source = source;
142
143 if (source instanceof IoService) {
144 transportMetadata = ((IoService) source).getTransportMetadata();
145 } else if (source instanceof IoSession) {
146 transportMetadata = ((IoSession) source).getTransportMetadata();
147 } else {
148 transportMetadata = null;
149 }
150
151 this.info = createModelMBeanInfo(source);
152 }
153
154 public final Object getAttribute(String fqan) throws AttributeNotFoundException,
155 MBeanException, ReflectionException {
156 try {
157 return convertValue(source.getClass(), fqan, getAttribute0(fqan), false);
158 } catch (AttributeNotFoundException e) {
159 } catch (Throwable e) {
160 throwMBeanException(e);
161 }
162
163
164 PropertyDescriptor pdesc = propertyDescriptors.get(fqan);
165 if (pdesc == null) {
166 throwMBeanException(new IllegalArgumentException(
167 "Unknown attribute: " + fqan));
168 }
169
170 try {
171
172 Object parent = getParent(fqan);
173 boolean writable = isWritable(source.getClass(), pdesc);
174
175 return convertValue(
176 parent.getClass(), getLeafAttributeName(fqan),
177 getAttribute(source, fqan, pdesc.getPropertyType()),
178 writable);
179 } catch (Throwable e) {
180 throwMBeanException(e);
181 }
182
183 throw new IllegalStateException();
184 }
185
186 public final void setAttribute(Attribute attribute)
187 throws AttributeNotFoundException, MBeanException,
188 ReflectionException {
189 String aname = attribute.getName();
190 Object avalue = attribute.getValue();
191
192 try {
193 setAttribute0(aname, avalue);
194 } catch (AttributeNotFoundException e) {
195 } catch (Throwable e) {
196 throwMBeanException(e);
197 }
198
199 PropertyDescriptor pdesc = propertyDescriptors.get(aname);
200 if (pdesc == null) {
201 throwMBeanException(new IllegalArgumentException(
202 "Unknown attribute: " + aname));
203 }
204
205 try {
206 PropertyEditor e = getPropertyEditor(
207 getParent(aname).getClass(),
208 pdesc.getName(), pdesc.getPropertyType());
209 e.setAsText((String) avalue);
210 OgnlContext ctx = (OgnlContext) Ognl.createDefaultContext(source);
211 ctx.setTypeConverter(typeConverter);
212 Ognl.setValue(aname, ctx, source, e.getValue());
213 } catch (Throwable e) {
214 throwMBeanException(e);
215 }
216 }
217
218 public final Object invoke(String name, Object params[], String signature[])
219 throws MBeanException, ReflectionException {
220
221
222 if (name.equals("unregisterMBean")) {
223 try {
224 server.unregisterMBean(this.name);
225 return null;
226 } catch (InstanceNotFoundException e) {
227 throwMBeanException(e);
228 }
229 }
230
231 try {
232 return convertValue(
233 null, null, invoke0(name, params, signature), false);
234 } catch (NoSuchMethodException e) {
235 } catch (Throwable e) {
236 throwMBeanException(e);
237 }
238
239
240 Class<?>[] paramTypes = new Class[signature.length];
241 for (int i = 0; i < paramTypes.length; i ++) {
242 try {
243 paramTypes[i] = getAttributeClass(signature[i]);
244 } catch (ClassNotFoundException e) {
245 throwMBeanException(e);
246 }
247
248 PropertyEditor e = getPropertyEditor(
249 source.getClass(), "p" + i, paramTypes[i]);
250 if (e == null) {
251 throwMBeanException(new RuntimeException("Conversion failure: " + params[i]));
252 }
253
254 e.setValue(params[i]);
255 params[i] = e.getAsText();
256 }
257
258 try {
259
260 for (Method m: source.getClass().getMethods()) {
261 if (!m.getName().equalsIgnoreCase(name)) {
262 continue;
263 }
264 Class<?>[] methodParamTypes = m.getParameterTypes();
265 if (methodParamTypes.length != params.length) {
266 continue;
267 }
268
269 Object[] convertedParams = new Object[params.length];
270 for (int i = 0; i < params.length; i ++) {
271 if (Iterable.class.isAssignableFrom(methodParamTypes[i])) {
272
273 convertedParams = null;
274 break;
275 }
276 PropertyEditor e = getPropertyEditor(source.getClass(), "p" + i, methodParamTypes[i]);
277 if (e == null) {
278 convertedParams = null;
279 break;
280 }
281
282 e.setAsText((String) params[i]);
283 convertedParams[i] = e.getValue();
284 }
285 if (convertedParams == null) {
286 continue;
287 }
288
289 return convertValue(
290 m.getReturnType(), "returnValue",
291 m.invoke(source, convertedParams), false);
292 }
293
294
295 throw new IllegalArgumentException("Failed to find a matching operation: " + name);
296 } catch (Throwable e) {
297 throwMBeanException(e);
298 }
299
300 throw new IllegalStateException();
301 }
302
303 public final T getSource() {
304 return source;
305 }
306
307 public final MBeanServer getServer() {
308 return server;
309 }
310
311 public final ObjectName getName() {
312 return name;
313 }
314
315 public final MBeanInfo getMBeanInfo() {
316 return info;
317 }
318
319 public final AttributeList getAttributes(String names[]) {
320 AttributeList answer = new AttributeList();
321 for (int i = 0; i < names.length; i++) {
322 try {
323 answer.add(new Attribute(names[i], getAttribute(names[i])));
324 } catch (Exception e) {
325
326 }
327 }
328 return answer;
329 }
330
331 public final AttributeList setAttributes(AttributeList attributes) {
332
333 String names[] = new String[attributes.size()];
334 int n = 0;
335 Iterator<Object> items = attributes.iterator();
336 while (items.hasNext()) {
337 Attribute item = (Attribute) items.next();
338 names[n++] = item.getName();
339 try {
340 setAttribute(item);
341 } catch (Exception e) {
342 ;
343 }
344 }
345
346 return getAttributes(names);
347 }
348
349 public final void setManagedResource(Object resource, String type)
350 throws InstanceNotFoundException, InvalidTargetObjectTypeException,
351 MBeanException {
352 throw new RuntimeOperationsException(new UnsupportedOperationException());
353
354 }
355
356 public final void setModelMBeanInfo(ModelMBeanInfo info) throws MBeanException {
357 throw new RuntimeOperationsException(new UnsupportedOperationException());
358 }
359
360 @Override
361 public final String toString() {
362 return source.toString();
363 }
364
365 public void addAttributeChangeNotificationListener(
366 NotificationListener listener, String name, Object handback) {
367 }
368
369 public void removeAttributeChangeNotificationListener(
370 NotificationListener listener, String name)
371 throws ListenerNotFoundException {
372 }
373
374 public void sendAttributeChangeNotification(
375 AttributeChangeNotification notification) throws MBeanException {
376 throw new RuntimeOperationsException(new UnsupportedOperationException());
377 }
378
379 public void sendAttributeChangeNotification(Attribute oldValue,
380 Attribute newValue) throws MBeanException {
381 throw new RuntimeOperationsException(new UnsupportedOperationException());
382 }
383
384 public void sendNotification(Notification notification)
385 throws MBeanException {
386 throw new RuntimeOperationsException(new UnsupportedOperationException());
387 }
388
389 public void sendNotification(String message) throws MBeanException {
390 throw new RuntimeOperationsException(new UnsupportedOperationException());
391
392 }
393
394 public void addNotificationListener(NotificationListener listener,
395 NotificationFilter filter, Object handback)
396 throws IllegalArgumentException {
397 }
398
399 public MBeanNotificationInfo[] getNotificationInfo() {
400 return new MBeanNotificationInfo[0];
401 }
402
403 public void removeNotificationListener(NotificationListener listener)
404 throws ListenerNotFoundException {
405 }
406
407 public void load() throws InstanceNotFoundException, MBeanException,
408 RuntimeOperationsException {
409 throw new RuntimeOperationsException(new UnsupportedOperationException());
410 }
411
412 public void store() throws InstanceNotFoundException, MBeanException,
413 RuntimeOperationsException {
414 throw new RuntimeOperationsException(new UnsupportedOperationException());
415 }
416
417 public final ObjectName preRegister(MBeanServer server, ObjectName name)
418 throws Exception {
419 this.server = server;
420 this.name = name;
421 return name;
422 }
423
424 public final void postRegister(Boolean registrationDone) {
425 if (registrationDone) {
426 sources.put(name, source);
427 }
428 }
429
430 public final void preDeregister() throws Exception {
431 }
432
433 public final void postDeregister() {
434 sources.remove(name);
435 this.server = null;
436 this.name = null;
437 }
438
439 private MBeanInfo createModelMBeanInfo(T source) {
440 String className = source.getClass().getName();
441 String description = "";
442
443 ModelMBeanConstructorInfo[] constructors = new ModelMBeanConstructorInfo[0];
444 ModelMBeanNotificationInfo[] notifications = new ModelMBeanNotificationInfo[0];
445
446 List<ModelMBeanAttributeInfo> attributes = new ArrayList<ModelMBeanAttributeInfo>();
447 List<ModelMBeanOperationInfo> operations = new ArrayList<ModelMBeanOperationInfo>();
448
449 addAttributes(attributes, source);
450 addExtraAttributes(attributes);
451
452 addOperations(operations, source);
453 addExtraOperations(operations);
454 operations.add(new ModelMBeanOperationInfo(
455 "unregisterMBean", "unregisterMBean",
456 new MBeanParameterInfo[0], void.class.getName(),
457 ModelMBeanOperationInfo.ACTION));
458
459 return new ModelMBeanInfoSupport(
460 className, description,
461 attributes.toArray(new ModelMBeanAttributeInfo[attributes.size()]),
462 constructors,
463 operations.toArray(new ModelMBeanOperationInfo[operations.size()]),
464 notifications);
465 }
466
467 private void addAttributes(
468 List<ModelMBeanAttributeInfo> attributes, Object object) {
469 addAttributes(attributes, object, object.getClass(), "");
470 }
471
472 private void addAttributes(
473 List<ModelMBeanAttributeInfo> attributes,
474 Object object, Class<?> type, String prefix) {
475
476 PropertyDescriptor[] pdescs;
477 try {
478 pdescs = Introspector.getBeanInfo(type).getPropertyDescriptors();
479 } catch (IntrospectionException e) {
480 return;
481 }
482
483 for (PropertyDescriptor pdesc: pdescs) {
484
485 if (pdesc.getReadMethod() == null) {
486 continue;
487 }
488
489
490 String attrName = pdesc.getName();
491 Class<?> attrType = pdesc.getPropertyType();
492 if (attrName.equals("class")) {
493 continue;
494 }
495 if (!isReadable(type, attrName)) {
496 continue;
497 }
498
499
500 if (isExpandable(type, attrName)) {
501 expandAttribute(attributes, object, prefix, pdesc);
502 continue;
503 }
504
505
506 String fqan = prefix + attrName;
507 boolean writable = isWritable(type, pdesc);
508 attributes.add(new ModelMBeanAttributeInfo(
509 fqan, convertType(
510 object.getClass(), attrName, attrType, writable).getName(),
511 pdesc.getShortDescription(), true, writable, false));
512
513 propertyDescriptors.put(fqan, pdesc);
514 }
515 }
516
517 private boolean isWritable(Class<?> type, PropertyDescriptor pdesc) {
518 if (type == null) {
519 throw new NullPointerException("type");
520 }
521 if (pdesc == null) {
522 return false;
523 }
524 String attrName = pdesc.getName();
525 Class<?> attrType = pdesc.getPropertyType();
526 boolean writable = pdesc.getWriteMethod() != null || isWritable(type, attrName);
527 if (getPropertyEditor(type, attrName, attrType) == null) {
528 writable = false;
529 }
530 return writable;
531 }
532
533 private void expandAttribute(
534 List<ModelMBeanAttributeInfo> attributes,
535 Object object, String prefix, PropertyDescriptor pdesc) {
536 Object property;
537 String attrName = pdesc.getName();
538 try {
539 property = getAttribute(object, attrName, pdesc.getPropertyType());
540 } catch (Exception e) {
541 logger.debug("Unexpected exception.", e);
542 return;
543 }
544
545 if (property == null) {
546 return;
547 }
548
549 addAttributes(
550 attributes,
551 property, property.getClass(),
552 prefix + attrName + '.');
553 }
554
555 private void addOperations(
556 List<ModelMBeanOperationInfo> operations, Object object) {
557
558 for (Method m: object.getClass().getMethods()) {
559 String mname = m.getName();
560
561
562 if (mname.startsWith("is") || mname.startsWith("get") ||
563 mname.startsWith("set")) {
564 continue;
565 }
566
567
568 if (mname.matches(
569 "(wait|notify|notifyAll|toString|equals|compareTo|hashCode|clone)")) {
570 continue;
571 }
572
573
574 if (!isOperation(mname, m.getParameterTypes())) {
575 continue;
576 }
577
578 List<MBeanParameterInfo> signature = new ArrayList<MBeanParameterInfo>();
579 int i = 1;
580 for (Class<?> paramType: m.getParameterTypes()) {
581 String paramName = "p" + (i ++);
582 if (getPropertyEditor(source.getClass(), paramName, paramType) == null) {
583 continue;
584 }
585 signature.add(new MBeanParameterInfo(
586 paramName, convertType(
587 null, null, paramType, true).getName(),
588 paramName));
589 }
590
591 Class<?> returnType = convertType(null, null, m.getReturnType(), false);
592 operations.add(new ModelMBeanOperationInfo(
593 m.getName(), m.getName(),
594 signature.toArray(new MBeanParameterInfo[signature.size()]),
595 returnType.getName(), ModelMBeanOperationInfo.ACTION));
596 }
597 }
598
599 private Object getParent(String fqan) throws OgnlException {
600 Object parent;
601 int dotIndex = fqan.lastIndexOf('.');
602 if (dotIndex < 0) {
603 parent = source;
604 } else {
605 parent = getAttribute(source, fqan.substring(0, dotIndex), null);
606 }
607 return parent;
608 }
609
610 private String getLeafAttributeName(String fqan) {
611 int dotIndex = fqan.lastIndexOf('.');
612 if (dotIndex < 0) {
613 return fqan;
614 }
615 return fqan.substring(dotIndex + 1);
616 }
617
618 private Class<?> getAttributeClass(String signature)
619 throws ClassNotFoundException {
620 if (signature.equals(Boolean.TYPE.getName())) {
621 return Boolean.TYPE;
622 }
623 if (signature.equals(Byte.TYPE.getName())) {
624 return Byte.TYPE;
625 }
626 if (signature.equals(Character.TYPE.getName())) {
627 return Character.TYPE;
628 }
629 if (signature.equals(Double.TYPE.getName())) {
630 return Double.TYPE;
631 }
632 if (signature.equals(Float.TYPE.getName())) {
633 return Float.TYPE;
634 }
635 if (signature.equals(Integer.TYPE.getName())) {
636 return Integer.TYPE;
637 }
638 if (signature.equals(Long.TYPE.getName())) {
639 return Long.TYPE;
640 }
641 if (signature.equals(Short.TYPE.getName())) {
642 return Short.TYPE;
643 }
644
645 try {
646 ClassLoader cl = Thread.currentThread().getContextClassLoader();
647 if (cl != null) {
648 return cl.loadClass(signature);
649 }
650 } catch (ClassNotFoundException e) {
651 }
652
653 return Class.forName(signature);
654 }
655
656 private Object getAttribute(Object object, String fqan, Class<?> attrType) throws OgnlException {
657 Object property;
658 OgnlContext ctx = (OgnlContext) Ognl.createDefaultContext(object);
659 ctx.setTypeConverter(new OgnlTypeConverter());
660 if (attrType == null) {
661 property = Ognl.getValue(fqan, ctx, object);
662 } else {
663 property = Ognl.getValue(fqan, ctx, object, attrType);
664 }
665 return property;
666 }
667
668 @SuppressWarnings("unused")
669 private Class<?> convertType(Class<?> type, String attrName, Class<?> attrType, boolean writable) {
670 if (attrName != null && (attrType == Long.class || attrType == long.class)) {
671 if (attrName.endsWith("Time") &&
672 attrName.indexOf("Total") < 0 &&
673 attrName.indexOf("Min") < 0 &&
674 attrName.indexOf("Max") < 0 &&
675 attrName.indexOf("Avg") < 0 &&
676 attrName.indexOf("Average") < 0 &&
677 !propertyDescriptors.containsKey(attrName + "InMillis")) {
678 return Date.class;
679 }
680 }
681
682 if (IoFilterChain.class.isAssignableFrom(attrType)) {
683 return Map.class;
684 }
685
686 if (IoFilterChainBuilder.class.isAssignableFrom(attrType)) {
687 return Map.class;
688 }
689
690 if (!writable) {
691 if (Collection.class.isAssignableFrom(attrType) ||
692 Map.class.isAssignableFrom(attrType)) {
693 if (List.class.isAssignableFrom(attrType)) {
694 return List.class;
695 }
696 if (Set.class.isAssignableFrom(attrType)) {
697 return Set.class;
698 }
699 if (Map.class.isAssignableFrom(attrType)) {
700 return Map.class;
701 }
702 return Collection.class;
703 }
704
705 if (attrType.isPrimitive() ||
706 Date.class.isAssignableFrom(attrType) ||
707 Boolean.class.isAssignableFrom(attrType) ||
708 Character.class.isAssignableFrom(attrType) ||
709 Number.class.isAssignableFrom(attrType)) {
710 if (attrName == null || !attrName.endsWith("InMillis") ||
711 !propertyDescriptors.containsKey(
712 attrName.substring(0, attrName.length() - 8))) {
713 return attrType;
714 }
715 }
716 }
717
718 return String.class;
719 }
720
721 private Object convertValue(Class<?> type, String attrName, Object v, boolean writable) {
722 if (v == null) {
723 return null;
724 }
725
726 if (attrName != null && v instanceof Long) {
727 if (attrName.endsWith("Time") &&
728 attrName.indexOf("Total") < 0 &&
729 attrName.indexOf("Min") < 0 &&
730 attrName.indexOf("Max") < 0 &&
731 attrName.indexOf("Avg") < 0 &&
732 attrName.indexOf("Average") < 0 &&
733 !propertyDescriptors.containsKey(attrName + "InMillis")) {
734 long time = (Long) v;
735 if (time <= 0) {
736 return null;
737 }
738 System.out.println("Converted to date");
739 return new Date((Long) v);
740 }
741 }
742
743 if (v instanceof IoSessionDataStructureFactory ||
744 v instanceof IoHandler) {
745 return v.getClass().getName();
746 }
747
748 if (v instanceof IoFilterChainBuilder) {
749 Map<String, String> filterMapping = new LinkedHashMap<String, String>();
750 if (v instanceof DefaultIoFilterChainBuilder) {
751 for (IoFilterChain.Entry e: ((DefaultIoFilterChainBuilder) v).getAll()) {
752 filterMapping.put(e.getName(), e.getFilter().getClass().getName());
753 }
754 } else {
755 filterMapping.put("Unknown builder type", v.getClass().getName());
756 }
757 return filterMapping;
758 }
759
760 if (v instanceof IoFilterChain) {
761 Map<String, String> filterMapping = new LinkedHashMap<String, String>();
762 for (IoFilterChain.Entry e: ((IoFilterChain) v).getAll()) {
763 filterMapping.put(e.getName(), e.getFilter().getClass().getName());
764 }
765 return filterMapping;
766 }
767
768 if (!writable) {
769 if (v instanceof Collection || v instanceof Map) {
770 if (v instanceof List) {
771 return convertCollection(v, new ArrayList<Object>());
772 }
773 if (v instanceof Set) {
774 return convertCollection(v, new LinkedHashSet<Object>());
775 }
776 if (v instanceof Map) {
777 return convertCollection(v, new LinkedHashMap<Object, Object>());
778 }
779 return convertCollection(v, new ArrayList<Object>());
780 }
781
782 if (v instanceof Date ||
783 v instanceof Boolean ||
784 v instanceof Character ||
785 v instanceof Number) {
786 if (attrName == null || !attrName.endsWith("InMillis") ||
787 !propertyDescriptors.containsKey(
788 attrName.substring(0, attrName.length() - 8))) {
789 return v;
790 }
791 }
792 }
793
794 PropertyEditor editor = getPropertyEditor(type, attrName, v.getClass());
795 if (editor != null) {
796 editor.setValue(v);
797 return editor.getAsText();
798 }
799
800 return v.toString();
801 }
802
803 private Object convertCollection(Object src, Collection<Object> dst) {
804 Collection<?> srcCol = (Collection<?>) src;
805 for (Object e: srcCol) {
806 Object convertedValue = convertValue(dst.getClass(), "element", e, false);
807 if (e != null && convertedValue == null) {
808 convertedValue = e.toString();
809 }
810 dst.add(convertedValue);
811 }
812 return dst;
813 }
814
815 private Object convertCollection(Object src, Map<Object, Object> dst) {
816 Map<?, ?> srcCol = (Map<?, ?>) src;
817 for (Map.Entry<?, ?> e: srcCol.entrySet()) {
818 Object convertedKey = convertValue(dst.getClass(), "key", e.getKey(), false);
819 Object convertedValue = convertValue(dst.getClass(), "value", e.getValue(), false);
820 if (e.getKey() != null && convertedKey == null) {
821 convertedKey = e.getKey().toString();
822 }
823 if (e.getValue() != null && convertedValue == null) {
824 convertedKey = e.getValue().toString();
825 }
826 dst.put(convertedKey, convertedValue);
827 }
828 return dst;
829 }
830
831 private void throwMBeanException(Throwable e) throws MBeanException {
832 if (e instanceof OgnlException) {
833 OgnlException ognle = (OgnlException) e;
834 if (ognle.getReason() != null) {
835 throwMBeanException(ognle.getReason());
836 } else {
837 String message = ognle.getMessage();
838 if (e instanceof NoSuchPropertyException) {
839 message = "No such property: " + message;
840 } else if (e instanceof ExpressionSyntaxException) {
841 message = "Illegal expression syntax: " + message;
842 } else if (e instanceof InappropriateExpressionException) {
843 message = "Inappropriate expression: " + message;
844 }
845 e = new IllegalArgumentException(ognle.getMessage());
846 e.setStackTrace(ognle.getStackTrace());
847 }
848 }
849 if (e instanceof InvocationTargetException) {
850 throwMBeanException(e.getCause());
851 }
852
853 logger.warn("Unexpected exception.", e);
854 if (e.getClass().getPackage().getName().matches("javax?\\..+")) {
855 if (e instanceof Exception) {
856 throw new MBeanException((Exception) e, e.getMessage());
857 } else {
858 throw new MBeanException(
859 new RuntimeException(e), e.getMessage());
860 }
861 }
862
863 throw new MBeanException(new RuntimeException(
864 e.getClass().getName() + ": " + e.getMessage()),
865 e.getMessage());
866 }
867
868 protected Object getAttribute0(String fqan) throws Exception {
869 throw new AttributeNotFoundException(fqan);
870 }
871
872 @SuppressWarnings("unused")
873 protected void setAttribute0(String attrName, Object attrValue) throws Exception {
874 throw new AttributeNotFoundException(attrName);
875 }
876
877 @SuppressWarnings("unused")
878 protected Object invoke0(String name, Object params[], String signature[]) throws Exception {
879 throw new NoSuchMethodException();
880 }
881
882 protected boolean isReadable(Class<?> type, String attrName) {
883 if (IoService.class.isAssignableFrom(type) && attrName.equals("filterChain")) {
884 return false;
885 }
886 if (IoService.class.isAssignableFrom(type) && attrName.equals("localAddress")) {
887 return false;
888 }
889 if (IoService.class.isAssignableFrom(type) && attrName.equals("defaultLocalAddress")) {
890 return false;
891 }
892 if (IoSession.class.isAssignableFrom(type) && attrName.equals("attachment")) {
893 return false;
894 }
895 if (IoSession.class.isAssignableFrom(type) && attrName.equals("attributeKeys")) {
896 return false;
897 }
898 if (IoSession.class.isAssignableFrom(type) && attrName.equals("closeFuture")) {
899 return false;
900 }
901
902 if (ThreadPoolExecutor.class.isAssignableFrom(type) && attrName.equals("queue")) {
903 return false;
904 }
905
906 return true;
907 }
908
909 protected boolean isWritable(Class<?> type, String attrName) {
910 if (IoService.class.isAssignableFrom(type) && attrName.startsWith("defaultLocalAddress")) {
911 return true;
912 }
913 return false;
914 }
915
916 @SuppressWarnings("unused")
917 protected Class<?> getElementType(Class<?> type, String attrName) {
918 if (transportMetadata != null &&
919 IoAcceptor.class.isAssignableFrom(type) &&
920 "defaultLocalAddresses".equals(attrName)) {
921 return transportMetadata.getAddressType();
922 }
923 return String.class;
924 }
925
926 @SuppressWarnings("unused")
927 protected Class<?> getMapKeyType(Class<?> type, String attrName) {
928 return String.class;
929 }
930
931 @SuppressWarnings("unused")
932 protected Class<?> getMapValueType(Class<?> type, String attrName) {
933 return String.class;
934 }
935
936 protected boolean isExpandable(Class<?> type, String attrName) {
937 if (IoService.class.isAssignableFrom(type) && attrName.equals("sessionConfig")) {
938 return true;
939 }
940 if (IoService.class.isAssignableFrom(type) && attrName.equals("transportMetadata")) {
941 return true;
942 }
943 if (IoSession.class.isAssignableFrom(type) && attrName.equals("config")) {
944 return true;
945 }
946 if (IoSession.class.isAssignableFrom(type) && attrName.equals("transportMetadata")) {
947 return true;
948 }
949
950 if (ExecutorFilter.class.isAssignableFrom(type)) {
951 if (attrName.equals("executor")) {
952 return true;
953 }
954 }
955 if (ThreadPoolExecutor.class.isAssignableFrom(type)) {
956 if (attrName.equals("queueHandler")) {
957 return true;
958 }
959 }
960 return false;
961 }
962
963 @SuppressWarnings("unused")
964 protected boolean isOperation(String methodName, Class<?>[] paramTypes) {
965 return true;
966 }
967
968 @SuppressWarnings("unused")
969 protected void addExtraAttributes(List<ModelMBeanAttributeInfo> attributes) {}
970
971 @SuppressWarnings("unused")
972 protected void addExtraOperations(List<ModelMBeanOperationInfo> operations) {}
973
974 protected PropertyEditor getPropertyEditor(Class<?> type, String attrName, Class<?> attrType) {
975 if (type == null) {
976 throw new NullPointerException("type");
977 }
978 if (attrName == null) {
979 throw new NullPointerException("attrName");
980 }
981
982 if (transportMetadata != null && attrType == SocketAddress.class) {
983 attrType = transportMetadata.getAddressType();
984 }
985
986 if (attrName != null && (attrType == Long.class || attrType == long.class)) {
987 if (attrName.endsWith("Time") &&
988 attrName.indexOf("Total") < 0 &&
989 attrName.indexOf("Min") < 0 &&
990 attrName.indexOf("Max") < 0 &&
991 attrName.indexOf("Avg") < 0 &&
992 attrName.indexOf("Average") < 0 &&
993 !propertyDescriptors.containsKey(attrName + "InMillis")) {
994 return PropertyEditorFactory.getInstance(Date.class);
995 }
996
997 if (attrName.equals("id")) {
998 return PropertyEditorFactory.getInstance(String.class);
999 }
1000 }
1001
1002 if (type != null) {
1003 if (List.class.isAssignableFrom(attrType)) {
1004 return new ListEditor(getElementType(type, attrName));
1005 }
1006 if (Set.class.isAssignableFrom(attrType)) {
1007 return new SetEditor(getElementType(type, attrName));
1008 }
1009 if (Collection.class.isAssignableFrom(attrType)) {
1010 return new CollectionEditor(getElementType(type, attrName));
1011 }
1012 if (Map.class.isAssignableFrom(attrType)) {
1013 return new MapEditor(
1014 getMapKeyType(type, attrName),
1015 getMapValueType(type, attrName));
1016 }
1017 }
1018
1019 return PropertyEditorFactory.getInstance(attrType);
1020 }
1021
1022 private class OgnlTypeConverter extends PropertyTypeConverter {
1023 @Override
1024 protected PropertyEditor getPropertyEditor(
1025 Class<?> type, String attrName, Class<?> attrType) {
1026 return ObjectMBean.this.getPropertyEditor(type, attrName, attrType);
1027 }
1028 }
1029 }