blob: cef0397f19aee9d2ba286a1e88520a79da7eccbb [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002-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
26package javax.management.remote.rmi;
27
28
29import com.sun.jmx.remote.security.MBeanServerFileAccessController;
30import com.sun.jmx.remote.util.ClassLogger;
31import com.sun.jmx.remote.util.EnvHelp;
32
33import java.io.ByteArrayOutputStream;
34import java.io.IOException;
35import java.io.ObjectOutputStream;
36import java.net.MalformedURLException;
37import java.rmi.server.RMIClientSocketFactory;
38import java.rmi.server.RMIServerSocketFactory;
39import java.util.Collections;
40import java.util.HashMap;
41import java.util.HashSet;
42import java.util.Hashtable;
43import java.util.Map;
44import java.util.Set;
45
46import javax.management.InstanceNotFoundException;
47import javax.management.MBeanServer;
48
49import javax.management.remote.JMXConnectionNotification;
50import javax.management.remote.JMXConnector;
51import javax.management.remote.JMXConnectorServer;
52import javax.management.remote.JMXServiceURL;
53import javax.management.remote.MBeanServerForwarder;
54
55import javax.naming.InitialContext;
56import javax.naming.NamingException;
57
58/**
59 * <p>A JMX API connector server that creates RMI-based connections
60 * from remote clients. Usually, such connector servers are made
61 * using {@link javax.management.remote.JMXConnectorServerFactory
62 * JMXConnectorServerFactory}. However, specialized applications can
63 * use this class directly, for example with an {@link RMIServerImpl}
64 * object.</p>
65 *
66 * @since 1.5
67 */
68public class RMIConnectorServer extends JMXConnectorServer {
69 /**
70 * <p>Name of the attribute that specifies whether the {@link
71 * RMIServer} stub that represents an RMI connector server should
72 * override an existing stub at the same address. The value
73 * associated with this attribute, if any, should be a string that
74 * is equal, ignoring case, to <code>"true"</code> or
75 * <code>"false"</code>. The default value is false.</p>
76 */
77 public static final String JNDI_REBIND_ATTRIBUTE =
78 "jmx.remote.jndi.rebind";
79
80 /**
81 * <p>Name of the attribute that specifies the {@link
82 * RMIClientSocketFactory} for the RMI objects created in
83 * conjunction with this connector. The value associated with this
84 * attribute must be of type <code>RMIClientSocketFactory</code> and can
85 * only be specified in the <code>Map</code> argument supplied when
86 * creating a connector server.</p>
87 */
88 public static final String RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE =
89 "jmx.remote.rmi.client.socket.factory";
90
91 /**
92 * <p>Name of the attribute that specifies the {@link
93 * RMIServerSocketFactory} for the RMI objects created in
94 * conjunction with this connector. The value associated with this
95 * attribute must be of type <code>RMIServerSocketFactory</code> and can
96 * only be specified in the <code>Map</code> argument supplied when
97 * creating a connector server.</p>
98 */
99 public static final String RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE =
100 "jmx.remote.rmi.server.socket.factory";
101
102 /**
103 * <p>Makes an <code>RMIConnectorServer</code>.
104 * This is equivalent to calling {@link #RMIConnectorServer(
105 * JMXServiceURL,Map,RMIServerImpl,MBeanServer)
106 * RMIConnectorServer(directoryURL,environment,null,null)}</p>
107 *
108 * @param url the URL defining how to create the connector server.
109 * Cannot be null.
110 *
111 * @param environment attributes governing the creation and
112 * storing of the RMI object. Can be null, which is equivalent to
113 * an empty Map.
114 *
115 * @exception IllegalArgumentException if <code>url</code> is null.
116 *
117 * @exception MalformedURLException if <code>url</code> does not
118 * conform to the syntax for an RMI connector, or if its protocol
119 * is not recognized by this implementation. Only "rmi" and "iiop"
120 * are valid when this constructor is used.
121 *
122 * @exception IOException if the connector server cannot be created
123 * for some reason or if it is inevitable that its {@link #start()
124 * start} method will fail.
125 */
126 public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment)
127 throws IOException {
128 this(url, environment, (MBeanServer) null);
129 }
130
131 /**
132 * <p>Makes an <code>RMIConnectorServer</code> for the given MBean
133 * server.
134 * This is equivalent to calling {@link #RMIConnectorServer(
135 * JMXServiceURL,Map,RMIServerImpl,MBeanServer)
136 * RMIConnectorServer(directoryURL,environment,null,mbeanServer)}</p>
137 *
138 * @param url the URL defining how to create the connector server.
139 * Cannot be null.
140 *
141 * @param environment attributes governing the creation and
142 * storing of the RMI object. Can be null, which is equivalent to
143 * an empty Map.
144 *
145 * @param mbeanServer the MBean server to which the new connector
146 * server is attached, or null if it will be attached by being
147 * registered as an MBean in the MBean server.
148 *
149 * @exception IllegalArgumentException if <code>url</code> is null.
150 *
151 * @exception MalformedURLException if <code>url</code> does not
152 * conform to the syntax for an RMI connector, or if its protocol
153 * is not recognized by this implementation. Only "rmi" and "iiop"
154 * are valid when this constructor is used.
155 *
156 * @exception IOException if the connector server cannot be created
157 * for some reason or if it is inevitable that its {@link #start()
158 * start} method will fail.
159 */
160 public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment,
161 MBeanServer mbeanServer)
162 throws IOException {
163 this(url, environment, (RMIServerImpl) null, mbeanServer);
164 }
165
166 /**
167 * <p>Makes an <code>RMIConnectorServer</code> for the given MBean
168 * server.</p>
169 *
170 * @param url the URL defining how to create the connector server.
171 * Cannot be null.
172 *
173 * @param environment attributes governing the creation and
174 * storing of the RMI object. Can be null, which is equivalent to
175 * an empty Map.
176 *
177 * @param rmiServerImpl An implementation of the RMIServer interface,
178 * consistent with the protocol type specified in <var>url</var>.
179 * If this parameter is non null, the protocol type specified by
180 * <var>url</var> is not constrained, and is assumed to be valid.
181 * Otherwise, only "rmi" and "iiop" will be recognized.
182 *
183 * @param mbeanServer the MBean server to which the new connector
184 * server is attached, or null if it will be attached by being
185 * registered as an MBean in the MBean server.
186 *
187 * @exception IllegalArgumentException if <code>url</code> is null.
188 *
189 * @exception MalformedURLException if <code>url</code> does not
190 * conform to the syntax for an RMI connector, or if its protocol
191 * is not recognized by this implementation. Only "rmi" and "iiop"
192 * are recognized when <var>rmiServerImpl</var> is null.
193 *
194 * @exception IOException if the connector server cannot be created
195 * for some reason or if it is inevitable that its {@link #start()
196 * start} method will fail.
197 *
198 * @see #start
199 */
200 public RMIConnectorServer(JMXServiceURL url, Map<String,?> environment,
201 RMIServerImpl rmiServerImpl,
202 MBeanServer mbeanServer)
203 throws IOException {
204 super(mbeanServer);
205
206 if (url == null) throw new
207 IllegalArgumentException("Null JMXServiceURL");
208 if (rmiServerImpl == null) {
209 final String prt = url.getProtocol();
210 if (prt == null || !(prt.equals("rmi") || prt.equals("iiop"))) {
211 final String msg = "Invalid protocol type: " + prt;
212 throw new MalformedURLException(msg);
213 }
214 final String urlPath = url.getURLPath();
215 if (!urlPath.equals("")
216 && !urlPath.equals("/")
217 && !urlPath.startsWith("/jndi/")) {
218 final String msg = "URL path must be empty or start with " +
219 "/jndi/";
220 throw new MalformedURLException(msg);
221 }
222 }
223
224 if (environment == null)
225 this.attributes = Collections.emptyMap();
226 else {
227 EnvHelp.checkAttributes(environment);
228 this.attributes = Collections.unmodifiableMap(environment);
229 }
230
231 this.address = url;
232 this.rmiServerImpl = rmiServerImpl;
233 }
234
235 /**
236 * <p>Returns a client stub for this connector server. A client
237 * stub is a serializable object whose {@link
238 * JMXConnector#connect(Map) connect} method can be used to make
239 * one new connection to this connector server.</p>
240 *
241 * @param env client connection parameters of the same sort that
242 * could be provided to {@link JMXConnector#connect(Map)
243 * JMXConnector.connect(Map)}. Can be null, which is equivalent
244 * to an empty map.
245 *
246 * @return a client stub that can be used to make a new connection
247 * to this connector server.
248 *
249 * @exception UnsupportedOperationException if this connector
250 * server does not support the generation of client stubs.
251 *
252 * @exception IllegalStateException if the JMXConnectorServer is
253 * not started (see {@link #isActive()}).
254 *
255 * @exception IOException if a communications problem means that a
256 * stub cannot be created.
257 **/
258 public JMXConnector toJMXConnector(Map<String,?> env) throws IOException {
259 // The serialized for of rmiServerImpl is automatically
260 // a RMI server stub.
261 if (!isActive()) throw new
262 IllegalStateException("Connector is not active");
263
264 // Merge maps
265 Map<String, Object> usemap = new HashMap<String, Object>(
266 (this.attributes==null)?Collections.<String, Object>emptyMap():
267 this.attributes);
268
269 if (env != null) {
270 EnvHelp.checkAttributes(env);
271 usemap.putAll(env);
272 }
273
274 usemap = EnvHelp.filterAttributes(usemap);
275
276 final RMIServer stub=(RMIServer)rmiServerImpl.toStub();
277
278 return new RMIConnector(stub, usemap);
279 }
280
281 /**
282 * <p>Activates the connector server, that is starts listening for
283 * client connections. Calling this method when the connector
284 * server is already active has no effect. Calling this method
285 * when the connector server has been stopped will generate an
286 * <code>IOException</code>.</p>
287 *
288 * <p>The behavior of this method when called for the first time
289 * depends on the parameters that were supplied at construction,
290 * as described below.</p>
291 *
292 * <p>First, an object of a subclass of {@link RMIServerImpl} is
293 * required, to export the connector server through RMI:</p>
294 *
295 * <ul>
296 *
297 * <li>If an <code>RMIServerImpl</code> was supplied to the
298 * constructor, it is used.
299 *
300 * <li>Otherwise, if the protocol part of the
301 * <code>JMXServiceURL</code> supplied to the constructor was
302 * <code>iiop</code>, an object of type {@link RMIIIOPServerImpl}
303 * is created.
304 *
305 * <li>Otherwise, if the <code>JMXServiceURL</code>
306 * was null, or its protocol part was <code>rmi</code>, an object
307 * of type {@link RMIJRMPServerImpl} is created.
308 *
309 * <li>Otherwise, the implementation can create an
310 * implementation-specific {@link RMIServerImpl} or it can throw
311 * {@link MalformedURLException}.
312 *
313 * </ul>
314 *
315 * <p>If the given address includes a JNDI directory URL as
316 * specified in the package documentation for {@link
317 * javax.management.remote.rmi}, then this
318 * <code>RMIConnectorServer</code> will bootstrap by binding the
319 * <code>RMIServerImpl</code> to the given address.</p>
320 *
321 * <p>If the URL path part of the <code>JMXServiceURL</code> was
322 * empty or a single slash (<code>/</code>), then the RMI object
323 * will not be bound to a directory. Instead, a reference to it
324 * will be encoded in the URL path of the RMIConnectorServer
325 * address (returned by {@link #getAddress()}). The encodings for
326 * <code>rmi</code> and <code>iiop</code> are described in the
327 * package documentation for {@link
328 * javax.management.remote.rmi}.</p>
329 *
330 * <p>The behavior when the URL path is neither empty nor a JNDI
331 * directory URL, or when the protocol is neither <code>rmi</code>
332 * nor <code>iiop</code>, is implementation defined, and may
333 * include throwing {@link MalformedURLException} when the
334 * connector server is created or when it is started.</p>
335 *
336 * @exception IllegalStateException if the connector server has
337 * not been attached to an MBean server.
338 * @exception IOException if the connector server cannot be
339 * started.
340 */
341 public synchronized void start() throws IOException {
342 final boolean tracing = logger.traceOn();
343
344 if (state == STARTED) {
345 if (tracing) logger.trace("start", "already started");
346 return;
347 } else if (state == STOPPED) {
348 if (tracing) logger.trace("start", "already stopped");
349 throw new IOException("The server has been stopped.");
350 }
351
352 if (getMBeanServer() == null)
353 throw new IllegalStateException("This connector server is not " +
354 "attached to an MBean server");
355
356 // Check the internal access file property to see
357 // if an MBeanServerForwarder is to be provided
358 //
359 if (attributes != null) {
360 // Check if access file property is specified
361 //
362 String accessFile =
363 (String) attributes.get("jmx.remote.x.access.file");
364 if (accessFile != null) {
365 // Access file property specified, create an instance
366 // of the MBeanServerFileAccessController class
367 //
368 MBeanServerForwarder mbsf = null;
369 try {
370 mbsf = new MBeanServerFileAccessController(accessFile);
371 } catch (IOException e) {
372 throw EnvHelp.initCause(
373 new IllegalArgumentException(e.getMessage()), e);
374 }
375 // Set the MBeanServerForwarder
376 //
377 setMBeanServerForwarder(mbsf);
378 }
379 }
380
381 try {
382 if (tracing) logger.trace("start", "setting default class loader");
383 defaultClassLoader =
384 EnvHelp.resolveServerClassLoader(attributes, getMBeanServer());
385 } catch (InstanceNotFoundException infc) {
386 IllegalArgumentException x = new
387 IllegalArgumentException("ClassLoader not found: "+infc);
388 throw EnvHelp.initCause(x,infc);
389 }
390
391 if (tracing) logger.trace("start", "setting RMIServer object");
392 final RMIServerImpl rmiServer;
393
394 if (rmiServerImpl != null)
395 rmiServer = rmiServerImpl;
396 else
397 rmiServer = newServer();
398
399 rmiServer.setMBeanServer(getMBeanServer());
400 rmiServer.setDefaultClassLoader(defaultClassLoader);
401 rmiServer.setRMIConnectorServer(this);
402 rmiServer.export();
403
404 try {
405 if (tracing) logger.trace("start", "getting RMIServer object to export");
406 final RMIServer objref = objectToBind(rmiServer, attributes);
407
408 if (address != null && address.getURLPath().startsWith("/jndi/")) {
409 final String jndiUrl = address.getURLPath().substring(6);
410
411 if (tracing)
412 logger.trace("start", "Using external directory: " + jndiUrl);
413
414 final boolean rebind = EnvHelp.computeBooleanFromString(
415 attributes,
416 JNDI_REBIND_ATTRIBUTE);
417
418 if (tracing)
419 logger.trace("start", JNDI_REBIND_ATTRIBUTE + "=" + rebind);
420
421 try {
422 if (tracing) logger.trace("start", "binding to " + jndiUrl);
423
424 final Hashtable usemap = EnvHelp.mapToHashtable(attributes);
425
426 bind(jndiUrl, usemap, objref, rebind);
427
428 boundJndiUrl = jndiUrl;
429 } catch (NamingException e) {
430 // fit e in the nested exception if we are on 1.4
431 throw newIOException("Cannot bind to URL ["+jndiUrl+"]: "
432 + e, e);
433 }
434 } else {
435 // if jndiURL is null, we must encode the stub into the URL.
436 if (tracing) logger.trace("start", "Encoding URL");
437
438 encodeStubInAddress(objref, attributes);
439
440 if (tracing) logger.trace("start", "Encoded URL: " + this.address);
441 }
442 } catch (Exception e) {
443 try {
444 rmiServer.close();
445 } catch (Exception x) {
446 // OK: we are already throwing another exception
447 }
448 if (e instanceof RuntimeException)
449 throw (RuntimeException) e;
450 else if (e instanceof IOException)
451 throw (IOException) e;
452 else
453 throw newIOException("Got unexpected exception while " +
454 "starting the connector server: "
455 + e, e);
456 }
457
458 rmiServerImpl = rmiServer;
459
460 synchronized(openedServers) {
461 openedServers.add(this);
462 }
463
464 state = STARTED;
465
466 if (tracing) {
467 logger.trace("start", "Connector Server Address = " + address);
468 logger.trace("start", "started.");
469 }
470 }
471
472 /**
473 * <p>Deactivates the connector server, that is, stops listening for
474 * client connections. Calling this method will also close all
475 * client connections that were made by this server. After this
476 * method returns, whether normally or with an exception, the
477 * connector server will not create any new client
478 * connections.</p>
479 *
480 * <p>Once a connector server has been stopped, it cannot be started
481 * again.</p>
482 *
483 * <p>Calling this method when the connector server has already
484 * been stopped has no effect. Calling this method when the
485 * connector server has not yet been started will disable the
486 * connector server object permanently.</p>
487 *
488 * <p>If closing a client connection produces an exception, that
489 * exception is not thrown from this method. A {@link
490 * JMXConnectionNotification} is emitted from this MBean with the
491 * connection ID of the connection that could not be closed.</p>
492 *
493 * <p>Closing a connector server is a potentially slow operation.
494 * For example, if a client machine with an open connection has
495 * crashed, the close operation might have to wait for a network
496 * protocol timeout. Callers that do not want to block in a close
497 * operation should do it in a separate thread.</p>
498 *
499 * <p>This method calls the method {@link RMIServerImpl#close()
500 * close} on the connector server's <code>RMIServerImpl</code>
501 * object.</p>
502 *
503 * <p>If the <code>RMIServerImpl</code> was bound to a JNDI
504 * directory by the {@link #start() start} method, it is unbound
505 * from the directory by this method.</p>
506 *
507 * @exception IOException if the server cannot be closed cleanly,
508 * or if the <code>RMIServerImpl</code> cannot be unbound from the
509 * directory. When this exception is thrown, the server has
510 * already attempted to close all client connections, if
511 * appropriate; to call {@link RMIServerImpl#close()}; and to
512 * unbind the <code>RMIServerImpl</code> from its directory, if
513 * appropriate. All client connections are closed except possibly
514 * those that generated exceptions when the server attempted to
515 * close them.
516 */
517 public void stop() throws IOException {
518 final boolean tracing = logger.traceOn();
519
520 synchronized (this) {
521 if (state == STOPPED) {
522 if (tracing) logger.trace("stop","already stopped.");
523 return;
524 } else if (state == CREATED) {
525 if (tracing) logger.trace("stop","not started yet.");
526 }
527
528 if (tracing) logger.trace("stop", "stopping.");
529 state = STOPPED;
530 }
531
532 synchronized(openedServers) {
533 openedServers.remove(this);
534 }
535
536 IOException exception = null;
537
538 // rmiServerImpl can be null if stop() called without start()
539 if (rmiServerImpl != null) {
540 try {
541 if (tracing) logger.trace("stop", "closing RMI server.");
542 rmiServerImpl.close();
543 } catch (IOException e) {
544 if (tracing) logger.trace("stop", "failed to close RMI server: " + e);
545 if (logger.debugOn()) logger.debug("stop",e);
546 exception = e;
547 }
548 }
549
550 if (boundJndiUrl != null) {
551 try {
552 if (tracing)
553 logger.trace("stop",
554 "unbind from external directory: " + boundJndiUrl);
555
556 final Hashtable usemap = EnvHelp.mapToHashtable(attributes);
557
558 InitialContext ctx =
559 new InitialContext(usemap);
560
561 ctx.unbind(boundJndiUrl);
562
563 ctx.close();
564 } catch (NamingException e) {
565 if (tracing) logger.trace("stop", "failed to unbind RMI server: "+e);
566 if (logger.debugOn()) logger.debug("stop",e);
567 // fit e in as the nested exception if we are on 1.4
568 if (exception == null)
569 exception = newIOException("Cannot bind to URL: " + e, e);
570 }
571 }
572
573 if (exception != null) throw exception;
574
575 if (tracing) logger.trace("stop", "stopped");
576 }
577
578 public synchronized boolean isActive() {
579 return (state == STARTED);
580 }
581
582 public JMXServiceURL getAddress() {
583 if (!isActive())
584 return null;
585 return address;
586 }
587
588 public Map<String,?> getAttributes() {
589 Map<String, ?> map = EnvHelp.filterAttributes(attributes);
590 return Collections.unmodifiableMap(map);
591 }
592
593 public synchronized
594 void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
595 super.setMBeanServerForwarder(mbsf);
596 if (rmiServerImpl != null)
597 rmiServerImpl.setMBeanServer(getMBeanServer());
598 }
599
600 /* We repeat the definitions of connection{Opened,Closed,Failed}
601 here so that they are accessible to other classes in this package
602 even though they have protected access. */
603
604 protected void connectionOpened(String connectionId, String message,
605 Object userData) {
606 super.connectionOpened(connectionId, message, userData);
607 }
608
609 protected void connectionClosed(String connectionId, String message,
610 Object userData) {
611 super.connectionClosed(connectionId, message, userData);
612 }
613
614 protected void connectionFailed(String connectionId, String message,
615 Object userData) {
616 super.connectionFailed(connectionId, message, userData);
617 }
618
619 /**
620 * Bind a stub to a registry.
621 * @param jndiUrl URL of the stub in the registry, extracted
622 * from the <code>JMXServiceURL</code>.
623 * @param attributes A Hashtable containing environment parameters,
624 * built from the Map specified at this object creation.
625 * @param rmiServer The object to bind in the registry
626 * @param rebind true if the object must be rebound.
627 **/
628 void bind(String jndiUrl, Hashtable attributes,
629 RMIServer rmiServer, boolean rebind)
630 throws NamingException, MalformedURLException {
631 // if jndiURL is not null, we nust bind the stub to a
632 // directory.
633 InitialContext ctx =
634 new InitialContext(attributes);
635
636 if (rebind)
637 ctx.rebind(jndiUrl, rmiServer);
638 else
639 ctx.bind(jndiUrl, rmiServer);
640 ctx.close();
641 }
642
643 /**
644 * Creates a new RMIServerImpl.
645 **/
646 RMIServerImpl newServer() throws IOException {
647 final boolean iiop = isIiopURL(address,true);
648 final int port;
649 if (address == null)
650 port = 0;
651 else
652 port = address.getPort();
653 if (iiop)
654 return newIIOPServer(attributes);
655 else
656 return newJRMPServer(attributes, port);
657 }
658
659 /**
660 * Encode a stub into the JMXServiceURL.
661 * @param rmiServer The stub object to encode in the URL
662 * @param attributes A Map containing environment parameters,
663 * built from the Map specified at this object creation.
664 **/
665 private void encodeStubInAddress(RMIServer rmiServer, Map attributes)
666 throws IOException {
667
668 final String protocol, host;
669 final int port;
670
671 if (address == null) {
672 if (rmiServer instanceof javax.rmi.CORBA.Stub)
673 protocol = "iiop";
674 else
675 protocol = "rmi";
676 host = null; // will default to local host name
677 port = 0;
678 } else {
679 protocol = address.getProtocol();
680 host = (address.getHost().equals("")) ? null : address.getHost();
681 port = address.getPort();
682 }
683
684 final String urlPath = encodeStub(rmiServer, attributes);
685
686 address = new JMXServiceURL(protocol, host, port, urlPath);
687 }
688
689 static boolean isIiopURL(JMXServiceURL directoryURL, boolean strict)
690 throws MalformedURLException {
691 String protocol = directoryURL.getProtocol();
692 if (protocol.equals("rmi"))
693 return false;
694 else if (protocol.equals("iiop"))
695 return true;
696 else if (strict) {
697
698 throw new MalformedURLException("URL must have protocol " +
699 "\"rmi\" or \"iiop\": \"" +
700 protocol + "\"");
701 }
702 return false;
703 }
704
705 /**
706 * Returns the IOR of the given rmiServer.
707 **/
708 static String encodeStub(RMIServer rmiServer, Map env) throws IOException {
709 if (rmiServer instanceof javax.rmi.CORBA.Stub)
710 return "/ior/" + encodeIIOPStub(rmiServer, env);
711 else
712 return "/stub/" + encodeJRMPStub(rmiServer, env);
713 }
714
715 static String encodeJRMPStub(RMIServer rmiServer, Map env)
716 throws IOException {
717 ByteArrayOutputStream bout = new ByteArrayOutputStream();
718 ObjectOutputStream oout = new ObjectOutputStream(bout);
719 oout.writeObject(rmiServer);
720 oout.close();
721 byte[] bytes = bout.toByteArray();
722 return byteArrayToBase64(bytes);
723 }
724
725 static String encodeIIOPStub(RMIServer rmiServer, Map env)
726 throws IOException {
727 try {
728 javax.rmi.CORBA.Stub stub =
729 (javax.rmi.CORBA.Stub) rmiServer;
730 return stub._orb().object_to_string(stub);
731 } catch (org.omg.CORBA.BAD_OPERATION x) {
732 throw newIOException(x.getMessage(), x);
733 }
734 }
735
736 /**
737 * Object that we will bind to the registry.
738 * This object is a stub connected to our RMIServerImpl.
739 **/
740 private static RMIServer objectToBind(RMIServerImpl rmiServer, Map env)
741 throws IOException {
742 return RMIConnector.
743 connectStub((RMIServer)rmiServer.toStub(),env);
744 }
745
746 private static RMIServerImpl newJRMPServer(Map<String, ?> env, int port)
747 throws IOException {
748 RMIClientSocketFactory csf = (RMIClientSocketFactory)
749 env.get(RMI_CLIENT_SOCKET_FACTORY_ATTRIBUTE);
750 RMIServerSocketFactory ssf = (RMIServerSocketFactory)
751 env.get(RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE);
752 return new RMIJRMPServerImpl(port, csf, ssf, env);
753 }
754
755 private static RMIServerImpl newIIOPServer(Map<String, ?> env)
756 throws IOException {
757 return new RMIIIOPServerImpl(env);
758 }
759
760 private static String byteArrayToBase64(byte[] a) {
761 int aLen = a.length;
762 int numFullGroups = aLen/3;
763 int numBytesInPartialGroup = aLen - 3*numFullGroups;
764 int resultLen = 4*((aLen + 2)/3);
765 final StringBuilder result = new StringBuilder(resultLen);
766
767 // Translate all full groups from byte array elements to Base64
768 int inCursor = 0;
769 for (int i=0; i<numFullGroups; i++) {
770 int byte0 = a[inCursor++] & 0xff;
771 int byte1 = a[inCursor++] & 0xff;
772 int byte2 = a[inCursor++] & 0xff;
773 result.append(intToAlpha[byte0 >> 2]);
774 result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]);
775 result.append(intToAlpha[(byte1 << 2)&0x3f | (byte2 >> 6)]);
776 result.append(intToAlpha[byte2 & 0x3f]);
777 }
778
779 // Translate partial group if present
780 if (numBytesInPartialGroup != 0) {
781 int byte0 = a[inCursor++] & 0xff;
782 result.append(intToAlpha[byte0 >> 2]);
783 if (numBytesInPartialGroup == 1) {
784 result.append(intToAlpha[(byte0 << 4) & 0x3f]);
785 result.append("==");
786 } else {
787 // assert numBytesInPartialGroup == 2;
788 int byte1 = a[inCursor++] & 0xff;
789 result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]);
790 result.append(intToAlpha[(byte1 << 2)&0x3f]);
791 result.append('=');
792 }
793 }
794 // assert inCursor == a.length;
795 // assert result.length() == resultLen;
796 return result.toString();
797 }
798
799 /**
800 * This array is a lookup table that translates 6-bit positive integer
801 * index values into their "Base64 Alphabet" equivalents as specified
802 * in Table 1 of RFC 2045.
803 */
804 private static final char intToAlpha[] = {
805 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
806 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
807 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
808 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
809 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
810 };
811
812 /**
813 * Construct a new IOException with a nested exception.
814 * The nested exception is set only if JDK >= 1.4
815 */
816 private static IOException newIOException(String message,
817 Throwable cause) {
818 final IOException x = new IOException(message);
819 return EnvHelp.initCause(x,cause);
820 }
821
822
823 // Private variables
824 // -----------------
825
826 private static ClassLogger logger =
827 new ClassLogger("javax.management.remote.rmi", "RMIConnectorServer");
828
829 private JMXServiceURL address;
830 private RMIServerImpl rmiServerImpl;
831 private final Map<String, ?> attributes;
832 private ClassLoader defaultClassLoader = null;
833
834 private String boundJndiUrl;
835
836 // state
837 private static final int CREATED = 0;
838 private static final int STARTED = 1;
839 private static final int STOPPED = 2;
840
841 private int state = CREATED;
842 private final static Set<RMIConnectorServer> openedServers =
843 new HashSet<RMIConnectorServer>();
844}