1 22 package org.jboss.mx.mxbean; 23 24 import java.io.Serializable ; 25 import java.lang.reflect.InvocationHandler ; 26 import java.lang.reflect.Method ; 27 import java.lang.reflect.Proxy ; 28 import java.lang.reflect.Type ; 29 import java.util.HashMap ; 30 import java.util.Map ; 31 32 import javax.management.Attribute ; 33 import javax.management.MBeanAttributeInfo ; 34 import javax.management.MBeanOperationInfo ; 35 import javax.management.MBeanParameterInfo ; 36 import javax.management.MBeanServerConnection ; 37 import javax.management.NotificationBroadcaster ; 38 import javax.management.NotificationEmitter ; 39 import javax.management.NotificationFilter ; 40 import javax.management.NotificationListener ; 41 import javax.management.ObjectName ; 42 import javax.management.openmbean.OpenMBeanAttributeInfo ; 43 import javax.management.openmbean.OpenMBeanInfo ; 44 import javax.management.openmbean.OpenMBeanOperationInfo ; 45 import javax.management.openmbean.OpenMBeanParameterInfo ; 46 import javax.management.openmbean.OpenType ; 47 48 import org.jboss.mx.util.JMXExceptionDecoder; 49 50 56 public class MXBeanInvocationHandler implements InvocationHandler , Serializable 57 { 58 59 private static final long serialVersionUID = -2872014223541692039L; 60 61 private static final Class [] LISTENER = new Class [] { NotificationListener .class }; 62 private static final Class [] TRIPLET = new Class [] { NotificationListener .class, NotificationFilter .class, Object .class }; 63 64 private static final Method EQUALS; 65 private static final Method HASH_CODE; 66 private static final Method TO_STRING; 67 68 private static final Method ADD_NOTIFICATION_LISTENER; 69 private static final Method GET_NOTIFICATION_INFO; 70 private static final Method REMOVE_NOTIFICATION_LISTENER; 71 private static final Method REMOVE_NOTIFICATION_LISTENER_TRIPLET; 72 73 74 private MBeanServerConnection mbeanServerConnection; 75 76 77 private Class <?> mxbeanInterface; 78 79 80 private ObjectName objectName; 81 82 83 private transient Map <Method , Action> mappings; 84 85 86 private transient OpenMBeanInfo mbeanInfo; 87 88 static 89 { 90 try 91 { 92 ADD_NOTIFICATION_LISTENER = NotificationBroadcaster .class.getDeclaredMethod("addNotificationListener", TRIPLET); 93 GET_NOTIFICATION_INFO = NotificationBroadcaster .class.getDeclaredMethod("getNotificationInfo", new Class [0]); 94 REMOVE_NOTIFICATION_LISTENER = NotificationBroadcaster .class.getDeclaredMethod("removeNotificationListener", LISTENER); 95 REMOVE_NOTIFICATION_LISTENER_TRIPLET = NotificationEmitter .class.getDeclaredMethod("removeNotificationListener", TRIPLET); 96 EQUALS = Object .class.getDeclaredMethod("equals", new Class [] { Object .class }); 97 HASH_CODE = Object .class.getDeclaredMethod("hashCode", new Class [0]); 98 TO_STRING = Object .class.getDeclaredMethod("toString", new Class [0]); 99 } 100 catch (Exception e) 101 { 102 throw new RuntimeException (e); 103 } 104 } 105 106 113 public MXBeanInvocationHandler(MBeanServerConnection mbeanServerConnection, Class <?> mxbeanInterface, ObjectName objectName) 114 { 115 if (mbeanServerConnection == null) 116 throw new IllegalArgumentException ("Null mbeanServerConnection"); 117 if (mxbeanInterface == null) 118 throw new IllegalArgumentException ("Null mxmbeanInterface"); 119 if (objectName == null) 120 throw new IllegalArgumentException ("Null objectName"); 121 122 this.mbeanServerConnection = mbeanServerConnection; 123 this.mxbeanInterface = mxbeanInterface; 124 this.objectName = objectName; 125 } 126 127 132 public MBeanServerConnection getMBeanServerConnection() 133 { 134 return mbeanServerConnection; 135 } 136 137 142 public Class <?> getMXBeanInterface() 143 { 144 return mxbeanInterface; 145 } 146 147 152 public ObjectName getObjectName() 153 { 154 return objectName; 155 } 156 157 public Object invoke(Object proxy, Method method, Object [] args) throws Throwable 158 { 159 try 160 { 161 return getAction(proxy, method).perform(args); 162 } 163 catch (Throwable t) 164 { 165 throw JMXExceptionDecoder.decode(t); 166 } 167 } 168 169 176 private Action getAction(Object proxy, Method method) throws Throwable 177 { 178 if (mappings == null) 181 mappings = getMappings(proxy); 182 183 Action result = mappings.get(method); 185 if (result == null) 186 throw new UnsupportedOperationException ("Unknown method: " + method); 187 188 return result; 190 } 191 192 199 private Map <Method , Action> getMappings(Object proxy) throws Throwable 200 { 201 mbeanInfo = (OpenMBeanInfo ) mbeanServerConnection.getMBeanInfo(objectName); 202 Map <String , OpenMBeanAttributeInfo > attributesMap = new HashMap <String , OpenMBeanAttributeInfo >(); 203 MBeanAttributeInfo [] attributes = mbeanInfo.getAttributes(); 204 for (int i = 0; i < attributes.length; ++i) 205 { 206 OpenMBeanAttributeInfo openAttribute = (OpenMBeanAttributeInfo ) attributes[i]; 207 attributesMap.put(openAttribute.getName(), openAttribute); 208 } 209 MBeanOperationInfo [] operations = mbeanInfo.getOperations(); 210 211 Map <Method , Action> result = new HashMap <Method , Action>(); 212 213 Class [] interfaces = proxy.getClass().getInterfaces(); 214 for (int i = 0; i < interfaces.length; ++i) 215 { 216 if (NotificationBroadcaster .class.isAssignableFrom(interfaces[i])) 217 { 218 result.put(ADD_NOTIFICATION_LISTENER, new AddNotificationListenerAction()); 219 result.put(GET_NOTIFICATION_INFO, new GetNotificationInfoAction()); 220 result.put(REMOVE_NOTIFICATION_LISTENER, new RemoveNotificationListenerAction()); 221 result.put(REMOVE_NOTIFICATION_LISTENER_TRIPLET, new RemoveNotificationListenerTripletAction()); 222 } 223 else 224 { 225 Method [] methods = interfaces[i].getMethods(); 226 for (Method method : methods) 227 { 228 String methodName = method.getName(); 229 Class returnType = method.getReturnType(); 230 Class [] parameterTypes = method.getParameterTypes(); 231 232 if (methodName.startsWith("get") && 234 methodName.length() > 3 && 235 Void.TYPE.equals(returnType) == false && 236 parameterTypes.length == 0) 237 { 238 String name = methodName.substring(3); 239 OpenMBeanAttributeInfo attribute = attributesMap.get(name); 240 if (attribute != null) 241 { 242 Type type = method.getGenericReturnType(); 243 result.put(method, new GetAction(attribute, type)); 244 continue; 245 } 246 } 247 else if(methodName.startsWith("is") && 249 methodName.length() > 2 && 250 Boolean.TYPE.equals(returnType) && 251 parameterTypes.length == 0) 252 { 253 String name = methodName.substring(2); 254 OpenMBeanAttributeInfo attribute = attributesMap.get(name); 255 if (attribute != null) 256 { 257 Type type = method.getGenericReturnType(); 258 result.put(method, new GetAction(attribute, type)); 259 continue; 260 } 261 } 262 else if(methodName.startsWith("set") && 264 methodName.length() > 3 && 265 Void.TYPE.equals(returnType) && 266 parameterTypes.length == 1) 267 { 268 String name = methodName.substring(3); 269 OpenMBeanAttributeInfo attribute = attributesMap.get(name); 270 if (attribute != null) 271 { 272 result.put(method, new SetAction(attribute)); 273 continue; 274 } 275 } 276 OpenMBeanOperationInfo operation = findOperation(methodName, method.getGenericParameterTypes(), operations); 278 if (operation != null) 279 { 280 String [] signature = getSignature(method); 281 Type type = method.getGenericReturnType(); 282 result.put(method, new InvokeAction(operation, signature, type)); 283 } 284 else 285 { 286 result.put(method, new InvalidAction(method)); 287 } 288 } 289 } 290 } 291 292 result.put(EQUALS, new EqualsAction()); 294 result.put(HASH_CODE, new HashCodeAction()); 295 result.put(TO_STRING, new ToStringAction()); 296 297 return result; 298 } 299 300 private static OpenMBeanOperationInfo findOperation(String name, Type [] parameterTypes, MBeanOperationInfo [] operations) 301 { 302 OpenType [] signature = getSignature(parameterTypes); 303 for (int i = 0; i < operations.length; ++i) 304 { 305 if (operations[i].getName().equals(name) == false) 306 continue; 307 MBeanParameterInfo [] parameters = operations[i].getSignature(); 308 boolean match = true; 309 for (int p = 0; p < parameters.length && match; ++p) 310 { 311 OpenMBeanParameterInfo openMBeanParameterInfo = (OpenMBeanParameterInfo ) parameters[p]; 312 if (signature[p].equals(openMBeanParameterInfo.getOpenType()) == false) 313 match = false; 314 } 315 if (match) 316 return (OpenMBeanOperationInfo ) operations[i]; 317 } 318 return null; 319 } 320 321 private static String [] getSignature(final Method method) 322 { 323 Class [] parameterTypes = method.getParameterTypes(); 324 String [] signature = new String [parameterTypes.length]; 325 for (int p = 0; p < parameterTypes.length; ++p) 326 signature[p] = parameterTypes[p].getName(); 327 return signature; 328 } 329 330 private static OpenType [] getSignature(final Type [] parameterTypes) 331 { 332 OpenType [] signature = new OpenType [parameterTypes.length]; 333 for (int p = 0; p < parameterTypes.length; ++p) 334 signature[p] = MXBeanUtils.getOpenType(parameterTypes[p]); 335 return signature; 336 } 337 338 private interface Action 339 { 340 public Object perform(Object [] args) throws Throwable ; 341 } 342 343 private class GetAction implements Action 344 { 345 private OpenMBeanAttributeInfo attribute; 346 private Type type; 347 348 public GetAction(OpenMBeanAttributeInfo attribute, Type type) 349 { 350 this.attribute = attribute; 351 this.type = type; 352 } 353 354 public Object perform(Object [] args) throws Throwable 355 { 356 Object result = mbeanServerConnection.getAttribute(objectName, attribute.getName()); 357 358 return MXBeanUtils.reconstruct(attribute.getOpenType(), type, result, "Get attribute: " + attribute.getName()); 359 } 360 } 361 362 private class SetAction implements Action 363 { 364 private OpenMBeanAttributeInfo attribute; 365 366 public SetAction(OpenMBeanAttributeInfo attribute) 367 { 368 this.attribute = attribute; 369 } 370 371 public Object perform(Object [] args) throws Throwable 372 { 373 Object value = MXBeanUtils.construct(attribute.getOpenType(), args[0], "Set attribute: " + attribute.getName()); 374 375 Attribute attr = new Attribute (attribute.getName(), value); 376 mbeanServerConnection.setAttribute(objectName, attr); 377 return null; 378 } 379 } 380 381 private class InvokeAction implements Action 382 { 383 private OpenMBeanOperationInfo operation; 384 private String [] signature; 385 private Type type; 386 387 public InvokeAction(OpenMBeanOperationInfo operation, String [] signature, Type type) 388 { 389 this.operation = operation; 390 this.signature = signature; 391 this.type = type; 392 } 393 394 public Object perform(Object [] args) throws Throwable 395 { 396 MBeanParameterInfo [] parameters = operation.getSignature(); 397 Object [] arguments = new Object [args.length]; 398 for (int i = 0; i < parameters.length; ++i) 399 { 400 OpenMBeanParameterInfo parameter = (OpenMBeanParameterInfo ) parameters[i]; 401 arguments[i] = MXBeanUtils.construct(parameter.getOpenType(), args[i], operation.getName()); 402 } 403 404 Object result = mbeanServerConnection.invoke(objectName, operation.getName(), arguments, signature); 405 406 return MXBeanUtils.reconstruct(operation.getReturnOpenType(), type, result, operation.getName()); 407 } 408 } 409 410 private class InvalidAction implements Action 411 { 412 private Method method; 413 414 public InvalidAction(Method method) 415 { 416 this.method = method; 417 } 418 419 public Object perform(Object [] args) throws Throwable 420 { 421 throw new UnsupportedOperationException (method + " is not mapped to the MBeanInfo operations for " + objectName); 422 } 423 } 424 425 private class EqualsAction implements Action 426 { 427 public Object perform(Object [] args) throws Throwable 428 { 429 Object object = args[0]; 430 if (object == null || object instanceof Proxy == false) 431 return false; 432 InvocationHandler handler = Proxy.getInvocationHandler(object); 433 if (handler instanceof MXBeanInvocationHandler == false) 434 return false; 435 MXBeanInvocationHandler other = (MXBeanInvocationHandler) handler; 436 return mbeanServerConnection.equals(other.mbeanServerConnection) && objectName.equals(other.objectName); 437 } 438 } 439 440 private class HashCodeAction implements Action 441 { 442 public Object perform(Object [] args) throws Throwable 443 { 444 return objectName.hashCode(); 445 } 446 } 447 448 private class ToStringAction implements Action 449 { 450 public Object perform(Object [] args) throws Throwable 451 { 452 return "MXBeanInvocationHandler(" + objectName + ")"; 453 } 454 } 455 456 private class AddNotificationListenerAction implements Action 457 { 458 public Object perform(Object [] args) throws Throwable 459 { 460 NotificationListener listener = (NotificationListener ) args[0]; 461 NotificationFilter filter = (NotificationFilter ) args[1]; 462 Object handback = args[2]; 463 mbeanServerConnection.addNotificationListener(objectName, listener, filter, handback); 464 return null; 465 } 466 } 467 468 private class GetNotificationInfoAction implements Action 469 { 470 public Object perform(Object [] args) throws Throwable 471 { 472 return mbeanServerConnection.getMBeanInfo(objectName).getNotifications(); 473 } 474 } 475 476 private class RemoveNotificationListenerAction implements Action 477 { 478 public Object perform(Object [] args) throws Throwable 479 { 480 NotificationListener listener = (NotificationListener ) args[0]; 481 mbeanServerConnection.removeNotificationListener(objectName, listener); 482 return null; 483 } 484 } 485 486 private class RemoveNotificationListenerTripletAction implements Action 487 { 488 public Object perform(Object [] args) throws Throwable 489 { 490 NotificationListener listener = (NotificationListener ) args[0]; 491 NotificationFilter filter = (NotificationFilter ) args[1]; 492 Object handback = args[2]; 493 mbeanServerConnection.removeNotificationListener(objectName, listener, filter, handback); 494 return null; 495 } 496 } 497 } 498 | Popular Tags |