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