1 /* 2 * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.management; 27 28 import com.sun.jmx.mbeanserver.Introspector; 29 import java.lang.reflect.InvocationHandler; 30 import java.lang.reflect.Modifier; 31 import java.lang.reflect.Proxy; 32 import sun.reflect.misc.ReflectUtil; 33 34 /** 35 * Static methods from the JMX API. There are no instances of this class. 36 * 37 * @since 1.6 38 */ 39 public class JMX { 40 /* Code within this package can prove that by providing this instance of 41 * this class. 42 */ 43 static final JMX proof = new JMX(); 44 JMX()45 private JMX() {} 46 47 /** 48 * The name of the <a href="Descriptor.html#defaultValue">{@code 49 * defaultValue}</a> field. 50 */ 51 public static final String DEFAULT_VALUE_FIELD = "defaultValue"; 52 53 /** 54 * The name of the <a href="Descriptor.html#immutableInfo">{@code 55 * immutableInfo}</a> field. 56 */ 57 public static final String IMMUTABLE_INFO_FIELD = "immutableInfo"; 58 59 /** 60 * The name of the <a href="Descriptor.html#interfaceClassName">{@code 61 * interfaceClassName}</a> field. 62 */ 63 public static final String INTERFACE_CLASS_NAME_FIELD = "interfaceClassName"; 64 65 /** 66 * The name of the <a href="Descriptor.html#legalValues">{@code 67 * legalValues}</a> field. 68 */ 69 public static final String LEGAL_VALUES_FIELD = "legalValues"; 70 71 /** 72 * The name of the <a href="Descriptor.html#maxValue">{@code 73 * maxValue}</a> field. 74 */ 75 public static final String MAX_VALUE_FIELD = "maxValue"; 76 77 /** 78 * The name of the <a href="Descriptor.html#minValue">{@code 79 * minValue}</a> field. 80 */ 81 public static final String MIN_VALUE_FIELD = "minValue"; 82 83 /** 84 * The name of the <a href="Descriptor.html#mxbean">{@code 85 * mxbean}</a> field. 86 */ 87 public static final String MXBEAN_FIELD = "mxbean"; 88 89 /** 90 * The name of the <a href="Descriptor.html#openType">{@code 91 * openType}</a> field. 92 */ 93 public static final String OPEN_TYPE_FIELD = "openType"; 94 95 /** 96 * The name of the <a href="Descriptor.html#originalType">{@code 97 * originalType}</a> field. 98 */ 99 public static final String ORIGINAL_TYPE_FIELD = "originalType"; 100 101 /** 102 * <p>Make a proxy for a Standard MBean in a local or remote 103 * MBean Server.</p> 104 * 105 * <p>If you have an MBean Server {@code mbs} containing an MBean 106 * with {@link ObjectName} {@code name}, and if the MBean's 107 * management interface is described by the Java interface 108 * {@code MyMBean}, you can construct a proxy for the MBean like 109 * this:</p> 110 * 111 * <pre> 112 * MyMBean proxy = JMX.newMBeanProxy(mbs, name, MyMBean.class); 113 * </pre> 114 * 115 * <p>Suppose, for example, {@code MyMBean} looks like this:</p> 116 * 117 * <pre> 118 * public interface MyMBean { 119 * public String getSomeAttribute(); 120 * public void setSomeAttribute(String value); 121 * public void someOperation(String param1, int param2); 122 * } 123 * </pre> 124 * 125 * <p>Then you can execute:</p> 126 * 127 * <ul> 128 * 129 * <li>{@code proxy.getSomeAttribute()} which will result in a 130 * call to {@code mbs.}{@link MBeanServerConnection#getAttribute 131 * getAttribute}{@code (name, "SomeAttribute")}. 132 * 133 * <li>{@code proxy.setSomeAttribute("whatever")} which will result 134 * in a call to {@code mbs.}{@link MBeanServerConnection#setAttribute 135 * setAttribute}{@code (name, new Attribute("SomeAttribute", "whatever"))}. 136 * 137 * <li>{@code proxy.someOperation("param1", 2)} which will be 138 * translated into a call to {@code mbs.}{@link 139 * MBeanServerConnection#invoke invoke}{@code (name, "someOperation", <etc>)}. 140 * 141 * </ul> 142 * 143 * <p>The object returned by this method is a 144 * {@link Proxy} whose {@code InvocationHandler} is an 145 * {@link MBeanServerInvocationHandler}.</p> 146 * 147 * <p>This method is equivalent to {@link 148 * #newMBeanProxy(MBeanServerConnection, ObjectName, Class, 149 * boolean) newMBeanProxy(connection, objectName, interfaceClass, 150 * false)}.</p> 151 * 152 * @param connection the MBean server to forward to. 153 * @param objectName the name of the MBean within 154 * {@code connection} to forward to. 155 * @param interfaceClass the management interface that the MBean 156 * exports, which will also be implemented by the returned proxy. 157 * 158 * @param <T> allows the compiler to know that if the {@code 159 * interfaceClass} parameter is {@code MyMBean.class}, for 160 * example, then the return type is {@code MyMBean}. 161 * 162 * @return the new proxy instance. 163 * 164 * @throws IllegalArgumentException if {@code interfaceClass} is not 165 * a <a href="package-summary.html#mgIface">compliant MBean 166 * interface</a> 167 */ newMBeanProxy(MBeanServerConnection connection, ObjectName objectName, Class<T> interfaceClass)168 public static <T> T newMBeanProxy(MBeanServerConnection connection, 169 ObjectName objectName, 170 Class<T> interfaceClass) { 171 return newMBeanProxy(connection, objectName, interfaceClass, false); 172 } 173 174 /** 175 * <p>Make a proxy for a Standard MBean in a local or remote MBean 176 * Server that may also support the methods of {@link 177 * NotificationEmitter}.</p> 178 * 179 * <p>This method behaves the same as {@link 180 * #newMBeanProxy(MBeanServerConnection, ObjectName, Class)}, but 181 * additionally, if {@code notificationEmitter} is {@code 182 * true}, then the MBean is assumed to be a {@link 183 * NotificationBroadcaster} or {@link NotificationEmitter} and the 184 * returned proxy will implement {@link NotificationEmitter} as 185 * well as {@code interfaceClass}. A call to {@link 186 * NotificationBroadcaster#addNotificationListener} on the proxy 187 * will result in a call to {@link 188 * MBeanServerConnection#addNotificationListener(ObjectName, 189 * NotificationListener, NotificationFilter, Object)}, and 190 * likewise for the other methods of {@link 191 * NotificationBroadcaster} and {@link NotificationEmitter}.</p> 192 * 193 * @param connection the MBean server to forward to. 194 * @param objectName the name of the MBean within 195 * {@code connection} to forward to. 196 * @param interfaceClass the management interface that the MBean 197 * exports, which will also be implemented by the returned proxy. 198 * @param notificationEmitter make the returned proxy 199 * implement {@link NotificationEmitter} by forwarding its methods 200 * via {@code connection}. 201 * 202 * @param <T> allows the compiler to know that if the {@code 203 * interfaceClass} parameter is {@code MyMBean.class}, for 204 * example, then the return type is {@code MyMBean}. 205 * 206 * @return the new proxy instance. 207 * 208 * @throws IllegalArgumentException if {@code interfaceClass} is not 209 * a <a href="package-summary.html#mgIface">compliant MBean 210 * interface</a> 211 */ newMBeanProxy(MBeanServerConnection connection, ObjectName objectName, Class<T> interfaceClass, boolean notificationEmitter)212 public static <T> T newMBeanProxy(MBeanServerConnection connection, 213 ObjectName objectName, 214 Class<T> interfaceClass, 215 boolean notificationEmitter) { 216 return createProxy(connection, objectName, interfaceClass, notificationEmitter, false); 217 } 218 219 /** 220 * Make a proxy for an MXBean in a local or remote MBean Server. 221 * 222 * <p>If you have an MBean Server {@code mbs} containing an 223 * MXBean with {@link ObjectName} {@code name}, and if the 224 * MXBean's management interface is described by the Java 225 * interface {@code MyMXBean}, you can construct a proxy for 226 * the MXBean like this:</p> 227 * 228 * <pre> 229 * MyMXBean proxy = JMX.newMXBeanProxy(mbs, name, MyMXBean.class); 230 * </pre> 231 * 232 * <p>Suppose, for example, {@code MyMXBean} looks like this:</p> 233 * 234 * <pre> 235 * public interface MyMXBean { 236 * public String getSimpleAttribute(); 237 * public void setSimpleAttribute(String value); 238 * public {@link java.lang.management.MemoryUsage} getMappedAttribute(); 239 * public void setMappedAttribute(MemoryUsage memoryUsage); 240 * public MemoryUsage someOperation(String param1, MemoryUsage param2); 241 * } 242 * </pre> 243 * 244 * <p>Then:</p> 245 * 246 * <ul> 247 * 248 * <li><p>{@code proxy.getSimpleAttribute()} will result in a 249 * call to {@code mbs.}{@link MBeanServerConnection#getAttribute 250 * getAttribute}{@code (name, "SimpleAttribute")}.</p> 251 * 252 * <li><p>{@code proxy.setSimpleAttribute("whatever")} will result 253 * in a call to {@code mbs.}{@link 254 * MBeanServerConnection#setAttribute setAttribute}<code>(name, 255 * new Attribute("SimpleAttribute", "whatever"))</code>.</p> 256 * 257 * <p>Because {@code String} is a <em>simple type</em>, in the 258 * sense of {@link javax.management.openmbean.SimpleType}, it 259 * is not changed in the context of an MXBean. The MXBean 260 * proxy behaves the same as a Standard MBean proxy (see 261 * {@link #newMBeanProxy(MBeanServerConnection, ObjectName, 262 * Class) newMBeanProxy}) for the attribute {@code 263 * SimpleAttribute}.</p> 264 * 265 * <li><p>{@code proxy.getMappedAttribute()} will result in a call 266 * to {@code mbs.getAttribute("MappedAttribute")}. The MXBean 267 * mapping rules mean that the actual type of the attribute {@code 268 * MappedAttribute} will be {@link 269 * javax.management.openmbean.CompositeData CompositeData} and 270 * that is what the {@code mbs.getAttribute} call will return. 271 * The proxy will then convert the {@code CompositeData} back into 272 * the expected type {@code MemoryUsage} using the MXBean mapping 273 * rules.</p> 274 * 275 * <li><p>Similarly, {@code proxy.setMappedAttribute(memoryUsage)} 276 * will convert the {@code MemoryUsage} argument into a {@code 277 * CompositeData} before calling {@code mbs.setAttribute}.</p> 278 * 279 * <li><p>{@code proxy.someOperation("whatever", memoryUsage)} 280 * will convert the {@code MemoryUsage} argument into a {@code 281 * CompositeData} and call {@code mbs.invoke}. The value returned 282 * by {@code mbs.invoke} will be also be a {@code CompositeData}, 283 * and the proxy will convert this into the expected type {@code 284 * MemoryUsage} using the MXBean mapping rules.</p> 285 * 286 * </ul> 287 * 288 * <p>The object returned by this method is a 289 * {@link Proxy} whose {@code InvocationHandler} is an 290 * {@link MBeanServerInvocationHandler}.</p> 291 * 292 * <p>This method is equivalent to {@link 293 * #newMXBeanProxy(MBeanServerConnection, ObjectName, Class, 294 * boolean) newMXBeanProxy(connection, objectName, interfaceClass, 295 * false)}.</p> 296 * 297 * @param connection the MBean server to forward to. 298 * @param objectName the name of the MBean within 299 * {@code connection} to forward to. 300 * @param interfaceClass the MXBean interface, 301 * which will also be implemented by the returned proxy. 302 * 303 * @param <T> allows the compiler to know that if the {@code 304 * interfaceClass} parameter is {@code MyMXBean.class}, for 305 * example, then the return type is {@code MyMXBean}. 306 * 307 * @return the new proxy instance. 308 * 309 * @throws IllegalArgumentException if {@code interfaceClass} is not 310 * a {@link javax.management.MXBean compliant MXBean interface} 311 */ newMXBeanProxy(MBeanServerConnection connection, ObjectName objectName, Class<T> interfaceClass)312 public static <T> T newMXBeanProxy(MBeanServerConnection connection, 313 ObjectName objectName, 314 Class<T> interfaceClass) { 315 return newMXBeanProxy(connection, objectName, interfaceClass, false); 316 } 317 318 /** 319 * <p>Make a proxy for an MXBean in a local or remote MBean 320 * Server that may also support the methods of {@link 321 * NotificationEmitter}.</p> 322 * 323 * <p>This method behaves the same as {@link 324 * #newMXBeanProxy(MBeanServerConnection, ObjectName, Class)}, but 325 * additionally, if {@code notificationEmitter} is {@code 326 * true}, then the MXBean is assumed to be a {@link 327 * NotificationBroadcaster} or {@link NotificationEmitter} and the 328 * returned proxy will implement {@link NotificationEmitter} as 329 * well as {@code interfaceClass}. A call to {@link 330 * NotificationBroadcaster#addNotificationListener} on the proxy 331 * will result in a call to {@link 332 * MBeanServerConnection#addNotificationListener(ObjectName, 333 * NotificationListener, NotificationFilter, Object)}, and 334 * likewise for the other methods of {@link 335 * NotificationBroadcaster} and {@link NotificationEmitter}.</p> 336 * 337 * @param connection the MBean server to forward to. 338 * @param objectName the name of the MBean within 339 * {@code connection} to forward to. 340 * @param interfaceClass the MXBean interface, 341 * which will also be implemented by the returned proxy. 342 * @param notificationEmitter make the returned proxy 343 * implement {@link NotificationEmitter} by forwarding its methods 344 * via {@code connection}. 345 * 346 * @param <T> allows the compiler to know that if the {@code 347 * interfaceClass} parameter is {@code MyMXBean.class}, for 348 * example, then the return type is {@code MyMXBean}. 349 * 350 * @return the new proxy instance. 351 * 352 * @throws IllegalArgumentException if {@code interfaceClass} is not 353 * a {@link javax.management.MXBean compliant MXBean interface} 354 */ newMXBeanProxy(MBeanServerConnection connection, ObjectName objectName, Class<T> interfaceClass, boolean notificationEmitter)355 public static <T> T newMXBeanProxy(MBeanServerConnection connection, 356 ObjectName objectName, 357 Class<T> interfaceClass, 358 boolean notificationEmitter) { 359 return createProxy(connection, objectName, interfaceClass, notificationEmitter, true); 360 } 361 362 /** 363 * <p>Test whether an interface is an MXBean interface. 364 * An interface is an MXBean interface if it is public, 365 * annotated {@link MXBean @MXBean} or {@code @MXBean(true)} 366 * or if it does not have an {@code @MXBean} annotation 367 * and its name ends with "{@code MXBean}".</p> 368 * 369 * @param interfaceClass The candidate interface. 370 * 371 * @return true if {@code interfaceClass} is a 372 * {@link javax.management.MXBean compliant MXBean interface} 373 * 374 * @throws NullPointerException if {@code interfaceClass} is null. 375 */ isMXBeanInterface(Class<?> interfaceClass)376 public static boolean isMXBeanInterface(Class<?> interfaceClass) { 377 if (!interfaceClass.isInterface()) 378 return false; 379 if (!Modifier.isPublic(interfaceClass.getModifiers()) && 380 !Introspector.ALLOW_NONPUBLIC_MBEAN) { 381 return false; 382 } 383 MXBean a = interfaceClass.getAnnotation(MXBean.class); 384 if (a != null) 385 return a.value(); 386 return interfaceClass.getName().endsWith("MXBean"); 387 // We don't bother excluding the case where the name is 388 // exactly the string "MXBean" since that would mean there 389 // was no package name, which is pretty unlikely in practice. 390 } 391 392 /** 393 * Centralised M(X)Bean proxy creation code 394 * @param connection {@linkplain MBeanServerConnection} to use 395 * @param objectName M(X)Bean object name 396 * @param interfaceClass M(X)Bean interface class 397 * @param notificationEmitter Is a notification emitter? 398 * @param isMXBean Is an MXBean? 399 * @return Returns an M(X)Bean proxy generated for the provided interface class 400 * @throws SecurityException 401 * @throws IllegalArgumentException 402 */ createProxy(MBeanServerConnection connection, ObjectName objectName, Class<T> interfaceClass, boolean notificationEmitter, boolean isMXBean)403 private static <T> T createProxy(MBeanServerConnection connection, 404 ObjectName objectName, 405 Class<T> interfaceClass, 406 boolean notificationEmitter, 407 boolean isMXBean) { 408 409 try { 410 if (isMXBean) { 411 // Check interface for MXBean compliance 412 Introspector.testComplianceMXBeanInterface(interfaceClass); 413 } else { 414 // Check interface for MBean compliance 415 Introspector.testComplianceMBeanInterface(interfaceClass); 416 } 417 } catch (NotCompliantMBeanException e) { 418 throw new IllegalArgumentException(e); 419 } 420 421 InvocationHandler handler = new MBeanServerInvocationHandler( 422 connection, objectName, isMXBean); 423 final Class<?>[] interfaces; 424 if (notificationEmitter) { 425 interfaces = 426 new Class<?>[] {interfaceClass, NotificationEmitter.class}; 427 } else 428 interfaces = new Class<?>[] {interfaceClass}; 429 430 Object proxy = Proxy.newProxyInstance( 431 interfaceClass.getClassLoader(), 432 interfaces, 433 handler); 434 return interfaceClass.cast(proxy); 435 } 436 } 437