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