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 26package com.sun.jmx.mbeanserver; 27 28 29import javax.management.Attribute; 30import javax.management.AttributeList; 31import javax.management.AttributeNotFoundException; 32import javax.management.InvalidAttributeValueException; 33import javax.management.MBeanException; 34import javax.management.MBeanInfo; 35import javax.management.MBeanRegistration; 36import javax.management.MBeanServer; 37import javax.management.NotCompliantMBeanException; 38import javax.management.ObjectName; 39import javax.management.ReflectionException; 40import com.sun.jmx.mbeanserver.MXBeanMappingFactory; 41import sun.reflect.misc.ReflectUtil; 42 43/** 44 * Base class for MBeans. There is one instance of this class for 45 * every Standard MBean and every MXBean. We try to limit the amount 46 * of information per instance so we can handle very large numbers of 47 * MBeans comfortably. 48 * 49 * @param <M> either Method or ConvertingMethod, for Standard MBeans 50 * and MXBeans respectively. 51 * 52 * @since 1.6 53 */ 54/* 55 * We maintain a couple of caches to increase sharing between 56 * different MBeans of the same type and also to reduce creation time 57 * for the second and subsequent instances of the same type. 58 * 59 * The first cache maps from an MBean interface to a PerInterface 60 * object containing information parsed out of the interface. The 61 * interface is either a Standard MBean interface or an MXBean 62 * interface, and there is one cache for each case. 63 * 64 * The PerInterface includes an MBeanInfo. This contains the 65 * attributes and operations parsed out of the interface's methods, 66 * plus a basic Descriptor for the interface containing at least the 67 * interfaceClassName field and any fields derived from annotations on 68 * the interface. This MBeanInfo can never be the MBeanInfo for any 69 * actual MBean, because an MBeanInfo's getClassName() is the name of 70 * a concrete class and we don't know what the class will be. 71 * Furthermore a real MBeanInfo may need to add constructors and/or 72 * notifications to the MBeanInfo. 73 * 74 * The PerInterface also contains an MBeanDispatcher which is able to 75 * route getAttribute, setAttribute, and invoke to the appropriate 76 * method of the interface, including doing any necessary translation 77 * of parameters and return values for MXBeans. 78 * 79 * The PerInterface also contains the original Class for the interface. 80 * 81 * We need to be careful about references. When there are no MBeans 82 * with a given interface, there must not be any strong references to 83 * the interface Class. Otherwise it could never be garbage collected, 84 * and neither could its ClassLoader or any other classes loaded by 85 * its ClassLoader. Therefore the cache must wrap the PerInterface 86 * in a WeakReference. Each instance of MBeanSupport has a strong 87 * reference to its PerInterface, which prevents PerInterface instances 88 * from being garbage-collected prematurely. 89 * 90 * The second cache maps from a concrete class and an MBean interface 91 * that that class implements to the MBeanInfo for that class and 92 * interface. (The ability to specify an interface separately comes 93 * from the class StandardMBean. MBeans registered directly in the 94 * MBean Server will always have the same interface here.) 95 * 96 * The MBeanInfo in this second cache will be the MBeanInfo from the 97 * PerInterface cache for the given itnerface, but with the 98 * getClassName() having the concrete class's name, and the public 99 * constructors based on the concrete class's constructors. This 100 * MBeanInfo can be shared between all instances of the concrete class 101 * specifying the same interface, except instances that are 102 * NotificationBroadcasters. NotificationBroadcasters supply the 103 * MBeanNotificationInfo[] in the MBeanInfo based on the instance 104 * method NotificationBroadcaster.getNotificationInfo(), so two 105 * instances of the same concrete class do not necessarily have the 106 * same MBeanNotificationInfo[]. Currently we do not try to detect 107 * when they do, although it would probably be worthwhile doing that 108 * since it is a very common case. 109 * 110 * Standard MBeans additionally have the property that 111 * getNotificationInfo() must in principle be called every time 112 * getMBeanInfo() is called for the MBean, since the returned array is 113 * allowed to change over time. We attempt to reduce the cost of 114 * doing this by detecting when the Standard MBean is a subclass of 115 * NotificationBroadcasterSupport that does not override 116 * getNotificationInfo(), meaning that the MBeanNotificationInfo[] is 117 * the one that was supplied to the constructor. MXBeans do not have 118 * this problem because their getNotificationInfo() method is called 119 * only once. 120 * 121 */ 122public abstract class MBeanSupport<M> 123 implements DynamicMBean2, MBeanRegistration { 124 125 <T> MBeanSupport(T resource, Class<T> mbeanInterfaceType) 126 throws NotCompliantMBeanException { 127 if (mbeanInterfaceType == null) 128 throw new NotCompliantMBeanException("Null MBean interface"); 129 if (!mbeanInterfaceType.isInstance(resource)) { 130 final String msg = 131 "Resource class " + resource.getClass().getName() + 132 " is not an instance of " + mbeanInterfaceType.getName(); 133 throw new NotCompliantMBeanException(msg); 134 } 135 ReflectUtil.checkPackageAccess(mbeanInterfaceType); 136 this.resource = resource; 137 MBeanIntrospector<M> introspector = getMBeanIntrospector(); 138 this.perInterface = introspector.getPerInterface(mbeanInterfaceType); 139 this.mbeanInfo = introspector.getMBeanInfo(resource, perInterface); 140 } 141 142 /** Return the appropriate introspector for this type of MBean. */ 143 abstract MBeanIntrospector<M> getMBeanIntrospector(); 144 145 /** 146 * Return a cookie for this MBean. This cookie will be passed to 147 * MBean method invocations where it can supply additional information 148 * to the invocation. For example, with MXBeans it can be used to 149 * supply the MXBeanLookup context for resolving inter-MXBean references. 150 */ 151 abstract Object getCookie(); 152 153 public final boolean isMXBean() { 154 return perInterface.isMXBean(); 155 } 156 157 // Methods that javax.management.StandardMBean should call from its 158 // preRegister and postRegister, given that it is not supposed to 159 // call the contained object's preRegister etc methods even if it has them 160 public abstract void register(MBeanServer mbs, ObjectName name) 161 throws Exception; 162 public abstract void unregister(); 163 164 public final ObjectName preRegister(MBeanServer server, ObjectName name) 165 throws Exception { 166 if (resource instanceof MBeanRegistration) 167 name = ((MBeanRegistration) resource).preRegister(server, name); 168 return name; 169 } 170 171 public final void preRegister2(MBeanServer server, ObjectName name) 172 throws Exception { 173 register(server, name); 174 } 175 176 public final void registerFailed() { 177 unregister(); 178 } 179 180 public final void postRegister(Boolean registrationDone) { 181 if (resource instanceof MBeanRegistration) 182 ((MBeanRegistration) resource).postRegister(registrationDone); 183 } 184 185 public final void preDeregister() throws Exception { 186 if (resource instanceof MBeanRegistration) 187 ((MBeanRegistration) resource).preDeregister(); 188 } 189 190 public final void postDeregister() { 191 // Undo any work from registration. We do this in postDeregister 192 // not preDeregister, because if the user preDeregister throws an 193 // exception then the MBean is not unregistered. 194 try { 195 unregister(); 196 } finally { 197 if (resource instanceof MBeanRegistration) 198 ((MBeanRegistration) resource).postDeregister(); 199 } 200 } 201 202 public final Object getAttribute(String attribute) 203 throws AttributeNotFoundException, 204 MBeanException, 205 ReflectionException { 206 return perInterface.getAttribute(resource, attribute, getCookie()); 207 } 208 209 public final AttributeList getAttributes(String[] attributes) { 210 final AttributeList result = new AttributeList(attributes.length); 211 for (String attrName : attributes) { 212 try { 213 final Object attrValue = getAttribute(attrName); 214 result.add(new Attribute(attrName, attrValue)); 215 } catch (Exception e) { 216 // OK: attribute is not included in returned list, per spec 217 // XXX: log the exception 218 } 219 } 220 return result; 221 } 222 223 public final void setAttribute(Attribute attribute) 224 throws AttributeNotFoundException, 225 InvalidAttributeValueException, 226 MBeanException, 227 ReflectionException { 228 final String name = attribute.getName(); 229 final Object value = attribute.getValue(); 230 perInterface.setAttribute(resource, name, value, getCookie()); 231 } 232 233 public final AttributeList setAttributes(AttributeList attributes) { 234 final AttributeList result = new AttributeList(attributes.size()); 235 for (Object attrObj : attributes) { 236 // We can't use AttributeList.asList because it has side-effects 237 Attribute attr = (Attribute) attrObj; 238 try { 239 setAttribute(attr); 240 result.add(new Attribute(attr.getName(), attr.getValue())); 241 } catch (Exception e) { 242 // OK: attribute is not included in returned list, per spec 243 // XXX: log the exception 244 } 245 } 246 return result; 247 } 248 249 public final Object invoke(String operation, Object[] params, 250 String[] signature) 251 throws MBeanException, ReflectionException { 252 return perInterface.invoke(resource, operation, params, signature, 253 getCookie()); 254 } 255 256 // Overridden by StandardMBeanSupport 257 public MBeanInfo getMBeanInfo() { 258 return mbeanInfo; 259 } 260 261 public final String getClassName() { 262 return resource.getClass().getName(); 263 } 264 265 public final Object getResource() { 266 return resource; 267 } 268 269 public final Class<?> getMBeanInterface() { 270 return perInterface.getMBeanInterface(); 271 } 272 273 private final MBeanInfo mbeanInfo; 274 private final Object resource; 275 private final PerInterface<M> perInterface; 276} 277