1/* 2 * Copyright (c) 2005, 2008, 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 28import static com.sun.jmx.mbeanserver.Util.*; 29import java.util.Map; 30import java.lang.ref.WeakReference; 31import java.lang.reflect.InvocationHandler; 32import java.lang.reflect.Proxy; 33import java.security.AccessController; 34import javax.management.InstanceAlreadyExistsException; 35import javax.management.JMX; 36import javax.management.MBeanServerConnection; 37import javax.management.MBeanServerInvocationHandler; 38import javax.management.ObjectName; 39import javax.management.openmbean.OpenDataException; 40 41/** 42 * @since 1.6 43 */ 44 45/* 46 * This class handles the mapping between MXBean references and 47 * ObjectNames. Consider an MXBean interface like this: 48 * 49 * public interface ModuleMXBean { 50 * ProductMXBean getProduct(); 51 * void setProduct(ProductMXBean product); 52 * } 53 * 54 * This defines an attribute called "Product" whose originalType will 55 * be ProductMXBean and whose openType will be ObjectName. The 56 * mapping happens as follows. 57 * 58 * When the MXBean's getProduct method is called, it is supposed to 59 * return a reference to another MXBean, or a proxy for another 60 * MXBean. The MXBean layer has to convert this into an ObjectName. 61 * If it's a reference to another MXBean, it needs to be able to look 62 * up the name under which that MXBean has been registered in this 63 * MBeanServer; this is the purpose of the mxbeanToObjectName map. If 64 * it's a proxy, it can check that the MBeanServer matches and if so 65 * extract the ObjectName from the proxy. 66 * 67 * When the setProduct method is called on a proxy for this MXBean, 68 * the argument can be either an MXBean reference (only really logical 69 * if the proxy has a local MBeanServer) or another proxy. So the 70 * mapping logic is the same as for getProduct on the MXBean. 71 * 72 * When the MXBean's setProduct method is called, it needs to convert 73 * the ObjectName into an object implementing the ProductMXBean 74 * interface. We could have a lookup table that reverses 75 * mxbeanToObjectName, but this could violate the general JMX property 76 * that you cannot obtain a reference to an MBean object. So we 77 * always use a proxy for this. However we do have an 78 * objectNameToProxy map that allows us to reuse proxy instances. 79 * 80 * When the getProduct method is called on a proxy for this MXBean, it 81 * must convert the returned ObjectName into an instance of 82 * ProductMXBean. Again it can do this by making a proxy. 83 * 84 * From the above, it is clear that the logic for getX on an MXBean is 85 * the same as for setX on a proxy, and vice versa. 86 */ 87public class MXBeanLookup { 88 private MXBeanLookup(MBeanServerConnection mbsc) { 89 this.mbsc = mbsc; 90 } 91 92 static MXBeanLookup lookupFor(MBeanServerConnection mbsc) { 93 synchronized (mbscToLookup) { 94 WeakReference<MXBeanLookup> weakLookup = mbscToLookup.get(mbsc); 95 MXBeanLookup lookup = (weakLookup == null) ? null : weakLookup.get(); 96 if (lookup == null) { 97 lookup = new MXBeanLookup(mbsc); 98 mbscToLookup.put(mbsc, new WeakReference<MXBeanLookup>(lookup)); 99 } 100 return lookup; 101 } 102 } 103 104 synchronized <T> T objectNameToMXBean(ObjectName name, Class<T> type) { 105 WeakReference<Object> wr = objectNameToProxy.get(name); 106 if (wr != null) { 107 Object proxy = wr.get(); 108 if (type.isInstance(proxy)) 109 return type.cast(proxy); 110 } 111 T proxy = JMX.newMXBeanProxy(mbsc, name, type); 112 objectNameToProxy.put(name, new WeakReference<Object>(proxy)); 113 return proxy; 114 } 115 116 synchronized ObjectName mxbeanToObjectName(Object mxbean) 117 throws OpenDataException { 118 String wrong; 119 if (mxbean instanceof Proxy) { 120 InvocationHandler ih = Proxy.getInvocationHandler(mxbean); 121 if (ih instanceof MBeanServerInvocationHandler) { 122 MBeanServerInvocationHandler mbsih = 123 (MBeanServerInvocationHandler) ih; 124 if (mbsih.getMBeanServerConnection().equals(mbsc)) 125 return mbsih.getObjectName(); 126 else 127 wrong = "proxy for a different MBeanServer"; 128 } else 129 wrong = "not a JMX proxy"; 130 } else { 131 ObjectName name = mxbeanToObjectName.get(mxbean); 132 if (name != null) 133 return name; 134 wrong = "not an MXBean registered in this MBeanServer"; 135 } 136 String s = (mxbean == null) ? 137 "null" : "object of type " + mxbean.getClass().getName(); 138 throw new OpenDataException( 139 "Could not convert " + s + " to an ObjectName: " + wrong); 140 // Message will be strange if mxbean is null but it is not 141 // supposed to be. 142 } 143 144 synchronized void addReference(ObjectName name, Object mxbean) 145 throws InstanceAlreadyExistsException { 146 ObjectName existing = mxbeanToObjectName.get(mxbean); 147 if (existing != null) { 148 String multiname = AccessController.doPrivileged( 149 new GetPropertyAction("jmx.mxbean.multiname")); 150 if (!"true".equalsIgnoreCase(multiname)) { 151 throw new InstanceAlreadyExistsException( 152 "MXBean already registered with name " + existing); 153 } 154 } 155 mxbeanToObjectName.put(mxbean, name); 156 } 157 158 synchronized boolean removeReference(ObjectName name, Object mxbean) { 159 if (name.equals(mxbeanToObjectName.get(mxbean))) { 160 mxbeanToObjectName.remove(mxbean); 161 return true; 162 } else 163 return false; 164 /* removeReference can be called when the above condition fails, 165 * notably if you try to register the same MXBean twice. 166 */ 167 } 168 169 static MXBeanLookup getLookup() { 170 return currentLookup.get(); 171 } 172 173 static void setLookup(MXBeanLookup lookup) { 174 currentLookup.set(lookup); 175 } 176 177 private static final ThreadLocal<MXBeanLookup> currentLookup = 178 new ThreadLocal<MXBeanLookup>(); 179 180 private final MBeanServerConnection mbsc; 181 private final WeakIdentityHashMap<Object, ObjectName> 182 mxbeanToObjectName = WeakIdentityHashMap.make(); 183 private final Map<ObjectName, WeakReference<Object>> 184 objectNameToProxy = newMap(); 185 private static final WeakIdentityHashMap<MBeanServerConnection, 186 WeakReference<MXBeanLookup>> 187 mbscToLookup = WeakIdentityHashMap.make(); 188} 189