View Javadoc
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  
26  package com.sun.jmx.mbeanserver;
27  
28  import static com.sun.jmx.mbeanserver.Util.*;
29  import java.util.Map;
30  import java.lang.ref.WeakReference;
31  import java.lang.reflect.InvocationHandler;
32  import java.lang.reflect.Proxy;
33  import java.security.AccessController;
34  import javax.management.InstanceAlreadyExistsException;
35  import javax.management.JMX;
36  import javax.management.MBeanServerConnection;
37  import javax.management.MBeanServerInvocationHandler;
38  import javax.management.ObjectName;
39  import 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   */
87  public 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 }