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