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