2
|
1 |
/*
|
|
2 |
* Copyright 2003-2007 Sun Microsystems, Inc. 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. 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 |
|
|
27 |
package javax.management.remote;
|
|
28 |
|
1004
|
29 |
import com.sun.jmx.remote.util.EnvHelp;
|
2
|
30 |
import java.io.IOException;
|
|
31 |
import java.util.ArrayList;
|
|
32 |
import java.util.List;
|
|
33 |
import java.util.Map;
|
|
34 |
|
1004
|
35 |
import java.util.NoSuchElementException;
|
|
36 |
import javax.management.MBeanInfo; // for javadoc
|
2
|
37 |
import javax.management.MBeanNotificationInfo;
|
|
38 |
import javax.management.MBeanRegistration;
|
|
39 |
import javax.management.MBeanServer;
|
|
40 |
import javax.management.Notification;
|
|
41 |
import javax.management.NotificationBroadcasterSupport;
|
|
42 |
import javax.management.ObjectName;
|
1004
|
43 |
import javax.management.event.EventClientDelegate;
|
2
|
44 |
|
|
45 |
/**
|
|
46 |
* <p>Superclass of every connector server. A connector server is
|
|
47 |
* attached to an MBean server. It listens for client connection
|
|
48 |
* requests and creates a connection for each one.</p>
|
|
49 |
*
|
|
50 |
* <p>A connector server is associated with an MBean server either by
|
|
51 |
* registering it in that MBean server, or by passing the MBean server
|
|
52 |
* to its constructor.</p>
|
|
53 |
*
|
|
54 |
* <p>A connector server is inactive when created. It only starts
|
|
55 |
* listening for client connections when the {@link #start() start}
|
|
56 |
* method is called. A connector server stops listening for client
|
|
57 |
* connections when the {@link #stop() stop} method is called or when
|
|
58 |
* the connector server is unregistered from its MBean server.</p>
|
|
59 |
*
|
|
60 |
* <p>Stopping a connector server does not unregister it from its
|
|
61 |
* MBean server. A connector server once stopped cannot be
|
|
62 |
* restarted.</p>
|
|
63 |
*
|
|
64 |
* <p>Each time a client connection is made or broken, a notification
|
|
65 |
* of class {@link JMXConnectionNotification} is emitted.</p>
|
|
66 |
*
|
|
67 |
* @since 1.5
|
|
68 |
*/
|
|
69 |
public abstract class JMXConnectorServer
|
|
70 |
extends NotificationBroadcasterSupport
|
|
71 |
implements JMXConnectorServerMBean, MBeanRegistration, JMXAddressable {
|
|
72 |
|
|
73 |
/**
|
|
74 |
* <p>Name of the attribute that specifies the authenticator for a
|
|
75 |
* connector server. The value associated with this attribute, if
|
|
76 |
* any, must be an object that implements the interface {@link
|
|
77 |
* JMXAuthenticator}.</p>
|
|
78 |
*/
|
|
79 |
public static final String AUTHENTICATOR =
|
|
80 |
"jmx.remote.authenticator";
|
|
81 |
|
1004
|
82 |
/**
|
|
83 |
* <p>Name of the attribute that specifies whether this connector
|
|
84 |
* server can delegate notification handling to the
|
|
85 |
* {@linkplain javax.management.event Event Service}.
|
|
86 |
* The value associated with
|
|
87 |
* this attribute, if any, is a String, which must be equal,
|
|
88 |
* ignoring case, to {@code "true"} or {@code "false"}.</p>
|
|
89 |
*
|
|
90 |
* <p>Not all connector servers will understand this attribute, but the
|
|
91 |
* standard {@linkplain javax.management.remote.rmi.RMIConnectorServer
|
|
92 |
* RMI Connector Server} does.</p>
|
|
93 |
*
|
|
94 |
* <p>If this attribute is not present, then the system property of the
|
|
95 |
* same name (<code>{@value}</code>) is consulted. If that is not set
|
|
96 |
* either, then the Event Service is used if the connector server
|
|
97 |
* supports it.</p>
|
|
98 |
*
|
|
99 |
* @since 1.7
|
|
100 |
*/
|
|
101 |
public static final String DELEGATE_TO_EVENT_SERVICE =
|
|
102 |
"jmx.remote.delegate.event.service";
|
|
103 |
|
|
104 |
/**
|
|
105 |
* <p>Name of the attribute that specifies whether this connector
|
|
106 |
* server simulates the existence of the {@link EventClientDelegate}
|
|
107 |
* MBean. The value associated with this attribute, if any, must
|
|
108 |
* be a string that is equal to {@code "true"} or {@code "false"},
|
|
109 |
* ignoring case. If it is {@code "true"}, then the connector server
|
|
110 |
* will simulate an EventClientDelegate MBean, as described in {@link
|
|
111 |
* EventClientDelegate#newForwarder}. This MBean is needed for {@link
|
|
112 |
* javax.management.event.EventClient EventClient} to function correctly.</p>
|
|
113 |
*
|
|
114 |
* <p>Not all connector servers will understand this attribute, but the
|
|
115 |
* standard {@linkplain javax.management.remote.rmi.RMIConnectorServer
|
|
116 |
* RMI Connector Server} does. For a connector server that understands
|
|
117 |
* this attribute, the default value is {@code "true"}.</p>
|
|
118 |
*
|
|
119 |
* @since 1.7
|
|
120 |
*/
|
|
121 |
public static final String EVENT_CLIENT_DELEGATE_FORWARDER =
|
|
122 |
"jmx.remote.event.client.delegate.forwarder";
|
|
123 |
|
2
|
124 |
/**
|
|
125 |
* <p>Constructs a connector server that will be registered as an
|
|
126 |
* MBean in the MBean server it is attached to. This constructor
|
|
127 |
* is typically called by one of the <code>createMBean</code>
|
|
128 |
* methods when creating, within an MBean server, a connector
|
|
129 |
* server that makes it available remotely.</p>
|
|
130 |
*/
|
|
131 |
public JMXConnectorServer() {
|
|
132 |
this(null);
|
|
133 |
}
|
|
134 |
|
|
135 |
/**
|
|
136 |
* <p>Constructs a connector server that is attached to the given
|
|
137 |
* MBean server. A connector server that is created in this way
|
1004
|
138 |
* can be registered in a different MBean server, or not registered
|
|
139 |
* in any MBean server.</p>
|
2
|
140 |
*
|
|
141 |
* @param mbeanServer the MBean server that this connector server
|
|
142 |
* is attached to. Null if this connector server will be attached
|
|
143 |
* to an MBean server by being registered in it.
|
|
144 |
*/
|
|
145 |
public JMXConnectorServer(MBeanServer mbeanServer) {
|
1004
|
146 |
insertUserMBeanServer(mbeanServer);
|
2
|
147 |
}
|
|
148 |
|
|
149 |
/**
|
|
150 |
* <p>Returns the MBean server that this connector server is
|
1004
|
151 |
* attached to, or the first in a chain of user-added
|
|
152 |
* {@link MBeanServerForwarder}s, if any.</p>
|
2
|
153 |
*
|
|
154 |
* @return the MBean server that this connector server is attached
|
|
155 |
* to, or null if it is not yet attached to an MBean server.
|
1004
|
156 |
*
|
|
157 |
* @see #setMBeanServerForwarder
|
|
158 |
* @see #getSystemMBeanServer
|
2
|
159 |
*/
|
|
160 |
public synchronized MBeanServer getMBeanServer() {
|
1004
|
161 |
return userMBeanServer;
|
|
162 |
}
|
|
163 |
|
|
164 |
public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
|
|
165 |
if (mbsf == null)
|
|
166 |
throw new IllegalArgumentException("Invalid null argument: mbsf");
|
|
167 |
|
|
168 |
if (userMBeanServer != null)
|
|
169 |
mbsf.setMBeanServer(userMBeanServer);
|
|
170 |
insertUserMBeanServer(mbsf);
|
2
|
171 |
}
|
|
172 |
|
1004
|
173 |
/**
|
|
174 |
* <p>Remove a forwarder from the chain of forwarders. The forwarder can
|
|
175 |
* be in the system chain or the user chain. On successful return from
|
|
176 |
* this method, the first occurrence in the chain of an object that is
|
|
177 |
* {@linkplain Object#equals equal} to {@code mbsf} will have been
|
|
178 |
* removed.</p>
|
|
179 |
* @param mbsf the forwarder to remove
|
|
180 |
* @throws NoSuchElementException if there is no occurrence of {@code mbsf}
|
|
181 |
* in the chain.
|
|
182 |
* @throws IllegalArgumentException if {@code mbsf} is null.
|
|
183 |
*/
|
|
184 |
public synchronized void removeMBeanServerForwarder(MBeanServerForwarder mbsf) {
|
2
|
185 |
if (mbsf == null)
|
|
186 |
throw new IllegalArgumentException("Invalid null argument: mbsf");
|
|
187 |
|
1004
|
188 |
MBeanServerForwarder prev = null;
|
|
189 |
MBeanServer curr = systemMBeanServer;
|
|
190 |
while (curr instanceof MBeanServerForwarder && !mbsf.equals(curr)) {
|
|
191 |
prev = (MBeanServerForwarder) curr;
|
|
192 |
curr = prev.getMBeanServer();
|
|
193 |
}
|
|
194 |
if (!(curr instanceof MBeanServerForwarder))
|
|
195 |
throw new NoSuchElementException("MBeanServerForwarder not in chain");
|
|
196 |
MBeanServerForwarder deleted = (MBeanServerForwarder) curr;
|
|
197 |
MBeanServer next = deleted.getMBeanServer();
|
|
198 |
if (prev != null)
|
|
199 |
prev.setMBeanServer(next);
|
|
200 |
if (systemMBeanServer == deleted)
|
|
201 |
systemMBeanServer = next;
|
|
202 |
if (userMBeanServer == deleted)
|
|
203 |
userMBeanServer = next;
|
|
204 |
}
|
|
205 |
|
|
206 |
/*
|
|
207 |
* Set userMBeanServer to mbs and arrange for the end of the chain of
|
|
208 |
* system MBeanServerForwarders to point to it. See the comment before
|
|
209 |
* the systemMBeanServer and userMBeanServer field declarations.
|
|
210 |
*/
|
|
211 |
private void insertUserMBeanServer(MBeanServer mbs) {
|
|
212 |
MBeanServerForwarder lastSystemMBSF = null;
|
|
213 |
for (MBeanServer mbsi = systemMBeanServer;
|
|
214 |
mbsi != userMBeanServer;
|
|
215 |
mbsi = lastSystemMBSF.getMBeanServer()) {
|
|
216 |
lastSystemMBSF = (MBeanServerForwarder) mbsi;
|
|
217 |
}
|
|
218 |
userMBeanServer = mbs;
|
|
219 |
if (lastSystemMBSF == null)
|
|
220 |
systemMBeanServer = mbs;
|
|
221 |
else
|
|
222 |
lastSystemMBSF.setMBeanServer(mbs);
|
|
223 |
}
|
|
224 |
|
|
225 |
/**
|
|
226 |
* <p>Returns the first item in the chain of system and then user
|
|
227 |
* forwarders. In the simplest case, a {@code JMXConnectorServer}
|
|
228 |
* is connected directly to an {@code MBeanServer}. But there can
|
|
229 |
* also be a chain of {@link MBeanServerForwarder}s between the two.
|
|
230 |
* This chain consists of two sub-chains: first the <em>system chain</em>
|
|
231 |
* and then the <em>user chain</em>. Incoming requests are given to the
|
|
232 |
* first forwarder in the system chain. Each forwarder can handle
|
|
233 |
* a request itself, or more usually forward it to the next forwarder,
|
|
234 |
* perhaps with some extra behavior such as logging or security
|
|
235 |
* checking before or after the forwarding. The last forwarder in
|
|
236 |
* the system chain is followed by the first forwarder in the user
|
|
237 |
* chain.</p>
|
|
238 |
*
|
|
239 |
* <p>The <em>system chain</em> is usually
|
|
240 |
* defined by a connector server based on the environment Map;
|
|
241 |
* see {@link JMXConnectorServerFactory#newJMXConnectorServer}. Allowing the
|
|
242 |
* connector server to define its forwarders in this way ensures that
|
|
243 |
* they are in the correct order - some forwarders need to be inserted
|
|
244 |
* before others for correct behavior. It is possible to modify the
|
|
245 |
* system chain, for example using {@link #setSystemMBeanServerForwarder} or
|
|
246 |
* {@link #removeMBeanServerForwarder}, but in that case the system
|
|
247 |
* chain is no longer guaranteed to be correct.</p>
|
|
248 |
*
|
|
249 |
* <p>The <em>user chain</em> is defined by calling {@link
|
|
250 |
* #setMBeanServerForwarder} to insert forwarders at the head of the user
|
|
251 |
* chain.</p>
|
|
252 |
*
|
|
253 |
* <p>If there are no forwarders in either chain, then both
|
|
254 |
* {@link #getMBeanServer()} and {@code getSystemMBeanServer()} will
|
|
255 |
* return the {@code MBeanServer} for this connector server. If there
|
|
256 |
* are forwarders in the user chain but not the system chain, then
|
|
257 |
* both methods will return the first forwarder in the user chain.
|
|
258 |
* If there are forwarders in the system chain but not the user chain,
|
|
259 |
* then {@code getSystemMBeanServer()} will return the first forwarder
|
|
260 |
* in the system chain, and {@code getMBeanServer()} will return the
|
|
261 |
* {@code MBeanServer} for this connector server. Finally, if there
|
|
262 |
* are forwarders in each chain then {@code getSystemMBeanServer()}
|
|
263 |
* will return the first forwarder in the system chain, and {@code
|
|
264 |
* getMBeanServer()} will return the first forwarder in the user chain.</p>
|
|
265 |
*
|
|
266 |
* <p>This code illustrates how the chains can be traversed:</p>
|
|
267 |
*
|
|
268 |
* <pre>
|
|
269 |
* JMXConnectorServer cs;
|
|
270 |
* System.out.println("system chain:");
|
|
271 |
* MBeanServer mbs = cs.getSystemMBeanServer();
|
|
272 |
* while (true) {
|
|
273 |
* if (mbs == cs.getMBeanServer())
|
|
274 |
* System.out.println("user chain:");
|
|
275 |
* if (!(mbs instanceof MBeanServerForwarder))
|
|
276 |
* break;
|
|
277 |
* MBeanServerForwarder mbsf = (MBeanServerForwarder) mbs;
|
|
278 |
* System.out.println("--forwarder: " + mbsf);
|
|
279 |
* mbs = mbsf.getMBeanServer();
|
|
280 |
* }
|
|
281 |
* System.out.println("--MBean Server");
|
|
282 |
* </pre>
|
|
283 |
*
|
|
284 |
* @return the first item in the system chain of forwarders.
|
|
285 |
*
|
|
286 |
* @see #setSystemMBeanServerForwarder
|
|
287 |
*/
|
|
288 |
public synchronized MBeanServer getSystemMBeanServer() {
|
|
289 |
return systemMBeanServer;
|
|
290 |
}
|
|
291 |
|
|
292 |
/**
|
|
293 |
* <p>Inserts an object that intercepts requests for the MBean server
|
|
294 |
* that arrive through this connector server. This object will be
|
|
295 |
* supplied as the <code>MBeanServer</code> for any new connection
|
|
296 |
* created by this connector server. Existing connections are
|
|
297 |
* unaffected.</p>
|
|
298 |
*
|
|
299 |
* <p>This method can be called more than once with different
|
|
300 |
* {@link MBeanServerForwarder} objects. The result is a chain
|
|
301 |
* of forwarders. The last forwarder added is the first in the chain.</p>
|
|
302 |
*
|
|
303 |
* <p>This method modifies the system chain of {@link MBeanServerForwarder}s.
|
|
304 |
* Usually user code should change the user chain instead, via
|
|
305 |
* {@link #setMBeanServerForwarder}.</p>
|
|
306 |
*
|
|
307 |
* <p>Not all connector servers support a system chain of forwarders.
|
|
308 |
* Calling this method on a connector server that does not will produce an
|
|
309 |
* {@link UnsupportedOperationException}.</p>
|
|
310 |
*
|
|
311 |
* <p>Suppose {@code mbs} is the result of {@link #getSystemMBeanServer()}
|
|
312 |
* before calling this method. If {@code mbs} is not null, then
|
|
313 |
* {@code mbsf.setMBeanServer(mbs)} will be called. If doing so
|
|
314 |
* produces an exception, this method throws the same exception without
|
|
315 |
* any other effect. If {@code mbs} is null, or if the call to
|
|
316 |
* {@code mbsf.setMBeanServer(mbs)} succeeds, then this method will
|
|
317 |
* return normally and {@code getSystemMBeanServer()} will then return
|
|
318 |
* {@code mbsf}.</p>
|
|
319 |
*
|
|
320 |
* <p>The result of {@link #getMBeanServer()} is unchanged by this method.</p>
|
|
321 |
*
|
|
322 |
* @param mbsf the new <code>MBeanServerForwarder</code>.
|
|
323 |
*
|
|
324 |
* @throws IllegalArgumentException if the call to {@link
|
|
325 |
* MBeanServerForwarder#setMBeanServer mbsf.setMBeanServer} fails
|
|
326 |
* with <code>IllegalArgumentException</code>, or if
|
|
327 |
* <code>mbsf</code> is null.
|
|
328 |
*
|
|
329 |
* @throws UnsupportedOperationException if
|
|
330 |
* {@link #supportsSystemMBeanServerForwarder} returns false.
|
|
331 |
*
|
|
332 |
* @see #getSystemMBeanServer()
|
|
333 |
*/
|
|
334 |
public synchronized void setSystemMBeanServerForwarder(
|
|
335 |
MBeanServerForwarder mbsf) {
|
|
336 |
if (mbsf == null)
|
|
337 |
throw new IllegalArgumentException("Invalid null argument: mbsf");
|
|
338 |
mustSupportSystemMBSF();
|
|
339 |
|
|
340 |
if (systemMBeanServer != null)
|
|
341 |
mbsf.setMBeanServer(systemMBeanServer);
|
|
342 |
systemMBeanServer = mbsf;
|
|
343 |
}
|
|
344 |
|
|
345 |
/**
|
|
346 |
* <p>Returns true if this connector server supports a system chain of
|
|
347 |
* {@link MBeanServerForwarder}s. The default implementation of this
|
|
348 |
* method returns false. Connector servers that do support the system
|
|
349 |
* chain must override this method to return true.
|
|
350 |
*
|
|
351 |
* @return true if this connector server supports the system chain of
|
|
352 |
* forwarders.
|
|
353 |
*/
|
|
354 |
public boolean supportsSystemMBeanServerForwarder() {
|
|
355 |
return false;
|
|
356 |
}
|
|
357 |
|
|
358 |
private void mustSupportSystemMBSF() {
|
|
359 |
if (!supportsSystemMBeanServerForwarder()) {
|
|
360 |
throw new UnsupportedOperationException(
|
|
361 |
"System MBeanServerForwarder not supported by this " +
|
|
362 |
"connector server");
|
|
363 |
}
|
|
364 |
}
|
|
365 |
|
|
366 |
/**
|
|
367 |
* <p>Install {@link MBeanServerForwarder}s in the system chain
|
|
368 |
* based on the attributes in the given {@code Map}. A connector
|
|
369 |
* server that {@linkplain #supportsSystemMBeanServerForwarder supports}
|
|
370 |
* a system chain of {@code MBeanServerForwarder}s can call this method
|
|
371 |
* to add forwarders to that chain based on the contents of {@code env}.
|
|
372 |
* In order:</p>
|
|
373 |
*
|
|
374 |
* <ul>
|
|
375 |
*
|
|
376 |
* <li>If {@link #EVENT_CLIENT_DELEGATE_FORWARDER} is absent, or is
|
|
377 |
* present with the value {@code "true"}, then a forwarder with the
|
|
378 |
* functionality of {@link EventClientDelegate#newForwarder} is inserted
|
|
379 |
* at the start of the system chain.</li>
|
|
380 |
*
|
|
381 |
* </ul>
|
|
382 |
*
|
|
383 |
* <p>For {@code EVENT_CLIENT_DELEGATE_FORWARDER}, if the
|
|
384 |
* attribute is absent from the {@code Map} and a system property
|
|
385 |
* of the same name is defined, then the value of the system
|
|
386 |
* property is used as if it were in the {@code Map}.
|
|
387 |
*
|
|
388 |
* <p>Attributes in {@code env} that are not listed above are ignored
|
|
389 |
* by this method.</p>
|
|
390 |
*
|
|
391 |
* @throws UnsupportedOperationException if {@link
|
|
392 |
* #supportsSystemMBeanServerForwarder} is false.
|
|
393 |
*/
|
|
394 |
protected void installStandardForwarders(Map<String, ?> env) {
|
|
395 |
mustSupportSystemMBSF();
|
|
396 |
|
|
397 |
// Remember that forwarders must be added in reverse order!
|
|
398 |
|
|
399 |
boolean ecd = EnvHelp.computeBooleanFromString(
|
|
400 |
env, EVENT_CLIENT_DELEGATE_FORWARDER, false, true);
|
|
401 |
|
|
402 |
if (ecd) {
|
|
403 |
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
|
|
404 |
setSystemMBeanServerForwarder(mbsf);
|
|
405 |
}
|
2
|
406 |
}
|
|
407 |
|
|
408 |
public String[] getConnectionIds() {
|
|
409 |
synchronized (connectionIds) {
|
|
410 |
return connectionIds.toArray(new String[connectionIds.size()]);
|
|
411 |
}
|
|
412 |
}
|
|
413 |
|
|
414 |
/**
|
|
415 |
* <p>Returns a client stub for this connector server. A client
|
|
416 |
* stub is a serializable object whose {@link
|
|
417 |
* JMXConnector#connect(Map) connect} method can be used to make
|
|
418 |
* one new connection to this connector server.</p>
|
|
419 |
*
|
|
420 |
* <p>A given connector need not support the generation of client
|
|
421 |
* stubs. However, the connectors specified by the JMX Remote API do
|
|
422 |
* (JMXMP Connector and RMI Connector).</p>
|
|
423 |
*
|
|
424 |
* <p>The default implementation of this method uses {@link
|
|
425 |
* #getAddress} and {@link JMXConnectorFactory} to generate the
|
|
426 |
* stub, with code equivalent to the following:</p>
|
|
427 |
*
|
|
428 |
* <pre>
|
|
429 |
* JMXServiceURL addr = {@link #getAddress() getAddress()};
|
|
430 |
* return {@link JMXConnectorFactory#newJMXConnector(JMXServiceURL, Map)
|
|
431 |
* JMXConnectorFactory.newJMXConnector(addr, env)};
|
|
432 |
* </pre>
|
|
433 |
*
|
|
434 |
* <p>A connector server for which this is inappropriate must
|
|
435 |
* override this method so that it either implements the
|
|
436 |
* appropriate logic or throws {@link
|
|
437 |
* UnsupportedOperationException}.</p>
|
|
438 |
*
|
|
439 |
* @param env client connection parameters of the same sort that
|
|
440 |
* could be provided to {@link JMXConnector#connect(Map)
|
|
441 |
* JMXConnector.connect(Map)}. Can be null, which is equivalent
|
|
442 |
* to an empty map.
|
|
443 |
*
|
|
444 |
* @return a client stub that can be used to make a new connection
|
|
445 |
* to this connector server.
|
|
446 |
*
|
|
447 |
* @exception UnsupportedOperationException if this connector
|
|
448 |
* server does not support the generation of client stubs.
|
|
449 |
*
|
|
450 |
* @exception IllegalStateException if the JMXConnectorServer is
|
|
451 |
* not started (see {@link JMXConnectorServerMBean#isActive()}).
|
|
452 |
*
|
|
453 |
* @exception IOException if a communications problem means that a
|
|
454 |
* stub cannot be created.
|
|
455 |
**/
|
|
456 |
public JMXConnector toJMXConnector(Map<String,?> env)
|
|
457 |
throws IOException
|
|
458 |
{
|
|
459 |
if (!isActive()) throw new
|
|
460 |
IllegalStateException("Connector is not active");
|
|
461 |
JMXServiceURL addr = getAddress();
|
|
462 |
return JMXConnectorFactory.newJMXConnector(addr, env);
|
|
463 |
}
|
|
464 |
|
|
465 |
/**
|
|
466 |
* <p>Returns an array indicating the notifications that this MBean
|
|
467 |
* sends. The implementation in <code>JMXConnectorServer</code>
|
|
468 |
* returns an array with one element, indicating that it can emit
|
|
469 |
* notifications of class {@link JMXConnectionNotification} with
|
|
470 |
* the types defined in that class. A subclass that can emit other
|
|
471 |
* notifications should return an array that contains this element
|
|
472 |
* plus descriptions of the other notifications.</p>
|
|
473 |
*
|
|
474 |
* @return the array of possible notifications.
|
|
475 |
*/
|
|
476 |
public MBeanNotificationInfo[] getNotificationInfo() {
|
|
477 |
final String[] types = {
|
|
478 |
JMXConnectionNotification.OPENED,
|
|
479 |
JMXConnectionNotification.CLOSED,
|
|
480 |
JMXConnectionNotification.FAILED,
|
|
481 |
};
|
|
482 |
final String className = JMXConnectionNotification.class.getName();
|
|
483 |
final String description =
|
|
484 |
"A client connection has been opened or closed";
|
|
485 |
return new MBeanNotificationInfo[] {
|
|
486 |
new MBeanNotificationInfo(types, className, description),
|
|
487 |
};
|
|
488 |
}
|
|
489 |
|
|
490 |
/**
|
|
491 |
* <p>Called by a subclass when a new client connection is opened.
|
|
492 |
* Adds <code>connectionId</code> to the list returned by {@link
|
|
493 |
* #getConnectionIds()}, then emits a {@link
|
|
494 |
* JMXConnectionNotification} with type {@link
|
|
495 |
* JMXConnectionNotification#OPENED}.</p>
|
|
496 |
*
|
|
497 |
* @param connectionId the ID of the new connection. This must be
|
|
498 |
* different from the ID of any connection previously opened by
|
|
499 |
* this connector server.
|
|
500 |
*
|
|
501 |
* @param message the message for the emitted {@link
|
|
502 |
* JMXConnectionNotification}. Can be null. See {@link
|
|
503 |
* Notification#getMessage()}.
|
|
504 |
*
|
|
505 |
* @param userData the <code>userData</code> for the emitted
|
|
506 |
* {@link JMXConnectionNotification}. Can be null. See {@link
|
|
507 |
* Notification#getUserData()}.
|
|
508 |
*
|
|
509 |
* @exception NullPointerException if <code>connectionId</code> is
|
|
510 |
* null.
|
|
511 |
*/
|
|
512 |
protected void connectionOpened(String connectionId,
|
|
513 |
String message,
|
|
514 |
Object userData) {
|
|
515 |
|
|
516 |
if (connectionId == null)
|
|
517 |
throw new NullPointerException("Illegal null argument");
|
|
518 |
|
|
519 |
synchronized (connectionIds) {
|
|
520 |
connectionIds.add(connectionId);
|
|
521 |
}
|
|
522 |
|
|
523 |
sendNotification(JMXConnectionNotification.OPENED, connectionId,
|
|
524 |
message, userData);
|
|
525 |
}
|
|
526 |
|
|
527 |
/**
|
|
528 |
* <p>Called by a subclass when a client connection is closed
|
|
529 |
* normally. Removes <code>connectionId</code> from the list returned
|
|
530 |
* by {@link #getConnectionIds()}, then emits a {@link
|
|
531 |
* JMXConnectionNotification} with type {@link
|
|
532 |
* JMXConnectionNotification#CLOSED}.</p>
|
|
533 |
*
|
|
534 |
* @param connectionId the ID of the closed connection.
|
|
535 |
*
|
|
536 |
* @param message the message for the emitted {@link
|
|
537 |
* JMXConnectionNotification}. Can be null. See {@link
|
|
538 |
* Notification#getMessage()}.
|
|
539 |
*
|
|
540 |
* @param userData the <code>userData</code> for the emitted
|
|
541 |
* {@link JMXConnectionNotification}. Can be null. See {@link
|
|
542 |
* Notification#getUserData()}.
|
|
543 |
*
|
|
544 |
* @exception NullPointerException if <code>connectionId</code>
|
|
545 |
* is null.
|
|
546 |
*/
|
|
547 |
protected void connectionClosed(String connectionId,
|
|
548 |
String message,
|
|
549 |
Object userData) {
|
|
550 |
|
|
551 |
if (connectionId == null)
|
|
552 |
throw new NullPointerException("Illegal null argument");
|
|
553 |
|
|
554 |
synchronized (connectionIds) {
|
|
555 |
connectionIds.remove(connectionId);
|
|
556 |
}
|
|
557 |
|
|
558 |
sendNotification(JMXConnectionNotification.CLOSED, connectionId,
|
|
559 |
message, userData);
|
|
560 |
}
|
|
561 |
|
|
562 |
/**
|
|
563 |
* <p>Called by a subclass when a client connection fails.
|
|
564 |
* Removes <code>connectionId</code> from the list returned by
|
|
565 |
* {@link #getConnectionIds()}, then emits a {@link
|
|
566 |
* JMXConnectionNotification} with type {@link
|
|
567 |
* JMXConnectionNotification#FAILED}.</p>
|
|
568 |
*
|
|
569 |
* @param connectionId the ID of the failed connection.
|
|
570 |
*
|
|
571 |
* @param message the message for the emitted {@link
|
|
572 |
* JMXConnectionNotification}. Can be null. See {@link
|
|
573 |
* Notification#getMessage()}.
|
|
574 |
*
|
|
575 |
* @param userData the <code>userData</code> for the emitted
|
|
576 |
* {@link JMXConnectionNotification}. Can be null. See {@link
|
|
577 |
* Notification#getUserData()}.
|
|
578 |
*
|
|
579 |
* @exception NullPointerException if <code>connectionId</code> is
|
|
580 |
* null.
|
|
581 |
*/
|
|
582 |
protected void connectionFailed(String connectionId,
|
|
583 |
String message,
|
|
584 |
Object userData) {
|
|
585 |
|
|
586 |
if (connectionId == null)
|
|
587 |
throw new NullPointerException("Illegal null argument");
|
|
588 |
|
|
589 |
synchronized (connectionIds) {
|
|
590 |
connectionIds.remove(connectionId);
|
|
591 |
}
|
|
592 |
|
|
593 |
sendNotification(JMXConnectionNotification.FAILED, connectionId,
|
|
594 |
message, userData);
|
|
595 |
}
|
|
596 |
|
|
597 |
private void sendNotification(String type, String connectionId,
|
|
598 |
String message, Object userData) {
|
|
599 |
Notification notif =
|
|
600 |
new JMXConnectionNotification(type,
|
|
601 |
getNotificationSource(),
|
|
602 |
connectionId,
|
|
603 |
nextSequenceNumber(),
|
|
604 |
message,
|
|
605 |
userData);
|
|
606 |
sendNotification(notif);
|
|
607 |
}
|
|
608 |
|
|
609 |
private synchronized Object getNotificationSource() {
|
|
610 |
if (myName != null)
|
|
611 |
return myName;
|
|
612 |
else
|
|
613 |
return this;
|
|
614 |
}
|
|
615 |
|
|
616 |
private static long nextSequenceNumber() {
|
|
617 |
synchronized (sequenceNumberLock) {
|
|
618 |
return sequenceNumber++;
|
|
619 |
}
|
|
620 |
}
|
|
621 |
|
|
622 |
// implements MBeanRegistration
|
|
623 |
/**
|
|
624 |
* <p>Called by an MBean server when this connector server is
|
|
625 |
* registered in that MBean server. This connector server becomes
|
|
626 |
* attached to the MBean server and its {@link #getMBeanServer()}
|
|
627 |
* method will return <code>mbs</code>.</p>
|
|
628 |
*
|
|
629 |
* <p>If this connector server is already attached to an MBean
|
|
630 |
* server, this method has no effect. The MBean server it is
|
|
631 |
* attached to is not necessarily the one it is being registered
|
|
632 |
* in.</p>
|
|
633 |
*
|
|
634 |
* @param mbs the MBean server in which this connection server is
|
|
635 |
* being registered.
|
|
636 |
*
|
|
637 |
* @param name The object name of the MBean.
|
|
638 |
*
|
|
639 |
* @return The name under which the MBean is to be registered.
|
|
640 |
*
|
|
641 |
* @exception NullPointerException if <code>mbs</code> or
|
|
642 |
* <code>name</code> is null.
|
|
643 |
*/
|
|
644 |
public synchronized ObjectName preRegister(MBeanServer mbs,
|
|
645 |
ObjectName name) {
|
|
646 |
if (mbs == null || name == null)
|
|
647 |
throw new NullPointerException("Null MBeanServer or ObjectName");
|
1004
|
648 |
if (userMBeanServer == null) {
|
|
649 |
insertUserMBeanServer(mbs);
|
2
|
650 |
myName = name;
|
|
651 |
}
|
|
652 |
return name;
|
|
653 |
}
|
|
654 |
|
|
655 |
public void postRegister(Boolean registrationDone) {
|
|
656 |
// do nothing
|
|
657 |
}
|
|
658 |
|
|
659 |
/**
|
|
660 |
* <p>Called by an MBean server when this connector server is
|
|
661 |
* unregistered from that MBean server. If this connector server
|
|
662 |
* was attached to that MBean server by being registered in it,
|
|
663 |
* and if the connector server is still active,
|
|
664 |
* then unregistering it will call the {@link #stop stop} method.
|
|
665 |
* If the <code>stop</code> method throws an exception, the
|
|
666 |
* unregistration attempt will fail. It is recommended to call
|
|
667 |
* the <code>stop</code> method explicitly before unregistering
|
|
668 |
* the MBean.</p>
|
|
669 |
*
|
|
670 |
* @exception IOException if thrown by the {@link #stop stop} method.
|
|
671 |
*/
|
|
672 |
public synchronized void preDeregister() throws Exception {
|
|
673 |
if (myName != null && isActive()) {
|
|
674 |
stop();
|
|
675 |
myName = null; // just in case stop is buggy and doesn't stop
|
|
676 |
}
|
|
677 |
}
|
|
678 |
|
|
679 |
public void postDeregister() {
|
|
680 |
myName = null;
|
|
681 |
}
|
|
682 |
|
1004
|
683 |
/*
|
|
684 |
* Fields describing the chains of forwarders (MBeanServerForwarders).
|
|
685 |
* In the general case, the forwarders look something like this:
|
|
686 |
*
|
|
687 |
* systemMBeanServer userMBeanServer
|
|
688 |
* | |
|
|
689 |
* v v
|
|
690 |
* mbsf1 -> mbsf2 -> mbsf3 -> mbsf4 -> mbsf5 -> mbs
|
|
691 |
*
|
|
692 |
* Here, each mbsfi is an MBeanServerForwarder, and the arrows
|
|
693 |
* illustrate its getMBeanServer() method. The last MBeanServerForwarder
|
|
694 |
* can point to an MBeanServer that is not instanceof MBeanServerForwarder,
|
|
695 |
* here mbs.
|
|
696 |
*
|
|
697 |
* Initially, the chain can be empty if this JMXConnectorServer was
|
|
698 |
* constructed without an MBeanServer. In this case, both systemMBS
|
|
699 |
* and userMBS will be null. If there is initially an MBeanServer,
|
|
700 |
* then both systemMBS and userMBS will point to it.
|
|
701 |
*
|
|
702 |
* Whenever userMBS is changed, the system chain must be updated. If there
|
|
703 |
* are forwarders in the system chain (between systemMBS and userMBS in the
|
|
704 |
* picture above), then the last one must point to the old value of userMBS
|
|
705 |
* (possibly null). It must be updated to point to the new value. If there
|
|
706 |
* are no forwarders in the system chain, then systemMBS must be updated to
|
|
707 |
* the new value of userMBS. The invariant is that starting from systemMBS
|
|
708 |
* and repeatedly calling MBSF.getMBeanServer() you will end up at
|
|
709 |
* userMBS. The implication is that you will not see any MBeanServer
|
|
710 |
* object on the way that is not also an MBeanServerForwarder.
|
|
711 |
*
|
|
712 |
* The method insertUserMBeanServer contains the logic to change userMBS
|
|
713 |
* and adjust the system chain appropriately.
|
|
714 |
*
|
|
715 |
* If userMBS is null and this JMXConnectorServer is registered in an
|
|
716 |
* MBeanServer, then userMBS becomes that MBeanServer, and the system
|
|
717 |
* chain must be updated as just described.
|
|
718 |
*
|
|
719 |
* When systemMBS is updated, there is no effect on userMBS. The system
|
|
720 |
* chain may contain forwarders even though the user chain is empty
|
|
721 |
* (there is no MBeanServer). In that case an attempt to forward an
|
|
722 |
* incoming request through the chain will fall off the end and fail with a
|
|
723 |
* NullPointerException. Usually a connector server will refuse to start()
|
|
724 |
* if it is not attached to an MBS, so this situation should not arise.
|
2
|
725 |
*/
|
1004
|
726 |
|
|
727 |
private MBeanServer userMBeanServer;
|
|
728 |
|
|
729 |
private MBeanServer systemMBeanServer;
|
2
|
730 |
|
|
731 |
/**
|
|
732 |
* The name used to registered this server in an MBeanServer.
|
|
733 |
* It is null if the this server is not registered or has been unregistered.
|
|
734 |
*/
|
|
735 |
private ObjectName myName;
|
|
736 |
|
|
737 |
private List<String> connectionIds = new ArrayList<String>();
|
|
738 |
|
|
739 |
private static final int[] sequenceNumberLock = new int[0];
|
|
740 |
private static long sequenceNumber;
|
|
741 |
}
|