2
|
1 |
/*
|
715
|
2 |
* Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved.
|
2
|
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. Sun designates this
|
|
8 |
* particular file as subject to the "Classpath" exception as provided
|
|
9 |
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
22 |
* CA 95054 USA or visit www.sun.com if you need additional information or
|
|
23 |
* have any questions.
|
|
24 |
*/
|
|
25 |
|
|
26 |
package com.sun.jmx.mbeanserver;
|
|
27 |
|
687
|
28 |
import com.sun.jmx.remote.util.EnvHelp;
|
|
29 |
import java.io.InvalidObjectException;
|
2
|
30 |
import static com.sun.jmx.mbeanserver.Util.*;
|
|
31 |
import java.util.Map;
|
|
32 |
import java.lang.ref.WeakReference;
|
|
33 |
import java.lang.reflect.InvocationHandler;
|
|
34 |
import java.lang.reflect.Proxy;
|
687
|
35 |
import java.security.AccessController;
|
|
36 |
import javax.management.InstanceAlreadyExistsException;
|
2
|
37 |
import javax.management.JMX;
|
|
38 |
import javax.management.MBeanServerConnection;
|
|
39 |
import javax.management.MBeanServerInvocationHandler;
|
687
|
40 |
import javax.management.MalformedObjectNameException;
|
2
|
41 |
import javax.management.ObjectName;
|
687
|
42 |
import javax.management.openmbean.OpenDataException;
|
2
|
43 |
|
|
44 |
/**
|
|
45 |
* @since 1.6
|
|
46 |
*/
|
|
47 |
|
|
48 |
/*
|
|
49 |
* This class handles the mapping between MXBean references and
|
|
50 |
* ObjectNames. Consider an MXBean interface like this:
|
|
51 |
*
|
|
52 |
* public interface ModuleMXBean {
|
|
53 |
* ProductMXBean getProduct();
|
|
54 |
* void setProduct(ProductMXBean product);
|
|
55 |
* }
|
|
56 |
*
|
|
57 |
* This defines an attribute called "Product" whose originalType will
|
|
58 |
* be ProductMXBean and whose openType will be ObjectName. The
|
|
59 |
* mapping happens as follows.
|
|
60 |
*
|
|
61 |
* When the MXBean's getProduct method is called, it is supposed to
|
|
62 |
* return a reference to another MXBean, or a proxy for another
|
|
63 |
* MXBean. The MXBean layer has to convert this into an ObjectName.
|
|
64 |
* If it's a reference to another MXBean, it needs to be able to look
|
|
65 |
* up the name under which that MXBean has been registered in this
|
|
66 |
* MBeanServer; this is the purpose of the mxbeanToObjectName map. If
|
|
67 |
* it's a proxy, it can check that the MBeanServer matches and if so
|
|
68 |
* extract the ObjectName from the proxy.
|
|
69 |
*
|
|
70 |
* When the setProduct method is called on a proxy for this MXBean,
|
|
71 |
* the argument can be either an MXBean reference (only really logical
|
|
72 |
* if the proxy has a local MBeanServer) or another proxy. So the
|
|
73 |
* mapping logic is the same as for getProduct on the MXBean.
|
|
74 |
*
|
|
75 |
* When the MXBean's setProduct method is called, it needs to convert
|
|
76 |
* the ObjectName into an object implementing the ProductMXBean
|
|
77 |
* interface. We could have a lookup table that reverses
|
|
78 |
* mxbeanToObjectName, but this could violate the general JMX property
|
|
79 |
* that you cannot obtain a reference to an MBean object. So we
|
|
80 |
* always use a proxy for this. However we do have an
|
|
81 |
* objectNameToProxy map that allows us to reuse proxy instances.
|
|
82 |
*
|
|
83 |
* When the getProduct method is called on a proxy for this MXBean, it
|
|
84 |
* must convert the returned ObjectName into an instance of
|
|
85 |
* ProductMXBean. Again it can do this by making a proxy.
|
|
86 |
*
|
|
87 |
* From the above, it is clear that the logic for getX on an MXBean is
|
|
88 |
* the same as for setX on a proxy, and vice versa.
|
687
|
89 |
*
|
|
90 |
* The above describes the logic for "plain" MXBeanLookup, represented
|
|
91 |
* by MXBeanLookup.Plain. When namespaces enter the picture, we see
|
|
92 |
* MXBeanLookup.Prefix. Here, the idea is that the name of the ModuleMXBean
|
|
93 |
* might be a//m:m=m. In this case, we don't accept a reference to
|
|
94 |
* an MXBean object, since that would require different namespaces to know
|
|
95 |
* each others' objects. We only accept proxies. Suppose you have a proxy
|
|
96 |
* for a//m:m=m, call it moduleProxy, and you call
|
|
97 |
* moduleProxy.setProduct(productProxy). Then if productProxy is for
|
|
98 |
* a//p:p=p we should convert this to just p:p=p. If productProxy is for
|
|
99 |
* a//b//p:p=p we should convert it to b//p:p=p. Conversely, if getProduct
|
|
100 |
* returns an ObjectName like b//p:p=p then we should convert it into a proxy
|
|
101 |
* for a//b//p:p=p.
|
2
|
102 |
*/
|
687
|
103 |
public abstract class MXBeanLookup {
|
2
|
104 |
private MXBeanLookup(MBeanServerConnection mbsc) {
|
|
105 |
this.mbsc = mbsc;
|
|
106 |
}
|
|
107 |
|
687
|
108 |
static MXBeanLookup lookupFor(MBeanServerConnection mbsc, String prefix) {
|
|
109 |
if (prefix == null)
|
|
110 |
return Plain.lookupFor(mbsc);
|
|
111 |
else
|
|
112 |
return new Prefix(mbsc, prefix);
|
|
113 |
}
|
|
114 |
|
|
115 |
abstract <T> T objectNameToMXBean(ObjectName name, Class<T> type)
|
|
116 |
throws InvalidObjectException;
|
|
117 |
|
|
118 |
abstract ObjectName mxbeanToObjectName(Object mxbean)
|
|
119 |
throws OpenDataException;
|
|
120 |
|
|
121 |
static class Plain extends MXBeanLookup {
|
|
122 |
Plain(MBeanServerConnection mbsc) {
|
|
123 |
super(mbsc);
|
|
124 |
}
|
|
125 |
|
|
126 |
static Plain lookupFor(MBeanServerConnection mbsc) {
|
|
127 |
synchronized (mbscToLookup) {
|
|
128 |
WeakReference<Plain> weakLookup = mbscToLookup.get(mbsc);
|
|
129 |
Plain lookup = (weakLookup == null) ? null : weakLookup.get();
|
|
130 |
if (lookup == null) {
|
|
131 |
lookup = new Plain(mbsc);
|
|
132 |
mbscToLookup.put(mbsc, new WeakReference<Plain>(lookup));
|
|
133 |
}
|
|
134 |
return lookup;
|
|
135 |
}
|
|
136 |
}
|
|
137 |
|
|
138 |
@Override
|
|
139 |
synchronized <T> T objectNameToMXBean(ObjectName name, Class<T> type) {
|
|
140 |
WeakReference<Object> wr = objectNameToProxy.get(name);
|
|
141 |
if (wr != null) {
|
|
142 |
Object proxy = wr.get();
|
|
143 |
if (type.isInstance(proxy))
|
|
144 |
return type.cast(proxy);
|
|
145 |
}
|
|
146 |
T proxy = JMX.newMXBeanProxy(mbsc, name, type);
|
|
147 |
objectNameToProxy.put(name, new WeakReference<Object>(proxy));
|
|
148 |
return proxy;
|
|
149 |
}
|
|
150 |
|
|
151 |
@Override
|
|
152 |
synchronized ObjectName mxbeanToObjectName(Object mxbean)
|
|
153 |
throws OpenDataException {
|
|
154 |
String wrong;
|
|
155 |
if (mxbean instanceof Proxy) {
|
|
156 |
InvocationHandler ih = Proxy.getInvocationHandler(mxbean);
|
|
157 |
if (ih instanceof MBeanServerInvocationHandler) {
|
|
158 |
MBeanServerInvocationHandler mbsih =
|
|
159 |
(MBeanServerInvocationHandler) ih;
|
|
160 |
if (mbsih.getMBeanServerConnection().equals(mbsc))
|
|
161 |
return mbsih.getObjectName();
|
|
162 |
else
|
|
163 |
wrong = "proxy for a different MBeanServer";
|
|
164 |
} else
|
|
165 |
wrong = "not a JMX proxy";
|
|
166 |
} else {
|
|
167 |
ObjectName name = mxbeanToObjectName.get(mxbean);
|
|
168 |
if (name != null)
|
|
169 |
return name;
|
|
170 |
wrong = "not an MXBean registered in this MBeanServer";
|
2
|
171 |
}
|
687
|
172 |
String s = (mxbean == null) ?
|
|
173 |
"null" : "object of type " + mxbean.getClass().getName();
|
|
174 |
throw new OpenDataException(
|
|
175 |
"Could not convert " + s + " to an ObjectName: " + wrong);
|
|
176 |
// Message will be strange if mxbean is null but it is not
|
|
177 |
// supposed to be.
|
|
178 |
}
|
|
179 |
|
|
180 |
synchronized void addReference(ObjectName name, Object mxbean)
|
|
181 |
throws InstanceAlreadyExistsException {
|
|
182 |
ObjectName existing = mxbeanToObjectName.get(mxbean);
|
|
183 |
if (existing != null) {
|
|
184 |
String multiname = AccessController.doPrivileged(
|
|
185 |
new GetPropertyAction("jmx.mxbean.multiname"));
|
|
186 |
if (!"true".equalsIgnoreCase(multiname)) {
|
|
187 |
throw new InstanceAlreadyExistsException(
|
|
188 |
"MXBean already registered with name " + existing);
|
|
189 |
}
|
|
190 |
}
|
|
191 |
mxbeanToObjectName.put(mxbean, name);
|
|
192 |
}
|
|
193 |
|
|
194 |
synchronized boolean removeReference(ObjectName name, Object mxbean) {
|
|
195 |
if (name.equals(mxbeanToObjectName.get(mxbean))) {
|
|
196 |
mxbeanToObjectName.remove(mxbean);
|
|
197 |
return true;
|
|
198 |
} else
|
|
199 |
return false;
|
|
200 |
/* removeReference can be called when the above condition fails,
|
|
201 |
* notably if you try to register the same MXBean twice.
|
|
202 |
*/
|
|
203 |
}
|
|
204 |
|
|
205 |
private final WeakIdentityHashMap<Object, ObjectName>
|
|
206 |
mxbeanToObjectName = WeakIdentityHashMap.make();
|
|
207 |
private final Map<ObjectName, WeakReference<Object>>
|
|
208 |
objectNameToProxy = newMap();
|
|
209 |
private static WeakIdentityHashMap<MBeanServerConnection,
|
|
210 |
WeakReference<Plain>>
|
|
211 |
mbscToLookup = WeakIdentityHashMap.make();
|
|
212 |
}
|
|
213 |
|
|
214 |
private static class Prefix extends MXBeanLookup {
|
|
215 |
private final String prefix;
|
|
216 |
|
|
217 |
Prefix(MBeanServerConnection mbsc, String prefix) {
|
|
218 |
super(mbsc);
|
|
219 |
this.prefix = prefix;
|
|
220 |
}
|
|
221 |
|
|
222 |
@Override
|
|
223 |
<T> T objectNameToMXBean(ObjectName name, Class<T> type)
|
|
224 |
throws InvalidObjectException {
|
|
225 |
String domain = prefix + name.getDomain();
|
|
226 |
try {
|
|
227 |
name = switchDomain(domain, name);
|
|
228 |
} catch (MalformedObjectNameException e) {
|
|
229 |
throw EnvHelp.initCause(
|
|
230 |
new InvalidObjectException(e.getMessage()), e);
|
|
231 |
}
|
|
232 |
return JMX.newMXBeanProxy(mbsc, name, type);
|
|
233 |
}
|
|
234 |
|
|
235 |
@Override
|
|
236 |
ObjectName mxbeanToObjectName(Object mxbean)
|
|
237 |
throws OpenDataException {
|
|
238 |
ObjectName name = proxyToObjectName(mxbean);
|
|
239 |
String domain = name.getDomain();
|
|
240 |
if (!domain.startsWith(prefix)) {
|
|
241 |
throw new OpenDataException(
|
|
242 |
"Proxy's name does not start with " + prefix + ": " + name);
|
|
243 |
}
|
|
244 |
try {
|
|
245 |
name = switchDomain(domain.substring(prefix.length()), name);
|
|
246 |
} catch (MalformedObjectNameException e) {
|
|
247 |
throw EnvHelp.initCause(new OpenDataException(e.getMessage()), e);
|
|
248 |
}
|
|
249 |
return name;
|
2
|
250 |
}
|
|
251 |
}
|
|
252 |
|
687
|
253 |
ObjectName proxyToObjectName(Object proxy) {
|
|
254 |
InvocationHandler ih = Proxy.getInvocationHandler(proxy);
|
|
255 |
if (ih instanceof MBeanServerInvocationHandler) {
|
|
256 |
MBeanServerInvocationHandler mbsih =
|
|
257 |
(MBeanServerInvocationHandler) ih;
|
|
258 |
if (mbsih.getMBeanServerConnection().equals(mbsc))
|
|
259 |
return mbsih.getObjectName();
|
2
|
260 |
}
|
687
|
261 |
return null;
|
|
262 |
}
|
|
263 |
|
|
264 |
static MXBeanLookup getLookup() {
|
|
265 |
return currentLookup.get();
|
2
|
266 |
}
|
|
267 |
|
687
|
268 |
static void setLookup(MXBeanLookup lookup) {
|
|
269 |
currentLookup.set(lookup);
|
2
|
270 |
}
|
|
271 |
|
687
|
272 |
// Method temporarily added until we have ObjectName.switchDomain in the
|
|
273 |
// public API. Note that this method DOES NOT PRESERVE the order of
|
|
274 |
// keys in the ObjectName so it must not be used in the final release.
|
|
275 |
static ObjectName switchDomain(String domain, ObjectName name)
|
|
276 |
throws MalformedObjectNameException {
|
|
277 |
return new ObjectName(domain, name.getKeyPropertyList());
|
2
|
278 |
}
|
|
279 |
|
687
|
280 |
private static final ThreadLocal<MXBeanLookup> currentLookup =
|
|
281 |
new ThreadLocal<MXBeanLookup>();
|
|
282 |
|
|
283 |
final MBeanServerConnection mbsc;
|
2
|
284 |
}
|