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