/*
 * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.jmx.mbeanserver;

import static com.sun.jmx.mbeanserver.Util.*;
import java.util.Map;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.security.AccessController;
import javax.management.InstanceAlreadyExistsException;
import javax.management.JMX;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerInvocationHandler;
import javax.management.ObjectName;
import javax.management.openmbean.OpenDataException;

Since:1.6
/** * @since 1.6 */
/* * This class handles the mapping between MXBean references and * ObjectNames. Consider an MXBean interface like this: * * public interface ModuleMXBean { * ProductMXBean getProduct(); * void setProduct(ProductMXBean product); * } * * This defines an attribute called "Product" whose originalType will * be ProductMXBean and whose openType will be ObjectName. The * mapping happens as follows. * * When the MXBean's getProduct method is called, it is supposed to * return a reference to another MXBean, or a proxy for another * MXBean. The MXBean layer has to convert this into an ObjectName. * If it's a reference to another MXBean, it needs to be able to look * up the name under which that MXBean has been registered in this * MBeanServer; this is the purpose of the mxbeanToObjectName map. If * it's a proxy, it can check that the MBeanServer matches and if so * extract the ObjectName from the proxy. * * When the setProduct method is called on a proxy for this MXBean, * the argument can be either an MXBean reference (only really logical * if the proxy has a local MBeanServer) or another proxy. So the * mapping logic is the same as for getProduct on the MXBean. * * When the MXBean's setProduct method is called, it needs to convert * the ObjectName into an object implementing the ProductMXBean * interface. We could have a lookup table that reverses * mxbeanToObjectName, but this could violate the general JMX property * that you cannot obtain a reference to an MBean object. So we * always use a proxy for this. However we do have an * objectNameToProxy map that allows us to reuse proxy instances. * * When the getProduct method is called on a proxy for this MXBean, it * must convert the returned ObjectName into an instance of * ProductMXBean. Again it can do this by making a proxy. * * From the above, it is clear that the logic for getX on an MXBean is * the same as for setX on a proxy, and vice versa. */ public class MXBeanLookup { private MXBeanLookup(MBeanServerConnection mbsc) { this.mbsc = mbsc; } static MXBeanLookup lookupFor(MBeanServerConnection mbsc) { synchronized (mbscToLookup) { WeakReference<MXBeanLookup> weakLookup = mbscToLookup.get(mbsc); MXBeanLookup lookup = (weakLookup == null) ? null : weakLookup.get(); if (lookup == null) { lookup = new MXBeanLookup(mbsc); mbscToLookup.put(mbsc, new WeakReference<MXBeanLookup>(lookup)); } return lookup; } } synchronized <T> T objectNameToMXBean(ObjectName name, Class<T> type) { WeakReference<Object> wr = objectNameToProxy.get(name); if (wr != null) { Object proxy = wr.get(); if (type.isInstance(proxy)) return type.cast(proxy); } T proxy = JMX.newMXBeanProxy(mbsc, name, type); objectNameToProxy.put(name, new WeakReference<Object>(proxy)); return proxy; } synchronized ObjectName mxbeanToObjectName(Object mxbean) throws OpenDataException { String wrong; if (mxbean instanceof Proxy) { InvocationHandler ih = Proxy.getInvocationHandler(mxbean); if (ih instanceof MBeanServerInvocationHandler) { MBeanServerInvocationHandler mbsih = (MBeanServerInvocationHandler) ih; if (mbsih.getMBeanServerConnection().equals(mbsc)) return mbsih.getObjectName(); else wrong = "proxy for a different MBeanServer"; } else wrong = "not a JMX proxy"; } else { ObjectName name = mxbeanToObjectName.get(mxbean); if (name != null) return name; wrong = "not an MXBean registered in this MBeanServer"; } String s = (mxbean == null) ? "null" : "object of type " + mxbean.getClass().getName(); throw new OpenDataException( "Could not convert " + s + " to an ObjectName: " + wrong); // Message will be strange if mxbean is null but it is not // supposed to be. } synchronized void addReference(ObjectName name, Object mxbean) throws InstanceAlreadyExistsException { ObjectName existing = mxbeanToObjectName.get(mxbean); if (existing != null) { String multiname = AccessController.doPrivileged( new GetPropertyAction("jmx.mxbean.multiname")); if (!"true".equalsIgnoreCase(multiname)) { throw new InstanceAlreadyExistsException( "MXBean already registered with name " + existing); } } mxbeanToObjectName.put(mxbean, name); } synchronized boolean removeReference(ObjectName name, Object mxbean) { if (name.equals(mxbeanToObjectName.get(mxbean))) { mxbeanToObjectName.remove(mxbean); return true; } else return false; /* removeReference can be called when the above condition fails, * notably if you try to register the same MXBean twice. */ } static MXBeanLookup getLookup() { return currentLookup.get(); } static void setLookup(MXBeanLookup lookup) { currentLookup.set(lookup); } private static final ThreadLocal<MXBeanLookup> currentLookup = new ThreadLocal<MXBeanLookup>(); private final MBeanServerConnection mbsc; private final WeakIdentityHashMap<Object, ObjectName> mxbeanToObjectName = WeakIdentityHashMap.make(); private final Map<ObjectName, WeakReference<Object>> objectNameToProxy = newMap(); private static final WeakIdentityHashMap<MBeanServerConnection, WeakReference<MXBeanLookup>> mbscToLookup = WeakIdentityHashMap.make(); }