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