Frames | No Frames |
1: /* MBeanServerInvocationHandler.java -- Provides a proxy for a bean. 2: Copyright (C) 2007 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package javax.management; 39: 40: import gnu.javax.management.Translator; 41: 42: import java.lang.reflect.InvocationHandler; 43: import java.lang.reflect.Method; 44: import java.lang.reflect.Proxy; 45: 46: /** 47: * <p> 48: * Provides a proxy for a management bean. The methods 49: * of the given interface are fulfilled by redirecting the 50: * calls over an {@link MBeanServerConnection} to the bean 51: * specified by the supplied {@link ObjectName}. 52: * </p> 53: * <p> 54: * The {@link java.lang.reflect.InvocationHandler} also makes 55: * provision for {@link MXBean}s by providing type conversion 56: * according to the rules defined for these beans. The input 57: * parameters are converted to their equivalent open type before 58: * calling the method, and then the return value is converted 59: * back from its open type to the appropriate Java type. For 60: * example, a method that takes an {@link Enum} as input and 61: * returns a {@link java.util.List} will have the input value 62: * converted from an {@link Enum} to a {@link String}, while 63: * the return value will be converted from its return type 64: * (an appropriately typed array) to a {@link java.util.List}. 65: * </p> 66: * <p> 67: * The proxy has special cases for the {@link Object#equals(Object)}, 68: * {@link Object#hashCode()} and {@link Object#toString()} methods. 69: * Unless they are specified explictly by the interface, the 70: * following behaviour is provided for these methods by the proxy: 71: * </p> 72: * <ul> 73: * <li><code>equals(Object)</code> returns true if the other object 74: * is an {@link MBeanServerInvocationHandler} with the same 75: * {@link MBeanServerConnection} and {@link ObjectName}. If an 76: * interface class was specified on construction for one of the 77: * proxies, then the same class must have also been specified 78: * for the other.</li> 79: * <li><code>hashCode()</code> returns the same value for 80: * equivalent proxies.</li> 81: * <li><code>toString()</code> returns a textual representation 82: * of the proxy.</li> 83: * </ul> 84: * 85: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 86: * @since 1.5 87: */ 88: public class MBeanServerInvocationHandler 89: implements InvocationHandler 90: { 91: 92: /** 93: * The connection used to make the calls. 94: */ 95: private MBeanServerConnection conn; 96: 97: /** 98: * The name of the bean to perform operations on. 99: */ 100: private ObjectName name; 101: 102: /** 103: * True if this proxy is for an {@link MXBean}. 104: */ 105: private boolean mxBean; 106: 107: /** 108: * The interface class associated with the bean. 109: */ 110: private Class<?> iface; 111: 112: /** 113: * Constructs a new {@link MBeanServerInvocationHandler} 114: * which forwards methods to the supplied bean via the 115: * given {@link MBeanServerConnection}. This constructor 116: * is used in preference to 117: * {@link JMX#newMBeanProxy(MBeanServerConnection, ObjectName, 118: * Class<T>)} if you wish to make your own call to 119: * {@link java.lang.reflect.Proxy#newInstance(ClassLoader, 120: * Class[], java.lang.reflect.InvocationHandler)} with 121: * a different {@link ClassLoader}. Calling this constructor 122: * is equivalent to <code>MBeanServerInvocationHandler(conn, 123: * name, false)</code>. The other constructor should be used 124: * instead if the bean being proxied is an {@link MXBean}. 125: * 126: * @param conn the connection through which methods will 127: * be forwarded to the bean. 128: * @param name the name of the bean to use to provide the 129: * actual calls. 130: */ 131: public MBeanServerInvocationHandler(MBeanServerConnection conn, 132: ObjectName name) 133: { 134: this(conn, name, false); 135: } 136: 137: /** 138: * Constructs a new {@link MBeanServerInvocationHandler} 139: * which forwards methods to the supplied bean via the 140: * given {@link MBeanServerConnection}. This constructor 141: * is used in preference to 142: * {@link JMX#newMBeanProxy(MBeanServerConnection, ObjectName, 143: * Class<T>)} if you wish to make your own call to 144: * {@link java.lang.reflect.Proxy#newInstance(ClassLoader, 145: * Class[], java.lang.reflect.InvocationHandler)} with 146: * a different {@link ClassLoader}. 147: * 148: * @param conn the connection through which methods will 149: * be forwarded to the bean. 150: * @param name the name of the bean to use to provide the 151: * actual calls. 152: * @param mxBean true if the bean being proxied is an 153: * {@link MXBean}. 154: * @since 1.6 155: */ 156: public MBeanServerInvocationHandler(MBeanServerConnection conn, 157: ObjectName name, boolean mxBean) 158: { 159: this.conn = conn; 160: this.name = name; 161: this.mxBean = mxBean; 162: } 163: 164: /** 165: * Returns the connection through which the calls to the bean 166: * will be made. 167: * 168: * @return the connection being used to forward the calls to 169: * the bean. 170: * @since 1.6 171: */ 172: public MBeanServerConnection getMBeanServerConnection() 173: { 174: return conn; 175: } 176: 177: /** 178: * Returns the name of the bean to which method calls are made. 179: * 180: * @return the bean which provides the actual method calls. 181: * @since 1.6 182: */ 183: public ObjectName getObjectName() 184: { 185: return name; 186: } 187: 188: /** 189: * Called by the proxy class whenever a method is called. The method 190: * is emulated by retrieving an attribute from, setting an attribute on 191: * or invoking a method on the server connection as required. Translation 192: * between the Java data types supplied as arguments to the open types used 193: * by the bean is provided, as well as translation of the return value back 194: * in to the appropriate Java type if the bean is an {@link MXBean}. 195: * 196: * @param proxy the proxy on which the method was called. 197: * @param method the method which was called. 198: * @param args the arguments supplied to the method. 199: * @return the return value from the method. 200: * @throws Throwable if an exception is thrown in performing the 201: * method emulation. 202: */ 203: public Object invoke(Object proxy, Method method, Object[] args) 204: throws Throwable 205: { 206: String mName = method.getName(); 207: Class<?> proxyClass = proxy.getClass(); 208: if (mName.equals("toString")) 209: { 210: if (inInterface(mName, proxyClass)) 211: return conn.invoke(name,mName,null,null); 212: else 213: return proxyClass.getName() + "[name=" + name 214: + ", conn=" + conn + "]"; 215: } 216: if (mName.equals("hashCode")) 217: { 218: if (inInterface(mName, proxyClass)) 219: return conn.invoke(name,mName,null,null); 220: else 221: return conn.hashCode() + name.hashCode() 222: + (iface == null ? 0 : iface.hashCode()); 223: } 224: if (mName.equals("equals")) 225: { 226: if (inInterface(mName, proxyClass, Object.class)) 227: return conn.invoke(name,mName,new Object[]{args[0]}, 228: new String[]{"java.lang.Object"}); 229: else 230: { 231: if (args[0].getClass() != proxy.getClass()) 232: return false; 233: InvocationHandler ih = Proxy.getInvocationHandler(args[0]); 234: if (ih instanceof MBeanServerInvocationHandler) 235: { 236: MBeanServerInvocationHandler h = 237: (MBeanServerInvocationHandler) ih; 238: return conn.equals(h.getMBeanServerConnection()) 239: && name.equals(h.getObjectName()) 240: && (iface == null ? h.iface == null 241: : iface.equals(h.iface)); 242: } 243: return false; 244: } 245: } 246: if (NotificationEmitter.class.isAssignableFrom(proxyClass)) 247: { 248: if (mName.equals("addNotificationListener")) 249: { 250: conn.addNotificationListener(name, 251: (NotificationListener) args[0], 252: (NotificationFilter) args[1], 253: args[2]); 254: return null; 255: } 256: if (mName.equals("getNotificationInfo")) 257: return conn.getMBeanInfo(name).getNotifications(); 258: if (mName.equals("removeNotificationListener")) 259: { 260: if (args.length == 1) 261: conn.removeNotificationListener(name, 262: (NotificationListener) 263: args[0]); 264: else 265: conn.removeNotificationListener(name, 266: (NotificationListener) 267: args[0], 268: (NotificationFilter) 269: args[1], args[2]); 270: return null; 271: } 272: } 273: String[] sigs; 274: if (args == null) 275: sigs = null; 276: else 277: { 278: sigs = new String[args.length]; 279: for (int a = 0; a < args.length; ++a) 280: sigs[a] = args[a].getClass().getName(); 281: } 282: String attrib = null; 283: if (mName.startsWith("get")) 284: attrib = mName.substring(3); 285: else if (mName.startsWith("is")) 286: attrib = mName.substring(2); 287: if (attrib != null) 288: { 289: Object val = conn.getAttribute(name, attrib); 290: if (mxBean) 291: return Translator.toJava(val, method); 292: else 293: return val; 294: } 295: else if (mName.startsWith("set")) 296: { 297: Object arg; 298: if (mxBean) 299: arg = Translator.fromJava(args, method)[0]; 300: else 301: arg = args[0]; 302: conn.setAttribute(name, new Attribute(mName.substring(3), arg)); 303: return null; 304: } 305: if (mxBean) 306: return Translator.toJava(conn.invoke(name, mName, 307: Translator.fromJava(args,method), 308: sigs), method); 309: else 310: return conn.invoke(name, mName, args, sigs); 311: } 312: 313: /** 314: * Returns true if this is a proxy for an {@link MXBean} 315: * and conversions must be applied to input parameters 316: * and return types, according to the rules for such beans. 317: * 318: * @return true if this is a proxy for an {@link MXBean}. 319: * @since 1.6 320: */ 321: public boolean isMXBean() 322: { 323: return mxBean; 324: } 325: 326: /** 327: * <p> 328: * Returns a proxy for the specified bean. A proxy object is created 329: * using <code>Proxy.newProxyInstance(iface.getClassLoader(), 330: * new Class[] { iface }, handler)</code>. The 331: * {@link javax.management.NotificationEmitter} class is included as the 332: * second element of the array if <code>broadcaster</code> is true. 333: * <code>handler</code> refers to the invocation handler which forwards 334: * calls to the connection, which is created by a call to 335: * <code>new MBeanServerInvocationHandler(conn, name)</code>. 336: * </p> 337: * <p> 338: * <strong>Note</strong>: use of the proxy may result in 339: * {@link java.io.IOException}s from the underlying 340: * {@link MBeanServerConnection}. 341: * As of 1.6, the use of {@link JMX#newMBeanProxy(MBeanServerConnection, 342: * ObjectName,Class)} and {@link JMX#newMBeanProxy(MBeanServerConnection, 343: * ObjectName,Class,boolean)} is preferred. 344: * </p> 345: * 346: * @param conn the server connection to use to access the bean. 347: * @param name the {@link javax.management.ObjectName} of the 348: * bean to provide a proxy for. 349: * @param iface the interface for the bean being proxied. 350: * @param broadcaster true if the proxy should implement 351: * {@link NotificationEmitter}. 352: * @return a proxy for the specified bean. 353: * @see JMX#newMBeanProxy(MBeanServerConnection,ObjectName,Class) 354: */ 355: // Suppress warnings as we know an instance of T will be returned. 356: @SuppressWarnings("unchecked") 357: public static <T> T newProxyInstance(MBeanServerConnection conn, 358: ObjectName name, Class<T> iface, 359: boolean broadcaster) 360: { 361: if (broadcaster) 362: return (T) Proxy.newProxyInstance(iface.getClassLoader(), 363: new Class[] { iface, 364: NotificationEmitter.class }, 365: new MBeanServerInvocationHandler(conn,name)); 366: else 367: return (T) Proxy.newProxyInstance(iface.getClassLoader(), 368: new Class[] { iface }, 369: new MBeanServerInvocationHandler(conn,name)); 370: } 371: 372: /** 373: * Returns true if the specified method is specified 374: * by one of the proxy's interfaces. 375: * 376: * @param name the name of the method to search for. 377: * @param proxyClass the class of the proxy. 378: * @param args the arguments to the method. 379: * @return true if one of the interfaces specifies the 380: * given method. 381: */ 382: private boolean inInterface(String name, Class<?> proxyClass, 383: Class<?>... args) 384: { 385: for (Class<?> iface : proxyClass.getInterfaces()) 386: { 387: try 388: { 389: iface.getMethod(name, args); 390: return true; 391: } 392: catch (NoSuchMethodException e) 393: { 394: /* Ignored; this interface doesn't specify 395: the method. */ 396: } 397: } 398: return false; 399: } 400: 401: }