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