1. /*
  2. * @(#)RMIConnector.java 1.115 04/06/28
  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. // JMX
  9. import javax.management.Attribute;
  10. import javax.management.AttributeList;
  11. import javax.management.AttributeNotFoundException;
  12. import javax.management.InstanceAlreadyExistsException;
  13. import javax.management.InstanceNotFoundException;
  14. import javax.management.IntrospectionException;
  15. import javax.management.InvalidAttributeValueException;
  16. import javax.management.ListenerNotFoundException;
  17. import javax.management.MalformedObjectNameException;
  18. import javax.management.MBeanException;
  19. import javax.management.MBeanInfo;
  20. import javax.management.MBeanRegistrationException;
  21. import javax.management.MBeanServerConnection;
  22. import javax.management.MBeanServerNotification;
  23. import javax.management.NotCompliantMBeanException;
  24. import javax.management.Notification;
  25. import javax.management.NotificationBroadcasterSupport;
  26. import javax.management.NotificationEmitter;
  27. import javax.management.NotificationFilter;
  28. import javax.management.NotificationFilterSupport;
  29. import javax.management.NotificationListener;
  30. import javax.management.ObjectInstance;
  31. import javax.management.ObjectName;
  32. import javax.management.QueryExp;
  33. import javax.management.ReflectionException;
  34. import javax.management.remote.JMXAuthenticator;
  35. import javax.management.remote.JMXConnectionNotification;
  36. import javax.management.remote.JMXConnector;
  37. import javax.management.remote.JMXConnectorFactory;
  38. import javax.management.remote.JMXServiceURL;
  39. import javax.management.remote.NotificationResult;
  40. import javax.management.remote.TargetedNotification;
  41. import javax.management.remote.JMXServerErrorException;
  42. import java.lang.ref.WeakReference;
  43. import java.lang.reflect.Constructor;
  44. import java.lang.reflect.InvocationTargetException;
  45. // Util
  46. import java.util.Arrays;
  47. import java.util.ArrayList;
  48. import java.util.Collections;
  49. import java.util.HashMap;
  50. import java.util.Map;
  51. import java.util.Properties;
  52. import java.util.Set;
  53. import java.util.WeakHashMap;
  54. // IO
  55. import java.io.ByteArrayInputStream;
  56. import java.io.IOException;
  57. import java.io.InputStream;
  58. import java.io.InvalidObjectException;
  59. import java.io.ObjectInputStream;
  60. import java.io.ObjectStreamClass;
  61. import java.io.Serializable;
  62. import java.io.WriteAbortedException;
  63. import java.io.NotSerializableException;
  64. // Net
  65. import java.net.MalformedURLException;
  66. // RMI
  67. import java.rmi.MarshalledObject;
  68. import java.rmi.NoSuchObjectException;
  69. import java.rmi.MarshalException;
  70. import java.rmi.UnmarshalException;
  71. import java.rmi.ServerException;
  72. import java.rmi.server.RemoteObject;
  73. import java.rmi.server.RemoteRef;
  74. //IIOP RMI
  75. import javax.rmi.PortableRemoteObject;
  76. import javax.rmi.CORBA.Stub;
  77. import org.omg.CORBA.portable.Delegate;
  78. // JNDI
  79. import javax.naming.InitialContext;
  80. import javax.naming.NamingException;
  81. import com.sun.jmx.remote.internal.ClientNotifForwarder;
  82. import com.sun.jmx.remote.internal.ClientCommunicatorAdmin;
  83. import com.sun.jmx.remote.internal.ClientListenerInfo;
  84. import com.sun.jmx.remote.internal.ProxyInputStream;
  85. import com.sun.jmx.remote.internal.ProxyRef;
  86. import com.sun.jmx.remote.util.ClassLogger;
  87. import com.sun.jmx.remote.util.EnvHelp;
  88. // SECURITY
  89. import java.security.AccessController;
  90. import java.security.PrivilegedAction;
  91. import java.security.PrivilegedExceptionAction;
  92. import java.security.ProtectionDomain;
  93. import javax.security.auth.Subject;
  94. /**
  95. * <p>A connection to a remote RMI connector. Usually, such
  96. * connections are made using {@link
  97. * javax.management.remote.JMXConnectorFactory JMXConnectorFactory}.
  98. * However, specialized applications can use this class directly, for
  99. * example with an {@link RMIServer} stub obtained without going
  100. * through JNDI.</p>
  101. *
  102. * @since 1.5
  103. * @since.unbundled 1.0
  104. */
  105. public class RMIConnector implements JMXConnector, Serializable {
  106. private static final ClassLogger logger =
  107. new ClassLogger("javax.management.remote.rmi", "RMIConnector");
  108. private static final long serialVersionUID = 817323035842634473L;
  109. private RMIConnector(RMIServer rmiServer, JMXServiceURL address,
  110. Map environment) {
  111. if (rmiServer == null && address == null) throw new
  112. IllegalArgumentException("rmiServer and jmxServiceURL both null");
  113. initTransients();
  114. this.rmiServer = rmiServer;
  115. this.jmxServiceURL = address;
  116. if (environment == null) {
  117. this.env = Collections.EMPTY_MAP;
  118. } else {
  119. EnvHelp.checkAttributes(environment);
  120. this.env = Collections.unmodifiableMap(environment);
  121. }
  122. }
  123. /**
  124. * <p>Constructs an <code>RMIConnector</code> that will connect
  125. * the RMI connector server with the given address.</p>
  126. *
  127. * <p>The address can refer directly to the connector server,
  128. * using one of the following syntaxes:</p>
  129. *
  130. * <pre>
  131. * service:jmx:rmi://<em>[host[:port]]</em>/stub/<em>encoded-stub</em>
  132. * service:jmx:iiop://<em>[host[:port]]</em>/ior/<em>encoded-IOR</em>
  133. * </pre>
  134. *
  135. * <p>(Here, the square brackets <code>[]</code> are not part of the
  136. * address but indicate that the host and port are optional.)</p>
  137. *
  138. * <p>The address can instead indicate where to find an RMI stub
  139. * through JNDI, using one of the following syntaxes:</p>
  140. *
  141. * <pre>
  142. * service:jmx:rmi://<em>[host[:port]]</em>/jndi/<em>jndi-name</em>
  143. * service:jmx:iiop://<em>[host[:port]]</em>/jndi/<em>jndi-name</em>
  144. * </pre>
  145. *
  146. * <p>An implementation may also recognize additional address
  147. * syntaxes, for example:</p>
  148. *
  149. * <pre>
  150. * service:jmx:iiop://<em>[host[:port]]</em>/stub/<em>encoded-stub</em>
  151. * </pre>
  152. *
  153. * @param url the address of the RMI connector server.
  154. *
  155. * @param environment additional attributes specifying how to make
  156. * the connection. For JNDI-based addresses, these attributes can
  157. * usefully include JNDI attributes recognized by {@link
  158. * InitialContext#InitialContext(Hashtable) InitialContext}. This
  159. * parameter can be null, which is equivalent to an empty Map.
  160. *
  161. * @exception IllegalArgumentException if <code>url</code>
  162. * is null.
  163. */
  164. public RMIConnector(JMXServiceURL url, Map<String,?> environment) {
  165. this(null, url, environment);
  166. }
  167. /**
  168. * <p>Constructs an <code>RMIConnector</code> using the given RMI stub.
  169. *
  170. * @param rmiServer an RMI stub representing the RMI connector server.
  171. * @param environment additional attributes specifying how to make
  172. * the connection. This parameter can be null, which is
  173. * equivalent to an empty Map.
  174. *
  175. * @exception IllegalArgumentException if <code>rmiServer</code>
  176. * is null.
  177. */
  178. public RMIConnector(RMIServer rmiServer, Map<String,?> environment) {
  179. this(rmiServer, null, environment);
  180. }
  181. /**
  182. * <p>Returns a string representation of this object. In general,
  183. * the <code>toString</code> method returns a string that
  184. * "textually represents" this object. The result should be a
  185. * concise but informative representation that is easy for a
  186. * person to read.</p>
  187. *
  188. * @return a String representation of this object.
  189. **/
  190. public String toString() {
  191. final StringBuffer b = new StringBuffer(this.getClass().getName());
  192. b.append(":");
  193. if (rmiServer != null) {
  194. b.append(" rmiServer=").append(rmiServer.toString());
  195. }
  196. if (jmxServiceURL != null) {
  197. if (rmiServer!=null) b.append(",");
  198. b.append(" jmxServiceURL=").append(jmxServiceURL.toString());
  199. }
  200. return b.toString();
  201. }
  202. //--------------------------------------------------------------------
  203. // implements JMXConnector interface
  204. //--------------------------------------------------------------------
  205. public void connect() throws IOException {
  206. connect(null);
  207. }
  208. public synchronized void connect(Map<String,?> environment)
  209. throws IOException {
  210. final boolean tracing = logger.traceOn();
  211. String idstr = (tracing?"["+this.toString()+"]":null);
  212. if (terminated) {
  213. logger.trace("connect",idstr + " already closed.");
  214. throw new IOException("Connector closed");
  215. }
  216. if (connected) {
  217. logger.trace("connect",idstr + " already connected.");
  218. return;
  219. }
  220. try {
  221. if (tracing) logger.trace("connect",idstr + " connecting...");
  222. final Map usemap =
  223. new HashMap((this.env==null)?Collections.EMPTY_MAP:this.env);
  224. if (environment != null) {
  225. EnvHelp.checkAttributes(environment);
  226. usemap.putAll(environment);
  227. }
  228. // Get RMIServer stub from directory or URL encoding if needed.
  229. if (tracing) logger.trace("connect",idstr + " finding stub...");
  230. RMIServer stub = (rmiServer!=null)?rmiServer:
  231. findRMIServer(jmxServiceURL, usemap);
  232. // Connect IIOP Stub if needed.
  233. if (tracing) logger.trace("connect",idstr + " connecting stub...");
  234. stub = connectStub(stub,usemap);
  235. idstr = (tracing?"["+this.toString()+"]":null);
  236. // Calling newClient on the RMIServer stub.
  237. if (tracing)
  238. logger.trace("connect",idstr + " getting connection...");
  239. Object credentials = usemap.get(CREDENTIALS);
  240. connection = getConnection(stub, credentials);
  241. // Always use one of:
  242. // ClassLoader provided in Map at connect time,
  243. // or contextClassLoader at connect time.
  244. if (tracing)
  245. logger.trace("connect",idstr + " getting class loader...");
  246. defaultClassLoader = EnvHelp.resolveClientClassLoader(usemap);
  247. usemap.put(JMXConnectorFactory.DEFAULT_CLASS_LOADER,
  248. defaultClassLoader);
  249. rmiNotifClient = new RMINotifClient(defaultClassLoader, usemap);
  250. env = usemap;
  251. final long checkPeriod = EnvHelp.getConnectionCheckPeriod(usemap);
  252. communicatorAdmin = new RMIClientCommunicatorAdmin(checkPeriod);
  253. connected = true;
  254. // The connectionId variable is used in doStart(), when
  255. // reconnecting, to identify the "old" connection.
  256. //
  257. connectionId = getConnectionId();
  258. Notification connectedNotif =
  259. new JMXConnectionNotification(JMXConnectionNotification.OPENED,
  260. this,
  261. connectionId,
  262. clientNotifID++,
  263. "Successful connection",
  264. null);
  265. sendNotification(connectedNotif);
  266. if (tracing) logger.trace("connect",idstr + " done...");
  267. } catch (IOException e) {
  268. if (tracing)
  269. logger.trace("connect",idstr + " failed to connect: " + e);
  270. throw e;
  271. } catch (RuntimeException e) {
  272. if (tracing)
  273. logger.trace("connect",idstr + " failed to connect: " + e);
  274. throw e;
  275. } catch (NamingException e) {
  276. final String msg = "Failed to retrieve RMIServer stub: " + e;
  277. if (tracing) logger.trace("connect",idstr + " " + msg);
  278. throw (IOException) EnvHelp.initCause(new IOException(msg),e);
  279. }
  280. }
  281. public synchronized String getConnectionId() throws IOException {
  282. if (terminated || !connected) {
  283. if (logger.traceOn())
  284. logger.trace("getConnectionId","["+this.toString()+
  285. "] not connected.");
  286. throw new IOException("Not connected");
  287. }
  288. // we do a remote call to have an IOException if the connection is broken.
  289. // see the bug 4939578
  290. return connection.getConnectionId();
  291. }
  292. public synchronized MBeanServerConnection getMBeanServerConnection()
  293. throws IOException {
  294. return getMBeanServerConnection(null);
  295. }
  296. public synchronized MBeanServerConnection
  297. getMBeanServerConnection(Subject delegationSubject)
  298. throws IOException {
  299. if (terminated) {
  300. if (logger.traceOn())
  301. logger.trace("getMBeanServerConnection","[" + this.toString() +
  302. "] already closed.");
  303. throw new IOException("Connection closed");
  304. } else if (!connected) {
  305. if (logger.traceOn())
  306. logger.trace("getMBeanServerConnection","[" + this.toString() +
  307. "] is not connected.");
  308. throw new IOException("Not connected");
  309. }
  310. MBeanServerConnection mbsc =
  311. (MBeanServerConnection) rmbscMap.get(delegationSubject);
  312. if (mbsc != null)
  313. return mbsc;
  314. mbsc = new RemoteMBeanServerConnection(delegationSubject);
  315. rmbscMap.put(delegationSubject, mbsc);
  316. return mbsc;
  317. }
  318. public void
  319. addConnectionNotificationListener(NotificationListener listener,
  320. NotificationFilter filter,
  321. Object handback) {
  322. if (listener == null)
  323. throw new NullPointerException("listener");
  324. connectionBroadcaster.addNotificationListener(listener, filter,
  325. handback);
  326. }
  327. public void
  328. removeConnectionNotificationListener(NotificationListener listener)
  329. throws ListenerNotFoundException {
  330. if (listener == null)
  331. throw new NullPointerException("listener");
  332. connectionBroadcaster.removeNotificationListener(listener);
  333. }
  334. public void
  335. removeConnectionNotificationListener(NotificationListener listener,
  336. NotificationFilter filter,
  337. Object handback)
  338. throws ListenerNotFoundException {
  339. if (listener == null)
  340. throw new NullPointerException("listener");
  341. connectionBroadcaster.removeNotificationListener(listener, filter,
  342. handback);
  343. }
  344. private void sendNotification(Notification n) {
  345. connectionBroadcaster.sendNotification(n);
  346. }
  347. public synchronized void close() throws IOException {
  348. // Return if already cleanly closed.
  349. //
  350. final boolean tracing = logger.traceOn();
  351. final boolean debug = logger.debugOn();
  352. final String idstr = (tracing?"["+this.toString()+"]":null);
  353. synchronized(terminationLock) {
  354. if (terminated) {
  355. if (closeException == null) {
  356. if (tracing) logger.trace("close",idstr + " already closed.");
  357. return;
  358. }
  359. } else {
  360. terminated = true;
  361. }
  362. }
  363. if (closeException != null && tracing) {
  364. // Already closed, but not cleanly. Attempt again.
  365. //
  366. if (tracing) {
  367. logger.trace("close",idstr + " had failed: " + closeException);
  368. logger.trace("close",idstr + " attempting to close again.");
  369. }
  370. }
  371. String savedConnectionId = null;
  372. if (connected) {
  373. savedConnectionId = connectionId;
  374. }
  375. closeException = null;
  376. if (tracing) logger.trace("close",idstr + " closing.");
  377. if (communicatorAdmin != null) {
  378. communicatorAdmin.terminate();
  379. }
  380. if (rmiNotifClient != null) {
  381. try {
  382. rmiNotifClient.terminate();
  383. if (tracing) logger.trace("close",idstr +
  384. " RMI Notification client terminated.");
  385. } catch (RuntimeException x) {
  386. closeException = x;
  387. if (tracing) logger.trace("close",idstr +
  388. " Failed to terminate RMI Notification client: " + x);
  389. if (debug) logger.debug("close",x);
  390. }
  391. }
  392. if (connection != null) {
  393. try {
  394. connection.close();
  395. if (tracing) logger.trace("close",idstr + " closed.");
  396. } catch (NoSuchObjectException nse) {
  397. // OK, the server maybe closed itself.
  398. } catch (IOException e) {
  399. closeException = e;
  400. if (tracing) logger.trace("close",idstr +
  401. " Failed to close RMIServer: " + e);
  402. if (debug) logger.debug("close",e);
  403. }
  404. }
  405. // Clean up MBeanServerConnection table
  406. //
  407. rmbscMap.clear();
  408. /* Send notification of closure. We don't do this if the user
  409. * never called connect() on the connector, because there's no
  410. * connection id in that case. */
  411. if (savedConnectionId != null) {
  412. Notification closedNotif =
  413. new JMXConnectionNotification(JMXConnectionNotification.CLOSED,
  414. this,
  415. savedConnectionId,
  416. clientNotifID++,
  417. "Client has been closed",
  418. null);
  419. sendNotification(closedNotif);
  420. }
  421. // throw exception if needed
  422. //
  423. if (closeException != null) {
  424. if (tracing) logger.trace("close",idstr + " failed to close: " +
  425. closeException);
  426. if (closeException instanceof IOException)
  427. throw (IOException) closeException;
  428. if (closeException instanceof RuntimeException)
  429. throw (RuntimeException) closeException;
  430. final IOException x =
  431. new IOException("Failed to close: " + closeException);
  432. throw (IOException) EnvHelp.initCause(x,closeException);
  433. }
  434. }
  435. // added for re-connection
  436. private Integer addListenerWithSubject(ObjectName name,
  437. MarshalledObject filter,
  438. Subject delegationSubject,
  439. boolean reconnect)
  440. throws InstanceNotFoundException, IOException {
  441. final boolean debug = logger.debugOn();
  442. if (debug)
  443. logger.debug("addListenerWithSubject",
  444. "(ObjectName,MarshalledObject,Subject)");
  445. final ClassLoader old = pushDefaultClassLoader();
  446. final ObjectName[] names = new ObjectName[] {name};
  447. final MarshalledObject[] filters = new MarshalledObject[] {filter};
  448. final Subject[] delegationSubjects = new Subject[] {
  449. delegationSubject
  450. };
  451. final Integer[] listenerIDs =
  452. addListenersWithSubjects(names,filters,delegationSubjects,
  453. reconnect);
  454. if (debug) logger.debug("addListenerWithSubject","listenerID="
  455. + listenerIDs[0]);
  456. return listenerIDs[0];
  457. }
  458. // added for re-connection
  459. private Integer[] addListenersWithSubjects(ObjectName[] names,
  460. MarshalledObject[] filters,
  461. Subject[] delegationSubjects,
  462. boolean reconnect)
  463. throws InstanceNotFoundException, IOException {
  464. final boolean debug = logger.debugOn();
  465. if (debug)
  466. logger.debug("addListenersWithSubjects",
  467. "(ObjectName[],MarshalledObject[],Subject[])");
  468. final ClassLoader old = pushDefaultClassLoader();
  469. Integer[] listenerIDs = null;
  470. try {
  471. listenerIDs = connection.addNotificationListeners(names,
  472. filters,
  473. delegationSubjects);
  474. } catch (NoSuchObjectException noe) {
  475. // maybe reconnect
  476. if (reconnect) {
  477. communicatorAdmin.gotIOException(noe);
  478. listenerIDs = connection.addNotificationListeners(names,
  479. filters,
  480. delegationSubjects);
  481. } else {
  482. throw noe;
  483. }
  484. } catch (IOException ioe) {
  485. // send a failed notif if necessary
  486. communicatorAdmin.gotIOException(ioe);
  487. } finally {
  488. popDefaultClassLoader(old);
  489. }
  490. if (debug) logger.debug("addListenersWithSubjects","registered "
  491. + listenerIDs.length + " listener(s)");
  492. return listenerIDs;
  493. }
  494. //--------------------------------------------------------------------
  495. // Implementation of MBeanServerConnection
  496. //--------------------------------------------------------------------
  497. private class RemoteMBeanServerConnection
  498. implements MBeanServerConnection {
  499. private Subject delegationSubject;
  500. public RemoteMBeanServerConnection() {
  501. this(null);
  502. }
  503. public RemoteMBeanServerConnection(Subject delegationSubject) {
  504. this.delegationSubject = delegationSubject;
  505. }
  506. public ObjectInstance createMBean(String className,
  507. ObjectName name)
  508. throws ReflectionException,
  509. InstanceAlreadyExistsException,
  510. MBeanRegistrationException,
  511. MBeanException,
  512. NotCompliantMBeanException,
  513. IOException {
  514. if (logger.debugOn())
  515. logger.debug("createMBean(String,ObjectName)",
  516. "className=" + className + ", name=" +
  517. name);
  518. final ClassLoader old = pushDefaultClassLoader();
  519. try {
  520. return connection.createMBean(className,
  521. name,
  522. delegationSubject);
  523. } catch (IOException ioe) {
  524. communicatorAdmin.gotIOException(ioe);
  525. return connection.createMBean(className,
  526. name,
  527. delegationSubject);
  528. } finally {
  529. popDefaultClassLoader(old);
  530. }
  531. }
  532. public ObjectInstance createMBean(String className,
  533. ObjectName name,
  534. ObjectName loaderName)
  535. throws ReflectionException,
  536. InstanceAlreadyExistsException,
  537. MBeanRegistrationException,
  538. MBeanException,
  539. NotCompliantMBeanException,
  540. InstanceNotFoundException,
  541. IOException {
  542. if (logger.debugOn())
  543. logger.debug("createMBean(String,ObjectName,ObjectName)",
  544. "className=" + className + ", name="
  545. + name + ", loaderName="
  546. + loaderName + ")");
  547. final ClassLoader old = pushDefaultClassLoader();
  548. try {
  549. return connection.createMBean(className,
  550. name,
  551. loaderName,
  552. delegationSubject);
  553. } catch (IOException ioe) {
  554. communicatorAdmin.gotIOException(ioe);
  555. return connection.createMBean(className,
  556. name,
  557. loaderName,
  558. delegationSubject);
  559. } finally {
  560. popDefaultClassLoader(old);
  561. }
  562. }
  563. public ObjectInstance createMBean(String className,
  564. ObjectName name,
  565. Object params[],
  566. String signature[])
  567. throws ReflectionException,
  568. InstanceAlreadyExistsException,
  569. MBeanRegistrationException,
  570. MBeanException,
  571. NotCompliantMBeanException,
  572. IOException {
  573. if (logger.debugOn())
  574. logger.debug("createMBean(String,ObjectName,Object[],String[])",
  575. "className=" + className + ", name="
  576. + name + ", params="
  577. + objects(params) + ", signature="
  578. + strings(signature));
  579. final MarshalledObject sParams = new MarshalledObject(params);
  580. final ClassLoader old = pushDefaultClassLoader();
  581. try {
  582. return connection.createMBean(className,
  583. name,
  584. sParams,
  585. signature,
  586. delegationSubject);
  587. } catch (IOException ioe) {
  588. communicatorAdmin.gotIOException(ioe);
  589. return connection.createMBean(className,
  590. name,
  591. sParams,
  592. signature,
  593. delegationSubject);
  594. } finally {
  595. popDefaultClassLoader(old);
  596. }
  597. }
  598. public ObjectInstance createMBean(String className,
  599. ObjectName name,
  600. ObjectName loaderName,
  601. Object params[],
  602. String signature[])
  603. throws ReflectionException,
  604. InstanceAlreadyExistsException,
  605. MBeanRegistrationException,
  606. MBeanException,
  607. NotCompliantMBeanException,
  608. InstanceNotFoundException,
  609. IOException {
  610. if (logger.debugOn()) logger.debug(
  611. "createMBean(String,ObjectName,ObjectName,Object[],String[])",
  612. "className=" + className + ", name=" + name + ", loaderName="
  613. + loaderName + ", params=" + objects(params)
  614. + ", signature=" + strings(signature));
  615. final MarshalledObject sParams = new MarshalledObject(params);
  616. final ClassLoader old = pushDefaultClassLoader();
  617. try {
  618. return connection.createMBean(className,
  619. name,
  620. loaderName,
  621. sParams,
  622. signature,
  623. delegationSubject);
  624. } catch (IOException ioe) {
  625. communicatorAdmin.gotIOException(ioe);
  626. return connection.createMBean(className,
  627. name,
  628. loaderName,
  629. sParams,
  630. signature,
  631. delegationSubject);
  632. } finally {
  633. popDefaultClassLoader(old);
  634. }
  635. }
  636. public void unregisterMBean(ObjectName name)
  637. throws InstanceNotFoundException,
  638. MBeanRegistrationException,
  639. IOException {
  640. if (logger.debugOn())
  641. logger.debug("unregisterMBean", "name=" + name);
  642. final ClassLoader old = pushDefaultClassLoader();
  643. try {
  644. connection.unregisterMBean(name, delegationSubject);
  645. } catch (IOException ioe) {
  646. communicatorAdmin.gotIOException(ioe);
  647. connection.unregisterMBean(name, delegationSubject);
  648. } finally {
  649. popDefaultClassLoader(old);
  650. }
  651. }
  652. public ObjectInstance getObjectInstance(ObjectName name)
  653. throws InstanceNotFoundException,
  654. IOException {
  655. if (logger.debugOn())
  656. logger.debug("getObjectInstance", "name=" + name);
  657. final ClassLoader old = pushDefaultClassLoader();
  658. try {
  659. return connection.getObjectInstance(name, delegationSubject);
  660. } catch (IOException ioe) {
  661. communicatorAdmin.gotIOException(ioe);
  662. return connection.getObjectInstance(name, delegationSubject);
  663. } finally {
  664. popDefaultClassLoader(old);
  665. }
  666. }
  667. public Set queryMBeans(ObjectName name,
  668. QueryExp query)
  669. throws IOException {
  670. if (logger.debugOn()) logger.debug("queryMBeans",
  671. "name=" + name + ", query=" + query);
  672. final MarshalledObject sQuery = new MarshalledObject(query);
  673. final ClassLoader old = pushDefaultClassLoader();
  674. try {
  675. return connection.queryMBeans(name, sQuery, delegationSubject);
  676. } catch (IOException ioe) {
  677. communicatorAdmin.gotIOException(ioe);
  678. return connection.queryMBeans(name, sQuery, delegationSubject);
  679. } finally {
  680. popDefaultClassLoader(old);
  681. }
  682. }
  683. public Set queryNames(ObjectName name,
  684. QueryExp query)
  685. throws IOException {
  686. if (logger.debugOn()) logger.debug("queryNames",
  687. "name=" + name + ", query=" + query);
  688. final MarshalledObject sQuery = new MarshalledObject(query);
  689. final ClassLoader old = pushDefaultClassLoader();
  690. try {
  691. return connection.queryNames(name, sQuery, delegationSubject);
  692. } catch (IOException ioe) {
  693. communicatorAdmin.gotIOException(ioe);
  694. return connection.queryNames(name, sQuery, delegationSubject);
  695. } finally {
  696. popDefaultClassLoader(old);
  697. }
  698. }
  699. public boolean isRegistered(ObjectName name)
  700. throws IOException {
  701. if (logger.debugOn())
  702. logger.debug("isRegistered", "name=" + name);
  703. final ClassLoader old = pushDefaultClassLoader();
  704. try {
  705. return connection.isRegistered(name, delegationSubject);
  706. } catch (IOException ioe) {
  707. communicatorAdmin.gotIOException(ioe);
  708. return connection.isRegistered(name, delegationSubject);
  709. } finally {
  710. popDefaultClassLoader(old);
  711. }
  712. }
  713. public Integer getMBeanCount()
  714. throws IOException {
  715. if (logger.debugOn()) logger.debug("getMBeanCount", "");
  716. final ClassLoader old = pushDefaultClassLoader();
  717. try {
  718. return connection.getMBeanCount(delegationSubject);
  719. } catch (IOException ioe) {
  720. communicatorAdmin.gotIOException(ioe);
  721. return connection.getMBeanCount(delegationSubject);
  722. } finally {
  723. popDefaultClassLoader(old);
  724. }
  725. }
  726. public Object getAttribute(ObjectName name,
  727. String attribute)
  728. throws MBeanException,
  729. AttributeNotFoundException,
  730. InstanceNotFoundException,
  731. ReflectionException,
  732. IOException {
  733. if (logger.debugOn()) logger.debug("getAttribute",
  734. "name=" + name + ", attribute="
  735. + attribute);
  736. final ClassLoader old = pushDefaultClassLoader();
  737. try {
  738. return connection.getAttribute(name,
  739. attribute,
  740. delegationSubject);
  741. } catch (IOException ioe) {
  742. communicatorAdmin.gotIOException(ioe);
  743. return connection.getAttribute(name,
  744. attribute,
  745. delegationSubject);
  746. } finally {
  747. popDefaultClassLoader(old);
  748. }
  749. }
  750. public AttributeList getAttributes(ObjectName name,
  751. String[] attributes)
  752. throws InstanceNotFoundException,
  753. ReflectionException,
  754. IOException {
  755. if (logger.debugOn()) logger.debug("getAttributes",
  756. "name=" + name + ", attributes="
  757. + strings(attributes));
  758. final ClassLoader old = pushDefaultClassLoader();
  759. try {
  760. return connection.getAttributes(name,
  761. attributes,
  762. delegationSubject);
  763. } catch (IOException ioe) {
  764. communicatorAdmin.gotIOException(ioe);
  765. return connection.getAttributes(name,
  766. attributes,
  767. delegationSubject);
  768. } finally {
  769. popDefaultClassLoader(old);
  770. }
  771. }
  772. public void setAttribute(ObjectName name,
  773. Attribute attribute)
  774. throws InstanceNotFoundException,
  775. AttributeNotFoundException,
  776. InvalidAttributeValueException,
  777. MBeanException,
  778. ReflectionException,
  779. IOException {
  780. if (logger.debugOn()) logger.debug("setAttribute",
  781. "name=" + name + ", attribute="
  782. + attribute);
  783. final MarshalledObject sAttribute =
  784. new MarshalledObject(attribute);
  785. final ClassLoader old = pushDefaultClassLoader();
  786. try {
  787. connection.setAttribute(name, sAttribute, delegationSubject);
  788. } catch (IOException ioe) {
  789. communicatorAdmin.gotIOException(ioe);
  790. connection.setAttribute(name, sAttribute, delegationSubject);
  791. } finally {
  792. popDefaultClassLoader(old);
  793. }
  794. }
  795. public AttributeList setAttributes(ObjectName name,
  796. AttributeList attributes)
  797. throws InstanceNotFoundException,
  798. ReflectionException,
  799. IOException {
  800. if (logger.debugOn()) logger.debug("setAttributes",
  801. "name=" + name + ", attributes="
  802. + attributes);
  803. final MarshalledObject sAttributes =
  804. new MarshalledObject(attributes);
  805. final ClassLoader old = pushDefaultClassLoader();
  806. try {
  807. return connection.setAttributes(name,
  808. sAttributes,
  809. delegationSubject);
  810. } catch (IOException ioe) {
  811. communicatorAdmin.gotIOException(ioe);
  812. return connection.setAttributes(name,
  813. sAttributes,
  814. delegationSubject);
  815. } finally {
  816. popDefaultClassLoader(old);
  817. }
  818. }
  819. public Object invoke(ObjectName name,
  820. String operationName,
  821. Object params[],
  822. String signature[])
  823. throws InstanceNotFoundException,
  824. MBeanException,
  825. ReflectionException,
  826. IOException {
  827. if (logger.debugOn()) logger.debug("invoke",
  828. "name=" + name
  829. + ", operationName=" + operationName
  830. + ", params=" + objects(params)
  831. + ", signature=" + strings(signature));
  832. final MarshalledObject sParams = new MarshalledObject(params);
  833. final ClassLoader old = pushDefaultClassLoader();
  834. try {
  835. return connection.invoke(name,
  836. operationName,
  837. sParams,
  838. signature,
  839. delegationSubject);
  840. } catch (IOException ioe) {
  841. communicatorAdmin.gotIOException(ioe);
  842. return connection.invoke(name,
  843. operationName,
  844. sParams,
  845. signature,
  846. delegationSubject);
  847. } finally {
  848. popDefaultClassLoader(old);
  849. }
  850. }
  851. public String getDefaultDomain()
  852. throws IOException {
  853. if (logger.debugOn()) logger.debug("getDefaultDomain", "");
  854. final ClassLoader old = pushDefaultClassLoader();
  855. try {
  856. return connection.getDefaultDomain(delegationSubject);
  857. } catch (IOException ioe) {
  858. communicatorAdmin.gotIOException(ioe);
  859. return connection.getDefaultDomain(delegationSubject);
  860. } finally {
  861. popDefaultClassLoader(old);
  862. }
  863. }
  864. public String[] getDomains() throws IOException {
  865. if (logger.debugOn()) logger.debug("getDomains", "");
  866. final ClassLoader old = pushDefaultClassLoader();
  867. try {
  868. return connection.getDomains(delegationSubject);
  869. } catch (IOException ioe) {
  870. communicatorAdmin.gotIOException(ioe);
  871. return connection.getDomains(delegationSubject);
  872. } finally {
  873. popDefaultClassLoader(old);
  874. }
  875. }
  876. public MBeanInfo getMBeanInfo(ObjectName name)
  877. throws InstanceNotFoundException,
  878. IntrospectionException,
  879. ReflectionException,
  880. IOException {
  881. if (logger.debugOn()) logger.debug("getMBeanInfo", "name=" + name);
  882. final ClassLoader old = pushDefaultClassLoader();
  883. try {
  884. return connection.getMBeanInfo(name, delegationSubject);
  885. } catch (IOException ioe) {
  886. communicatorAdmin.gotIOException(ioe);
  887. return connection.getMBeanInfo(name, delegationSubject);
  888. } finally {
  889. popDefaultClassLoader(old);
  890. }
  891. }
  892. public boolean isInstanceOf(ObjectName name,
  893. String className)
  894. throws InstanceNotFoundException,
  895. IOException {
  896. if (logger.debugOn())
  897. logger.debug("isInstanceOf", "name=" + name +
  898. ", className=" + className);
  899. final ClassLoader old = pushDefaultClassLoader();
  900. try {
  901. return connection.isInstanceOf(name,
  902. className,
  903. delegationSubject);
  904. } catch (IOException ioe) {
  905. communicatorAdmin.gotIOException(ioe);
  906. return connection.isInstanceOf(name,
  907. className,
  908. delegationSubject);
  909. } finally {
  910. popDefaultClassLoader(old);
  911. }
  912. }
  913. public void addNotificationListener(ObjectName name,
  914. ObjectName listener,
  915. NotificationFilter filter,
  916. Object handback)
  917. throws InstanceNotFoundException,
  918. IOException {
  919. if (logger.debugOn())
  920. logger.debug("addNotificationListener" +
  921. "(ObjectName,ObjectName,NotificationFilter,Object)",
  922. "name=" + name + ", listener=" + listener
  923. + ", filter=" + filter + ", handback=" + handback);
  924. final MarshalledObject sFilter = new MarshalledObject(filter);
  925. final MarshalledObject sHandback = new MarshalledObject(handback);
  926. final ClassLoader old = pushDefaultClassLoader();
  927. try {
  928. connection.addNotificationListener(name,
  929. listener,
  930. sFilter,
  931. sHandback,
  932. delegationSubject);
  933. } catch (IOException ioe) {
  934. communicatorAdmin.gotIOException(ioe);
  935. connection.addNotificationListener(name,
  936. listener,
  937. sFilter,
  938. sHandback,
  939. delegationSubject);
  940. } finally {
  941. popDefaultClassLoader(old);
  942. }
  943. }
  944. public void removeNotificationListener(ObjectName name,
  945. ObjectName listener)
  946. throws InstanceNotFoundException,
  947. ListenerNotFoundException,
  948. IOException {
  949. if (logger.debugOn()) logger.debug("removeNotificationListener" +
  950. "(ObjectName,ObjectName)",
  951. "name=" + name
  952. + ", listener=" + listener);
  953. final ClassLoader old = pushDefaultClassLoader();
  954. try {
  955. connection.removeNotificationListener(name,
  956. listener,
  957. delegationSubject);
  958. } catch (IOException ioe) {
  959. communicatorAdmin.gotIOException(ioe);
  960. connection.removeNotificationListener(name,
  961. listener,
  962. delegationSubject);
  963. } finally {
  964. popDefaultClassLoader(old);
  965. }
  966. }
  967. public void removeNotificationListener(ObjectName name,
  968. ObjectName listener,
  969. NotificationFilter filter,
  970. Object handback)
  971. throws InstanceNotFoundException,
  972. ListenerNotFoundException,
  973. IOException {
  974. if (logger.debugOn())
  975. logger.debug("removeNotificationListener" +
  976. "(ObjectName,ObjectName,NotificationFilter,Object)",
  977. "name=" + name
  978. + ", listener=" + listener
  979. + ", filter=" + filter
  980. + ", handback=" + handback);
  981. final MarshalledObject sFilter = new MarshalledObject(filter);
  982. final MarshalledObject sHandback = new MarshalledObject(handback);
  983. final ClassLoader old = pushDefaultClassLoader();
  984. try {
  985. connection.removeNotificationListener(name,
  986. listener,
  987. sFilter,
  988. sHandback,
  989. delegationSubject);
  990. } catch (IOException ioe) {
  991. communicatorAdmin.gotIOException(ioe);
  992. connection.removeNotificationListener(name,
  993. listener,
  994. sFilter,
  995. sHandback,
  996. delegationSubject);
  997. } finally {
  998. popDefaultClassLoader(old);
  999. }
  1000. }
  1001. // Specific Notification Handle ----------------------------------
  1002. public void addNotificationListener(ObjectName name,
  1003. NotificationListener listener,
  1004. NotificationFilter filter,
  1005. Object handback)
  1006. throws InstanceNotFoundException,
  1007. IOException {
  1008. final boolean debug = logger.debugOn();
  1009. if (debug)
  1010. logger.debug("addNotificationListener" +
  1011. "(ObjectName,NotificationListener,"+
  1012. "NotificationFilter,Object)",
  1013. "name=" + name
  1014. + ", listener=" + listener
  1015. + ", filter=" + filter
  1016. + ", handback=" + handback);
  1017. final Integer listenerID =
  1018. addListenerWithSubject(name, new MarshalledObject(filter),
  1019. delegationSubject,true);
  1020. rmiNotifClient.addNotificationListener(listenerID, name, listener,
  1021. filter, handback,
  1022. delegationSubject);
  1023. }
  1024. public void removeNotificationListener(ObjectName name,
  1025. NotificationListener listener)
  1026. throws InstanceNotFoundException,
  1027. ListenerNotFoundException,
  1028. IOException {
  1029. final boolean debug = logger.debugOn();
  1030. if (debug) logger.debug("removeNotificationListener"+
  1031. "(ObjectName,NotificationListener)",
  1032. "name=" + name
  1033. + ", listener=" + listener);
  1034. final Integer[] ret =
  1035. rmiNotifClient.removeNotificationListener(name, listener);
  1036. if (debug) logger.debug("removeNotificationListener",
  1037. "listenerIDs=" + objects(ret));
  1038. final ClassLoader old = pushDefaultClassLoader();
  1039. try {
  1040. connection.removeNotificationListeners(name,
  1041. ret,
  1042. delegationSubject);
  1043. } catch (IOException ioe) {
  1044. communicatorAdmin.gotIOException(ioe);
  1045. connection.removeNotificationListeners(name,
  1046. ret,
  1047. delegationSubject);
  1048. } finally {
  1049. popDefaultClassLoader(old);
  1050. }
  1051. }
  1052. public void removeNotificationListener(ObjectName name,
  1053. NotificationListener listener,
  1054. NotificationFilter filter,
  1055. Object handback)
  1056. throws InstanceNotFoundException,
  1057. ListenerNotFoundException,
  1058. IOException {
  1059. final boolean debug = logger.debugOn();
  1060. if (debug)
  1061. logger.debug("removeNotificationListener"+
  1062. "(ObjectName,NotificationListener,"+
  1063. "NotificationFilter,Object)",
  1064. "name=" + name
  1065. + ", listener=" + listener
  1066. + ", filter=" + filter
  1067. + ", handback=" + handback);
  1068. final Integer ret =
  1069. rmiNotifClient.removeNotificationListener(name, listener,
  1070. filter, handback);
  1071. if (debug) logger.debug("removeNotificationListener",
  1072. "listenerID=" + ret);
  1073. final ClassLoader old = pushDefaultClassLoader();
  1074. try {
  1075. connection.removeNotificationListeners(name,
  1076. new Integer[] {ret},
  1077. delegationSubject);
  1078. } catch (IOException ioe) {
  1079. communicatorAdmin.gotIOException(ioe);
  1080. connection.removeNotificationListeners(name,
  1081. new Integer[] {ret},
  1082. delegationSubject);
  1083. } finally {
  1084. popDefaultClassLoader(old);
  1085. }
  1086. }
  1087. }
  1088. //--------------------------------------------------------------------
  1089. private class RMINotifClient extends ClientNotifForwarder {
  1090. public RMINotifClient(ClassLoader cl, Map env) {
  1091. super(cl, env);
  1092. }
  1093. protected NotificationResult fetchNotifs(long clientSequenceNumber,
  1094. int maxNotifications,
  1095. long timeout)
  1096. throws IOException, ClassNotFoundException {
  1097. IOException org;
  1098. while (true) { // used for a successful re-connection
  1099. try {
  1100. return connection.fetchNotifications(clientSequenceNumber,
  1101. maxNotifications,
  1102. timeout);
  1103. } catch (IOException ioe) {
  1104. org = ioe;
  1105. // inform of IOException
  1106. try {
  1107. communicatorAdmin.gotIOException(ioe);
  1108. // The connection should be re-established.
  1109. continue;
  1110. } catch (IOException ee) {
  1111. // No more fetch, the Exception will be re-thrown.
  1112. break;
  1113. } // never reached
  1114. } // never reached
  1115. }
  1116. // specially treating for an UnmarshalException
  1117. if (org instanceof UnmarshalException) {
  1118. UnmarshalException ume = (UnmarshalException)org;
  1119. if (ume.detail instanceof ClassNotFoundException)
  1120. throw (ClassNotFoundException) ume.detail;
  1121. /* In Sun's RMI implementation, if a method return
  1122. contains an unserializable object, then we get
  1123. UnmarshalException wrapping WriteAbortedException
  1124. wrapping NotSerializableException. In that case we
  1125. extract the NotSerializableException so that our
  1126. caller can realize it should try to skip past the
  1127. notification that presumably caused it. It's not
  1128. certain that every other RMI implementation will
  1129. generate this exact exception sequence. If not, we
  1130. will not detect that the problem is due to an
  1131. unserializable object, and we will stop trying to
  1132. receive notifications from the server. It's not
  1133. clear we can do much better. */
  1134. if (ume.detail instanceof WriteAbortedException) {
  1135. WriteAbortedException wae =
  1136. (WriteAbortedException) ume.detail;
  1137. if (wae.detail instanceof IOException)
  1138. throw (IOException) wae.detail;
  1139. }
  1140. } else if (org instanceof MarshalException) {
  1141. // IIOP will throw MarshalException wrapping a NotSerializableException
  1142. // when a server fails to serialize a response.
  1143. MarshalException me = (MarshalException)org;
  1144. if (me.detail instanceof NotSerializableException) {
  1145. throw (NotSerializableException)me.detail;
  1146. }
  1147. }
  1148. // Not serialization problem, simply re-throw the orginal exception
  1149. throw org;
  1150. }
  1151. protected Integer addListenerForMBeanRemovedNotif()
  1152. throws IOException, InstanceNotFoundException {
  1153. MarshalledObject sFilter = null;
  1154. NotificationFilterSupport clientFilter =
  1155. new NotificationFilterSupport();
  1156. clientFilter.enableType(
  1157. MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
  1158. sFilter = new MarshalledObject(clientFilter);
  1159. Integer[] listenerIDs;
  1160. final ObjectName[] names = new ObjectName[] {delegateName};
  1161. final MarshalledObject[] filters =
  1162. new MarshalledObject[] {sFilter};
  1163. final Subject[] subjects = new Subject[] {null};
  1164. try {
  1165. listenerIDs =
  1166. connection.addNotificationListeners(names,
  1167. filters,
  1168. subjects);
  1169. } catch (IOException ioe) {
  1170. communicatorAdmin.gotIOException(ioe);
  1171. listenerIDs =
  1172. connection.addNotificationListeners(names,
  1173. filters,
  1174. subjects);
  1175. }
  1176. return listenerIDs[0];
  1177. }
  1178. protected void removeListenerForMBeanRemovedNotif(Integer id)
  1179. throws IOException, InstanceNotFoundException,
  1180. ListenerNotFoundException {
  1181. try {
  1182. connection.removeNotificationListeners(delegateName,
  1183. new Integer[] {id},
  1184. null);
  1185. } catch (IOException ioe) {
  1186. communicatorAdmin.gotIOException(ioe);
  1187. connection.removeNotificationListeners(delegateName,
  1188. new Integer[] {id},
  1189. null);
  1190. }
  1191. }
  1192. protected void lostNotifs(String message, long number) {
  1193. final String notifType = JMXConnectionNotification.NOTIFS_LOST;
  1194. final JMXConnectionNotification n =
  1195. new JMXConnectionNotification(notifType,
  1196. RMIConnector.this,
  1197. connectionId,
  1198. clientNotifCounter++,
  1199. message,
  1200. new Long(number));
  1201. sendNotification(n);
  1202. }
  1203. }
  1204. private class RMIClientCommunicatorAdmin extends ClientCommunicatorAdmin {
  1205. public RMIClientCommunicatorAdmin(long period) {
  1206. super(period);
  1207. }
  1208. public void gotIOException (IOException ioe) throws IOException {
  1209. if (ioe instanceof NoSuchObjectException) {
  1210. // need to restart
  1211. super.gotIOException(ioe);
  1212. return;
  1213. }
  1214. // check if the connection is broken
  1215. try {
  1216. connection.getDefaultDomain(null);
  1217. } catch (IOException ioexc) {
  1218. synchronized(terminationLock) {
  1219. if (!terminated) {
  1220. // we should close the connection,
  1221. // but send a failed notif at first
  1222. Notification failedNotif =
  1223. new JMXConnectionNotification(
  1224. JMXConnectionNotification.FAILED,
  1225. this,
  1226. connectionId,
  1227. clientNotifID++,
  1228. "Failed to communicate with the server: "+ioe.toString(),
  1229. ioe);
  1230. sendNotification(failedNotif);
  1231. try {
  1232. close();
  1233. } catch (Exception e) {
  1234. // OK.
  1235. // We are closing
  1236. }
  1237. }
  1238. }
  1239. }
  1240. // forward the exception
  1241. if (ioe instanceof ServerException) {
  1242. /* Need to unwrap the exception.
  1243. Some user-thrown exception at server side will be wrapped by
  1244. rmi into a ServerException.
  1245. For example, a RMIConnnectorServer will wrap a
  1246. ClassNotFoundException into a UnmarshalException, and rmi
  1247. will throw a ServerException at client side which wraps this
  1248. UnmarshalException.
  1249. No failed notif here.
  1250. */
  1251. Throwable tt = ((ServerException)ioe).detail;
  1252. if (tt instanceof IOException) {
  1253. throw (IOException)tt;
  1254. } else if (tt instanceof RuntimeException) {
  1255. throw (RuntimeException)tt;
  1256. }
  1257. }
  1258. throw ioe;
  1259. }
  1260. public void reconnectNotificationListeners(ClientListenerInfo[] old) throws IOException {
  1261. final int len = old.length;
  1262. int i;
  1263. ClientListenerInfo[] clis = new ClientListenerInfo[len];
  1264. final Subject[] subjects = new Subject[len];
  1265. final ObjectName[] names = new ObjectName[len];
  1266. final NotificationListener[] listeners = new NotificationListener[len];
  1267. final NotificationFilter[] filters = new NotificationFilter[len];
  1268. final MarshalledObject[] mFilters = new MarshalledObject[len];
  1269. final Object[] handbacks = new Object[len];
  1270. for (i=0;i<len;i++) {
  1271. subjects[i] = old[i].getDelegationSubject();
  1272. names[i] = old[i].getObjectName();
  1273. listeners[i] = old[i].getListener();
  1274. filters[i] = old[i].getNotificationFilter();
  1275. mFilters[i] = new MarshalledObject(filters[i]);
  1276. handbacks[i] = old[i].getHandback();
  1277. }
  1278. try {
  1279. Integer[] ids = addListenersWithSubjects(names,mFilters,subjects,false);
  1280. for (i=0;i<len;i++) {
  1281. clis[i] = new ClientListenerInfo(ids[i],
  1282. names[i],
  1283. listeners[i],
  1284. filters[i],
  1285. handbacks[i],
  1286. subjects[i]);
  1287. }
  1288. rmiNotifClient.postReconnection(clis);
  1289. return;
  1290. } catch (InstanceNotFoundException infe) {
  1291. // OK, we will do one by one
  1292. }
  1293. int j = 0;
  1294. for (i=0;i<len;i++) {
  1295. try {
  1296. Integer id = addListenerWithSubject(names[i],
  1297. new MarshalledObject(filters[i]),
  1298. subjects[i],
  1299. false);
  1300. clis[j++] = new ClientListenerInfo(id,
  1301. names[i],
  1302. listeners[i],
  1303. filters[i],
  1304. handbacks[i],
  1305. subjects[i]);
  1306. } catch (InstanceNotFoundException infe) {
  1307. logger.warning("reconnectNotificationListeners",
  1308. "Can't reconnect listener for " +
  1309. names[i]);
  1310. }
  1311. }
  1312. if (j != len) {
  1313. ClientListenerInfo[] tmp = clis;
  1314. clis = new ClientListenerInfo[j];
  1315. System.arraycopy(tmp, 0, clis, 0, j);
  1316. }
  1317. rmiNotifClient.postReconnection(clis);
  1318. }
  1319. protected void checkConnection() throws IOException {
  1320. if (logger.debugOn())
  1321. logger.debug("RMIClientCommunicatorAdmin-checkConnection",
  1322. "Calling the method getDefaultDomain.");
  1323. connection.getDefaultDomain(null);
  1324. }
  1325. protected void doStart() throws IOException {
  1326. // Get RMIServer stub from directory or URL encoding if needed.
  1327. RMIServer stub = null;
  1328. try {
  1329. stub = (rmiServer!=null)?rmiServer:
  1330. findRMIServer(jmxServiceURL, env);
  1331. } catch (NamingException ne) {
  1332. throw new IOException("Failed to get a RMI stub: "+ne);
  1333. }
  1334. // Connect IIOP Stub if needed.
  1335. stub = connectStub(stub,env);
  1336. // Calling newClient on the RMIServer stub.
  1337. Object credentials = env.get(CREDENTIALS);
  1338. connection = stub.newClient(credentials);
  1339. // notif issues
  1340. final ClientListenerInfo[] old = rmiNotifClient.preReconnection();
  1341. reconnectNotificationListeners(old);
  1342. connectionId = getConnectionId();
  1343. Notification reconnectedNotif =
  1344. new JMXConnectionNotification(JMXConnectionNotification.OPENED,
  1345. this,
  1346. connectionId,
  1347. clientNotifID++,
  1348. "Reconnected to server",
  1349. null);
  1350. sendNotification(reconnectedNotif);
  1351. }
  1352. protected void doStop() {
  1353. try {
  1354. close();
  1355. } catch (IOException ioe) {
  1356. logger.warning("RMIClientCommunicatorAdmin-doStop",
  1357. "Failed to call the method close():" + ioe);
  1358. logger.debug("RMIClientCommunicatorAdmin-doStop",ioe);
  1359. }
  1360. }
  1361. }
  1362. //--------------------------------------------------------------------
  1363. // Private stuff - Serialization
  1364. //--------------------------------------------------------------------
  1365. /**
  1366. * <p>In order to be usable, an IIOP stub must be connected to an ORB.
  1367. * The stub is automagically connected to the ORB if:
  1368. * <ul>
  1369. * <li> It was returned by the COS naming</li>
  1370. * <li> Its server counterpart has been registered in COS naming
  1371. * through JNDI.</li>
  1372. * </ul>
  1373. * Otherwise, it is not connected. A stub which is deserialized
  1374. * from Jini is not connected. A stub which is obtained from a
  1375. * non registered RMIIIOPServerImpl is not a connected.<br>
  1376. * A stub which is not connected can't be serialized, and thus
  1377. * can't be registered in Jini. A stub which is not connected can't
  1378. * be used to invoke methods on the server.
  1379. * <p>
  1380. * In order to palliate this, this method will connect the
  1381. * given stub if it is not yet connected. If the given
  1382. * <var>RMIServer</var> is not an instance of
  1383. * {@link javax.rmi.CORBA.Stub javax.rmi.CORBA.Stub}, then the
  1384. * method do nothing and simply returns that stub. Otherwise,
  1385. * this method will attempt to connect the stub to an ORB as
  1386. * follows:
  1387. * <ul>
  1388. * <p>This method looks in the provided <var>environment</var> for
  1389. * the "java.naming.corba.orb" property. If it is found, the
  1390. * referenced object (an {@link org.omg.CORBA.ORB ORB}) is used to
  1391. * connect the stub. Otherwise, a new org.omg.CORBA.ORB is created
  1392. * by calling {@link
  1393. * org.omg.CORBA.ORB#init(String[], Properties)
  1394. * org.omg.CORBA.ORB.init((String[])null,(Properties)null)}
  1395. * <p>The new created ORB is kept in a static
  1396. * {@link WeakReference} and can be reused for connecting other
  1397. * stubs. However, no reference is ever kept on the ORB provided
  1398. * in the <var>environment</var> map, if any.
  1399. * </ul>
  1400. * @param rmiServer A RMI Server Stub.
  1401. * @param environment An environment map, possibly containing an ORB.
  1402. * @return the given stub.
  1403. * @exception IllegalArgumentException if the
  1404. * <tt>java.naming.corba.orb</tt> property is specified and
  1405. * does not point to an {@link org.omg.CORBA.ORB ORB}.
  1406. * @exception IOException if the connection to the ORB failed.
  1407. **/
  1408. static RMIServer connectStub(RMIServer rmiServer,
  1409. Map environment)
  1410. throws IOException {
  1411. if (rmiServer instanceof javax.rmi.CORBA.Stub) {
  1412. javax.rmi.CORBA.Stub stub = (javax.rmi.CORBA.Stub) rmiServer;
  1413. try {
  1414. stub._orb();
  1415. } catch (org.omg.CORBA.BAD_OPERATION x) {
  1416. stub.connect(resolveOrb(environment));
  1417. }
  1418. }
  1419. return rmiServer;
  1420. }
  1421. /**
  1422. * Get the ORB specified by <var>environment</var>, or create a
  1423. * new one.
  1424. * <p>This method looks in the provided <var>environment</var> for
  1425. * the "java.naming.corba.orb" property. If it is found, the
  1426. * referenced object (an {@link org.omg.CORBA.ORB ORB}) is
  1427. * returned. Otherwise, a new org.omg.CORBA.ORB is created
  1428. * by calling {@link
  1429. * org.omg.CORBA.ORB#init(String[], java.util.Properties)
  1430. * org.omg.CORBA.ORB.init((String[])null,(Properties)null)}
  1431. * <p>The new created ORB is kept in a static
  1432. * {@link WeakReference} and can be reused for connecting other
  1433. * stubs. However, no reference is ever kept on the ORB provided
  1434. * in the <var>environment</var> map, if any.
  1435. * @param environment An environment map, possibly containing an ORB.
  1436. * @return An ORB.
  1437. * @exception IllegalArgumentException if the
  1438. * <tt>java.naming.corba.orb</tt> property is specified and
  1439. * does not point to an {@link org.omg.CORBA.ORB ORB}.
  1440. * @exception IOException if the ORB initialization failed.
  1441. **/
  1442. static org.omg.CORBA.ORB resolveOrb(Map environment)
  1443. throws IOException {
  1444. if (environment != null) {
  1445. final Object orb = environment.get(EnvHelp.DEFAULT_ORB);
  1446. if (orb != null && !(orb instanceof org.omg.CORBA.ORB))
  1447. throw new IllegalArgumentException(EnvHelp.DEFAULT_ORB +
  1448. " must be an instance of org.omg.CORBA.ORB.");
  1449. if (orb != null) return (org.omg.CORBA.ORB)orb;
  1450. }
  1451. final Object orb =
  1452. (RMIConnector.orb==null)?null:RMIConnector.orb.get();
  1453. if (orb != null) return (org.omg.CORBA.ORB)orb;
  1454. final org.omg.CORBA.ORB newOrb =
  1455. org.omg.CORBA.ORB.init((String[])null, (Properties)null);
  1456. RMIConnector.orb = new WeakReference(newOrb);
  1457. return newOrb;
  1458. }
  1459. /**
  1460. * Read RMIConnector fields from an {@link java.io.ObjectInputStream
  1461. * ObjectInputStream}.
  1462. * Calls <code>s.defaultReadObject()</code> and then initializes
  1463. * all transient variables that need initializing.
  1464. * @param s The ObjectInputStream to read from.
  1465. * @exception InvalidObjectException if none of <var>rmiServer</var> stub
  1466. * or <var>jmxServiceURL</var> are set.
  1467. * @see #RMIConnector(JMXServiceURL,Map)
  1468. * @see #RMIConnector(RMIServer,Map)
  1469. **/
  1470. private void readObject(java.io.ObjectInputStream s)
  1471. throws IOException, ClassNotFoundException {
  1472. s.defaultReadObject();
  1473. if (rmiServer == null && jmxServiceURL == null) throw new
  1474. InvalidObjectException("rmiServer and jmxServiceURL both null");
  1475. initTransients();
  1476. }
  1477. /**
  1478. * Writes the RMIConnector fields to an {@link java.io.ObjectOutputStream
  1479. * ObjectOutputStream}.
  1480. * <p>Connects the underlying RMIServer stub to an ORB, if needed,
  1481. * before serializing it. This is done using the environment
  1482. * map that was provided to the constructor, if any, and as documented
  1483. * in {@link javax.management.remote.rmi}.</p>
  1484. * <p>This method then calls <code>s.defaultWriteObject()</code>.
  1485. * Usually, <var>rmiServer</var> is null if this object
  1486. * was constructed with a JMXServiceURL, and <var>jmxServiceURL</var>
  1487. * is null if this object is constructed with a RMIServer stub.
  1488. * <p>Note that the environment Map is not serialized, since the objects
  1489. * it contains are assumed to be contextual and relevant only
  1490. * with respect to the local environment (class loader, ORB, etc...).</p>
  1491. * <p>After an RMIConnector is deserialized, it is assumed that the
  1492. * user will call {@link #connect(Map)}, providing a new Map that
  1493. * can contain values which are contextually relevant to the new
  1494. * local environment.</p>
  1495. * <p>Since connection to the ORB is needed prior to serializing, and
  1496. * since the ORB to connect to is one of those contextual parameters,
  1497. * it is not recommended to re-serialize a just de-serialized object -
  1498. * as the de-serialized object has no map. Thus, when an RMIConnector
  1499. * object is needed for serialization or transmission to a remote
  1500. * application, it is recommended to obtain a new RMIConnector stub
  1501. * by calling {@link RMIConnectorServer#toJMXConnector(Map)}.</p>
  1502. * @param s The ObjectOutputStream to write to.
  1503. * @exception InvalidObjectException if none of <var>rmiServer</var> stub
  1504. * or <var>jmxServiceURL</var> are set.
  1505. * @see #RMIConnector(JMXServiceURL,Map)
  1506. * @see #RMIConnector(RMIServer,Map)
  1507. **/
  1508. private void writeObject(java.io.ObjectOutputStream s)
  1509. throws IOException {
  1510. if (rmiServer == null && jmxServiceURL == null) throw new
  1511. InvalidObjectException("rmiServer and jmxServiceURL both null.");
  1512. connectStub(this.rmiServer,env);
  1513. s.defaultWriteObject();
  1514. }
  1515. // Initialization of transient variables.
  1516. private void initTransients() {
  1517. rmbscMap = new WeakHashMap();
  1518. connected = false;
  1519. terminated = false;
  1520. terminationLock = new int[0];
  1521. connectionBroadcaster = new NotificationBroadcasterSupport();
  1522. }
  1523. //--------------------------------------------------------------------
  1524. // Private stuff - RMIServer creation
  1525. //--------------------------------------------------------------------
  1526. private RMIServer findRMIServer(JMXServiceURL directoryURL,
  1527. Map environment)
  1528. throws NamingException, IOException {
  1529. final boolean isIiop = RMIConnectorServer.isIiopURL(directoryURL,true);
  1530. if (isIiop) {
  1531. // Make sure java.naming.corba.orb is in the Map.
  1532. environment.put(EnvHelp.DEFAULT_ORB,resolveOrb(environment));
  1533. }
  1534. String path = directoryURL.getURLPath();
  1535. if (path.startsWith("/jndi/"))
  1536. return findRMIServerJNDI(path.substring(6), environment, isIiop);
  1537. else if (path.startsWith("/stub/"))
  1538. return findRMIServerJRMP(path.substring(6), environment, isIiop);
  1539. else if (path.startsWith("/ior/"))
  1540. return findRMIServerIIOP(path.substring(5), environment, isIiop);
  1541. else {
  1542. final String msg = "URL path must begin with /jndi/ or /stub/ " +
  1543. "or /ior/: " + path;
  1544. throw new MalformedURLException(msg);
  1545. }
  1546. }
  1547. /**
  1548. * Lookup the RMIServer stub in a directory.
  1549. * @param jndiURL A JNDI URL indicating the location of the Stub
  1550. * (see {@link javax.management.remote.rmi}), e.g.:
  1551. * <ul><li><tt>rmi://registry-host:port/rmi-stub-name</tt></li>
  1552. * <li>or <tt>iiop://cosnaming-host:port/iiop-stub-name</tt></li>
  1553. * <li>or <tt>ldap://ldap-host:port/java-container-dn</tt></li>
  1554. * </ul>
  1555. * @param env the environment Map passed to the connector.
  1556. * @param isIiop true if the stub is expected to be an IIOP stub.
  1557. * @return The retrieved RMIServer stub.
  1558. * @exception NamingException if the stub couldn't be found.
  1559. **/
  1560. private RMIServer findRMIServerJNDI(String jndiURL, Map env, boolean isIiop)
  1561. throws NamingException {
  1562. InitialContext ctx = new InitialContext(EnvHelp.mapToHashtable(env));
  1563. Object objref = ctx.lookup(jndiURL);
  1564. ctx.close();
  1565. if (isIiop)
  1566. return narrowIIOPServer(objref);
  1567. else
  1568. return narrowJRMPServer(objref);
  1569. }
  1570. private static RMIServer narrowJRMPServer(Object objref) {
  1571. return (RMIServer) objref;
  1572. }
  1573. private static RMIServer narrowIIOPServer(Object objref) {
  1574. try {
  1575. return (RMIServer)
  1576. PortableRemoteObject.narrow(objref, RMIServer.class);
  1577. } catch (ClassCastException e) {
  1578. if (logger.traceOn())
  1579. logger.trace("narrowIIOPServer","Failed to narrow objref=" +
  1580. objref + ": " + e);
  1581. if (logger.debugOn()) logger.debug("narrowIIOPServer",e);
  1582. return null;
  1583. }
  1584. }
  1585. private RMIServer findRMIServerIIOP(String ior, Map env, boolean isIiop) {
  1586. // could forbid "rmi:" URL here -- but do we need to?
  1587. final org.omg.CORBA.ORB orb = (org.omg.CORBA.ORB)
  1588. env.get(EnvHelp.DEFAULT_ORB);
  1589. final Object stub = orb.string_to_object(ior);
  1590. return (RMIServer) PortableRemoteObject.narrow(stub, RMIServer.class);
  1591. }
  1592. private RMIServer findRMIServerJRMP(String base64, Map env, boolean isIiop)
  1593. throws IOException {
  1594. // could forbid "iiop:" URL here -- but do we need to?
  1595. final byte[] serialized;
  1596. try {
  1597. serialized = base64ToByteArray(base64);
  1598. } catch (IllegalArgumentException e) {
  1599. throw new MalformedURLException("Bad BASE64 encoding: " +
  1600. e.getMessage());
  1601. }
  1602. final ByteArrayInputStream bin = new ByteArrayInputStream(serialized);
  1603. final ClassLoader loader = EnvHelp.resolveClientClassLoader(env);
  1604. final ObjectInputStream oin =
  1605. (loader == null) ?
  1606. new ObjectInputStream(bin) :
  1607. new ObjectInputStreamWithLoader(bin, loader);
  1608. final Object stub;
  1609. try {
  1610. stub = oin.readObject();
  1611. } catch (ClassNotFoundException e) {
  1612. throw new MalformedURLException("Class not found: " + e);
  1613. }
  1614. return (RMIServer) PortableRemoteObject.narrow(stub, RMIServer.class);
  1615. }
  1616. private static final class ObjectInputStreamWithLoader
  1617. extends ObjectInputStream {
  1618. ObjectInputStreamWithLoader(InputStream in, ClassLoader cl)
  1619. throws IOException {
  1620. super(in);
  1621. this.loader = cl;
  1622. }
  1623. protected Class resolveClass(ObjectStreamClass classDesc)
  1624. throws IOException, ClassNotFoundException {
  1625. return Class.forName(classDesc.getName(), false, loader);
  1626. }
  1627. private final ClassLoader loader;
  1628. }
  1629. /*
  1630. The following section of code avoids a class loading problem
  1631. with RMI. The problem is that an RMI stub, when deserializing
  1632. a remote method return value or exception, will first of all
  1633. consult the first non-bootstrap class loader it finds in the
  1634. call stack. This can lead to behavior that is not portable
  1635. between implementations of the JMX Remote API. Notably, an
  1636. implementation on J2SE 1.4 will find the RMI stub's loader on
  1637. the stack. But in J2SE 5, this stub is loaded by the
  1638. bootstrap loader, so RMI will find the loader of the user code
  1639. that called an MBeanServerConnection method.
  1640. To avoid this problem, we take advantage of what the RMI stub
  1641. is doing internally. Each remote call will end up calling
  1642. ref.invoke(...), where ref is the RemoteRef parameter given to
  1643. the RMI stub's constructor. It is within this call that the
  1644. deserialization will happen. So we fabricate our own RemoteRef
  1645. that delegates everything to the "real" one but that is loaded
  1646. by a class loader that knows no other classes. The class
  1647. loader NoCallStackClassLoader does this: the RemoteRef is an
  1648. instance of the class named by proxyRefClassName, which is
  1649. fabricated by the class loader using byte code that is defined
  1650. by the string below.
  1651. The call stack when the deserialization happens is thus this:
  1652. MBeanServerConnection.getAttribute (or whatever)
  1653. -> RMIConnectionImpl_Stub.getAttribute
  1654. -> ProxyRef.invoke(...getAttribute...)
  1655. -> UnicastRef.invoke(...getAttribute...)
  1656. -> internal RMI stuff
  1657. Here UnicastRef is the RemoteRef created when the stub was
  1658. deserialized (which is of some RMI internal class). It and the
  1659. "internal RMI stuff" are loaded by the bootstrap loader, so are
  1660. transparent to the stack search. The first non-bootstrap
  1661. loader found is our ProxyRefLoader, as required.
  1662. In a future version of this code as integrated into J2SE 5,
  1663. this workaround could be replaced by direct access to the
  1664. internals of RMI. For now, we use the same code base for J2SE
  1665. and for the standalone Reference Implementation.
  1666. The byte code below encodes the following class, compiled using
  1667. J2SE 1.4.2 with the -g:none option.
  1668. package com.sun.jmx.remote.internal;
  1669. import java.lang.reflect.Method;
  1670. import java.rmi.Remote;
  1671. import java.rmi.server.RemoteRef;
  1672. import com.sun.jmx.remote.internal.ProxyRef;
  1673. public class PRef extends ProxyRef {
  1674. public PRef(RemoteRef ref) {
  1675. super(ref);
  1676. }
  1677. public Object invoke(Remote obj, Method method,
  1678. Object[] params, long opnum)
  1679. throws Exception {
  1680. return ref.invoke(obj, method, params, opnum);
  1681. }
  1682. }
  1683. */
  1684. private static final String rmiConnectionImplStubClassName =
  1685. RMIConnection.class.getName() + "Impl_Stub";
  1686. private static final Class rmiConnectionImplStubClass;
  1687. private static final String pRefClassName =
  1688. "com.sun.jmx.remote.internal.PRef";
  1689. private static final Constructor proxyRefConstructor;
  1690. static {
  1691. final String pRefByteCodeString =
  1692. "\312\376\272\276\0\0\0.\0\27\12\0\5\0\15\11\0\4\0\16\13\0\17\0"+
  1693. "\20\7\0\21\7\0\22\1\0\6<init>\1\0\36(Ljava/rmi/server/RemoteRef;"+
  1694. ")V\1\0\4Code\1\0\6invoke\1\0S(Ljava/rmi/Remote;Ljava/lang/reflec"+
  1695. "t/Method;[Ljava/lang/Object;J)Ljava/lang/Object;\1\0\12Exception"+
  1696. "s\7\0\23\14\0\6\0\7\14\0\24\0\25\7\0\26\14\0\11\0\12\1\0\40com/"+
  1697. "sun/jmx/remote/internal/PRef\1\0$com/sun/jmx/remote/internal/Pr"+
  1698. "oxyRef\1\0\23java/lang/Exception\1\0\3ref\1\0\33Ljava/rmi/serve"+
  1699. "r/RemoteRef;\1\0\31java/rmi/server/RemoteRef\0!\0\4\0\5\0\0\0\0"+
  1700. "\0\2\0\1\0\6\0\7\0\1\0\10\0\0\0\22\0\2\0\2\0\0\0\6*+\267\0\1\261"+
  1701. "\0\0\0\0\0\1\0\11\0\12\0\2\0\10\0\0\0\33\0\6\0\6\0\0\0\17*\264\0"+
  1702. "\2+,-\26\4\271\0\3\6\0\260\0\0\0\0\0\13\0\0\0\4\0\1\0\14\0\0";
  1703. final byte[] pRefByteCode =
  1704. NoCallStackClassLoader.stringToBytes(pRefByteCodeString);
  1705. PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
  1706. public Object run() throws Exception {
  1707. Class thisClass = RMIConnector.class;
  1708. ClassLoader thisLoader = thisClass.getClassLoader();
  1709. ProtectionDomain thisProtectionDomain =
  1710. thisClass.getProtectionDomain();
  1711. String[] otherClassNames = {ProxyRef.class.getName()};
  1712. ClassLoader cl =
  1713. new NoCallStackClassLoader(pRefClassName,
  1714. pRefByteCode,
  1715. otherClassNames,
  1716. thisLoader,
  1717. thisProtectionDomain);
  1718. Class c = cl.loadClass(pRefClassName);
  1719. return c.getConstructor(new Class[] {RemoteRef.class});
  1720. }
  1721. };
  1722. Class stubClass;
  1723. Constructor constr;
  1724. try {
  1725. stubClass = Class.forName(rmiConnectionImplStubClassName);
  1726. constr = (Constructor) AccessController.doPrivileged(action);
  1727. } catch (Exception e) {
  1728. logger.error("<clinit>",
  1729. "Failed to initialize proxy reference consructor "+
  1730. "for " + rmiConnectionImplStubClassName + ": " + e);
  1731. logger.debug("<clinit>",e);
  1732. stubClass = null;
  1733. constr = null;
  1734. }
  1735. rmiConnectionImplStubClass = stubClass;
  1736. proxyRefConstructor = constr;
  1737. }
  1738. private static RMIConnection shadowJrmpStub(RemoteObject stub)
  1739. throws InstantiationException, IllegalAccessException,
  1740. InvocationTargetException, ClassNotFoundException,
  1741. NoSuchMethodException {
  1742. RemoteRef ref = stub.getRef();
  1743. RemoteRef proxyRef = (RemoteRef)
  1744. proxyRefConstructor.newInstance(new Object[] {ref});
  1745. final Class[] constrTypes = {RemoteRef.class};
  1746. final Constructor rmiConnectionImplStubConstructor =
  1747. rmiConnectionImplStubClass.getConstructor(constrTypes);
  1748. Object[] args = {proxyRef};
  1749. RMIConnection proxyStub = (RMIConnection)
  1750. rmiConnectionImplStubConstructor.newInstance(args);
  1751. return proxyStub;
  1752. }
  1753. /*
  1754. The following code performs a similar trick for RMI/IIOP to the
  1755. one described above for RMI/JRMP. Unlike JRMP, though, we
  1756. can't easily insert an object between the RMIConnection stub
  1757. and the RMI/IIOP deserialization code, as explained below.
  1758. A method in an RMI/IIOP stub does the following. It makes an
  1759. org.omg.CORBA_2_3.portable.OutputStream for each request, and
  1760. writes the parameters to it. Then it calls
  1761. _invoke(OutputStream) which it inherits from CORBA's
  1762. ObjectImpl. That returns an
  1763. org.omg.CORBA_2_3.portable.InputStream. The return value is
  1764. read from this InputStream. So the stack during
  1765. deserialization looks like this:
  1766. MBeanServerConnection.getAttribute (or whatever)
  1767. -> _RMIConnection_Stub.getAttribute
  1768. -> Util.readAny (a CORBA method)
  1769. -> InputStream.read_any
  1770. -> internal CORBA stuff
  1771. What we would have *liked* to have done would be the same thing
  1772. as for RMI/JRMP. We create a "ProxyDelegate" that is an
  1773. org.omg.CORBA.portable.Delegate that simply forwards every
  1774. operation to the real original Delegate from the RMIConnection
  1775. stub, except that the InputStream returned by _invoke is
  1776. wrapped by a "ProxyInputStream" that is loaded by our
  1777. NoCallStackClassLoader.
  1778. Unfortunately, this doesn't work, at least with Sun's J2SE
  1779. 1.4.2, because the CORBA code is not designed to allow you to
  1780. change Delegates arbitrarily. You get a ClassCastException
  1781. from code that expects the Delegate to implement an internal
  1782. interface.
  1783. So instead we do the following. We create a subclass of the
  1784. stub that overrides the _invoke method so as to wrap the
  1785. returned InputStream in a ProxyInputStream. We create a
  1786. subclass of ProxyInputStream using the NoCallStackClassLoader
  1787. and override its read_any and read_value(Class) methods.
  1788. (These are the only methods called during deserialization of
  1789. MBeanServerConnection return values.) We extract the Delegate
  1790. from the original stub and insert it into our subclass stub,
  1791. and away we go. The state of a stub consists solely of its
  1792. Delegate.
  1793. We also need to catch ApplicationException, which will encode
  1794. any exceptions declared in the throws clause of the called
  1795. method. Its InputStream needs to be wrapped in a
  1796. ProxyInputSteam too.
  1797. We override _releaseReply in the stub subclass so that it
  1798. replaces a ProxyInputStream argument with the original
  1799. InputStream. This avoids problems if the implementation of
  1800. _releaseReply ends up casting this InputStream to an
  1801. implementation-specific interface (which in Sun's J2SE 5 it
  1802. does).
  1803. It is not strictly necessary for the stub subclass to be loaded
  1804. by a NoCallStackClassLoader, since the call-stack search stops
  1805. at the ProxyInputStream subclass. However, it is convenient
  1806. for two reasons. One is that it means that the
  1807. ProxyInputStream subclass can be accessed directly, without
  1808. using reflection. The other is that it avoids build problems,
  1809. since usually stubs are created after other classes are
  1810. compiled, so we can't access them from this class without,
  1811. again, using reflection.
  1812. The strings below encode the following two Java classes,
  1813. compiled using J2SE 1.4.2 with javac -g:none.
  1814. package com.sun.jmx.remote.internal;
  1815. import org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub;
  1816. import org.omg.CORBA.portable.ApplicationException;
  1817. import org.omg.CORBA.portable.InputStream;
  1818. import org.omg.CORBA.portable.OutputStream;
  1819. import org.omg.CORBA.portable.RemarshalException;
  1820. public class ProxyStub extends _RMIConnection_Stub {
  1821. public InputStream _invoke(OutputStream out)
  1822. throws ApplicationException, RemarshalException {
  1823. try {
  1824. return new PInputStream(super._invoke(out));
  1825. } catch (ApplicationException e) {
  1826. InputStream pis = new PInputStream(e.getInputStream());
  1827. throw new ApplicationException(e.getId(), pis);
  1828. }
  1829. }
  1830. public void _releaseReply(InputStream in) {
  1831. PInputStream pis = (PInputStream) in;
  1832. super._releaseReply(pis.getProxiedInputStream());
  1833. }
  1834. }
  1835. package com.sun.jmx.remote.internal;
  1836. public class PInputStream extends ProxyInputStream {
  1837. public PInputStream(org.omg.CORBA.portable.InputStream in) {
  1838. super(in);
  1839. }
  1840. public org.omg.CORBA.Any read_any() {
  1841. return in.read_any();
  1842. }
  1843. public java.io.Serializable read_value(Class clz) {
  1844. return narrow().read_value(clz);
  1845. }
  1846. }
  1847. */
  1848. private static final String iiopConnectionStubClassName =
  1849. "org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub";
  1850. private static final String proxyStubClassName =
  1851. "com.sun.jmx.remote.internal.ProxyStub";
  1852. private static final String pInputStreamClassName =
  1853. "com.sun.jmx.remote.internal.PInputStream";
  1854. private static final Class proxyStubClass;
  1855. static {
  1856. final String proxyStubByteCodeString =
  1857. "\312\376\272\276\0\0\0.\0)\12\0\14\0\26\7\0\27\12\0\14\0\30\12"+
  1858. "\0\2\0\31\7\0\32\12\0\5\0\33\12\0\5\0\34\12\0\5\0\35\12\0\2\0"+
  1859. "\36\12\0\14\0\37\7\0\40\7\0!\1\0\6<init>\1\0\3()V\1\0\4Code\1"+
  1860. "\0\7_invoke\1\0K(Lorg/omg/CORBA/portable/OutputStream;)Lorg/o"+
  1861. "mg/CORBA/portable/InputStream;\1\0\12Exceptions\7\0\"\1\0\15_"+
  1862. "releaseReply\1\0'(Lorg/omg/CORBA/portable/InputStream;)V\14\0"+
  1863. "\15\0\16\1\0(com/sun/jmx/remote/internal/PInputStream\14\0\20"+
  1864. "\0\21\14\0\15\0\25\1\0+org/omg/CORBA/portable/ApplicationExce"+
  1865. "ption\14\0#\0$\14\0%\0&\14\0\15\0'\14\0(\0$\14\0\24\0\25\1\0%"+
  1866. "com/sun/jmx/remote/internal/ProxyStub\1\0<org/omg/stub/javax/"+
  1867. "management/remote/rmi/_RMIConnection_Stub\1\0)org/omg/CORBA/p"+
  1868. "ortable/RemarshalException\1\0\16getInputStream\1\0&()Lorg/om"+
  1869. "g/CORBA/portable/InputStream;\1\0\5getId\1\0\24()Ljava/lang/S"+
  1870. "tring;\1\09(Ljava/lang/String;Lorg/omg/CORBA/portable/InputSt"+
  1871. "ream;)V\1\0\25getProxiedInputStream\0!\0\13\0\14\0\0\0\0\0\3\0"+
  1872. "\1\0\15\0\16\0\1\0\17\0\0\0\21\0\1\0\1\0\0\0\5*\267\0\1\261\0"+
  1873. "\0\0\0\0\1\0\20\0\21\0\2\0\17\0\0\0;\0\4\0\4\0\0\0'\273\0\2Y*"+
  1874. "+\267\0\3\267\0\4\260M\273\0\2Y,\266\0\6\267\0\4N\273\0\5Y,\266"+
  1875. "\0\7-\267\0\10\277\0\1\0\0\0\14\0\15\0\5\0\0\0\22\0\0\0\6\0\2"+
  1876. "\0\5\0\23\0\1\0\24\0\25\0\1\0\17\0\0\0\36\0\2\0\2\0\0\0\22+\306"+
  1877. "\0\13+\300\0\2\266\0\11L*+\267\0\12\261\0\0\0\0\0\0";
  1878. final String pInputStreamByteCodeString =
  1879. "\312\376\272\276\0\0\0.\0\36\12\0\7\0\17\11\0\6\0\20\12\0\21\0"+
  1880. "\22\12\0\6\0\23\12\0\24\0\25\7\0\26\7\0\27\1\0\6<init>\1\0'(L"+
  1881. "org/omg/CORBA/portable/InputStream;)V\1\0\4Code\1\0\10read_an"+
  1882. "y\1\0\25()Lorg/omg/CORBA/Any;\1\0\12read_value\1\0)(Ljava/lan"+
  1883. "g/Class;)Ljava/io/Serializable;\14\0\10\0\11\14\0\30\0\31\7\0"+
  1884. "\32\14\0\13\0\14\14\0\33\0\34\7\0\35\14\0\15\0\16\1\0(com/sun"+
  1885. "/jmx/remote/internal/PInputStream\1\0,com/sun/jmx/remote/inte"+
  1886. "rnal/ProxyInputStream\1\0\2in\1\0$Lorg/omg/CORBA/portable/Inp"+
  1887. "utStream;\1\0\"org/omg/CORBA/portable/InputStream\1\0\6narrow"+
  1888. "\1\0*()Lorg/omg/CORBA_2_3/portable/InputStream;\1\0&org/omg/C"+
  1889. "ORBA_2_3/portable/InputStream\0!\0\6\0\7\0\0\0\0\0\3\0\1\0\10"+
  1890. "\0\11\0\1\0\12\0\0\0\22\0\2\0\2\0\0\0\6*+\267\0\1\261\0\0\0\0"+
  1891. "\0\1\0\13\0\14\0\1\0\12\0\0\0\24\0\1\0\1\0\0\0\10*\264\0\2\266"+
  1892. "\0\3\260\0\0\0\0\0\1\0\15\0\16\0\1\0\12\0\0\0\25\0\2\0\2\0\0\0"+
  1893. "\11*\266\0\4+\266\0\5\260\0\0\0\0\0\0";
  1894. final byte[] proxyStubByteCode =
  1895. NoCallStackClassLoader.stringToBytes(proxyStubByteCodeString);
  1896. final byte[] pInputStreamByteCode =
  1897. NoCallStackClassLoader.stringToBytes(pInputStreamByteCodeString);
  1898. final String[] classNames={proxyStubClassName, pInputStreamClassName};
  1899. final byte[][] byteCodes = {proxyStubByteCode, pInputStreamByteCode};
  1900. final String[] otherClassNames = {
  1901. iiopConnectionStubClassName,
  1902. ProxyInputStream.class.getName(),
  1903. };
  1904. PrivilegedExceptionAction action = new PrivilegedExceptionAction() {
  1905. public Object run() throws Exception {
  1906. Class thisClass = RMIConnector.class;
  1907. ClassLoader thisLoader = thisClass.getClassLoader();
  1908. ProtectionDomain thisProtectionDomain =
  1909. thisClass.getProtectionDomain();
  1910. ClassLoader cl =
  1911. new NoCallStackClassLoader(classNames,
  1912. byteCodes,
  1913. otherClassNames,
  1914. thisLoader,
  1915. thisProtectionDomain);
  1916. return cl.loadClass(proxyStubClassName);
  1917. }
  1918. };
  1919. Class stubClass;
  1920. try {
  1921. stubClass = (Class) AccessController.doPrivileged(action);
  1922. } catch (Exception e) {
  1923. logger.error("<clinit>",
  1924. "Unexpected exception making shadow IIOP stub class: "+e);
  1925. logger.debug("<clinit>",e);
  1926. stubClass = null;
  1927. }
  1928. proxyStubClass = stubClass;
  1929. }
  1930. private static RMIConnection shadowIiopStub(Stub stub)
  1931. throws InstantiationException, IllegalAccessException {
  1932. Stub proxyStub = (Stub) proxyStubClass.newInstance();
  1933. proxyStub._set_delegate(stub._get_delegate());
  1934. return (RMIConnection) proxyStub;
  1935. }
  1936. private static RMIConnection getConnection(RMIServer server,
  1937. Object credentials)
  1938. throws IOException {
  1939. RMIConnection c = server.newClient(credentials);
  1940. try {
  1941. if (c.getClass() == rmiConnectionImplStubClass)
  1942. return shadowJrmpStub((RemoteObject) c);
  1943. if (c.getClass().getName().equals(iiopConnectionStubClassName))
  1944. return shadowIiopStub((Stub) c);
  1945. logger.trace("getConnection",
  1946. "Did not wrap " + c.getClass() + " to foil " +
  1947. "stack search for classes: class loading semantics " +
  1948. "may be incorrect");
  1949. } catch (Exception e) {
  1950. logger.error("getConnection",
  1951. "Could not wrap " + c.getClass() + " to foil " +
  1952. "stack search for classes: class loading semantics " +
  1953. "may be incorrect: " + e);
  1954. logger.debug("getConnection",e);
  1955. // so just return the original stub, which will work for all
  1956. // but the most exotic class loading situations
  1957. }
  1958. return c;
  1959. }
  1960. private static byte[] base64ToByteArray(String s) {
  1961. int sLen = s.length();
  1962. int numGroups = sLen4;
  1963. if (4*numGroups != sLen)
  1964. throw new IllegalArgumentException(
  1965. "String length must be a multiple of four.");
  1966. int missingBytesInLastGroup = 0;
  1967. int numFullGroups = numGroups;
  1968. if (sLen != 0) {
  1969. if (s.charAt(sLen-1) == '=') {
  1970. missingBytesInLastGroup++;
  1971. numFullGroups--;
  1972. }
  1973. if (s.charAt(sLen-2) == '=')
  1974. missingBytesInLastGroup++;
  1975. }
  1976. byte[] result = new byte[3*numGroups - missingBytesInLastGroup];
  1977. // Translate all full groups from base64 to byte array elements
  1978. int inCursor = 0, outCursor = 0;
  1979. for (int i=0; i<numFullGroups; i++) {
  1980. int ch0 = base64toInt(s.charAt(inCursor++));
  1981. int ch1 = base64toInt(s.charAt(inCursor++));
  1982. int ch2 = base64toInt(s.charAt(inCursor++));
  1983. int ch3 = base64toInt(s.charAt(inCursor++));
  1984. result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
  1985. result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
  1986. result[outCursor++] = (byte) ((ch2 << 6) | ch3);
  1987. }
  1988. // Translate partial group, if present
  1989. if (missingBytesInLastGroup != 0) {
  1990. int ch0 = base64toInt(s.charAt(inCursor++));
  1991. int ch1 = base64toInt(s.charAt(inCursor++));
  1992. result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
  1993. if (missingBytesInLastGroup == 1) {
  1994. int ch2 = base64toInt(s.charAt(inCursor++));
  1995. result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
  1996. }
  1997. }
  1998. // assert inCursor == s.length()-missingBytesInLastGroup;
  1999. // assert outCursor == result.length;
  2000. return result;
  2001. }
  2002. /**
  2003. * Translates the specified character, which is assumed to be in the
  2004. * "Base 64 Alphabet" into its equivalent 6-bit positive integer.
  2005. *
  2006. * @throw IllegalArgumentException if
  2007. * c is not in the Base64 Alphabet.
  2008. */
  2009. private static int base64toInt(char c) {
  2010. int result;
  2011. if (c >= base64ToInt.length)
  2012. result = -1;
  2013. else
  2014. result = base64ToInt[c];
  2015. if (result < 0)
  2016. throw new IllegalArgumentException("Illegal character " + c);
  2017. return result;
  2018. }
  2019. /**
  2020. * This array is a lookup table that translates unicode characters
  2021. * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
  2022. * into their 6-bit positive integer equivalents. Characters that
  2023. * are not in the Base64 alphabet but fall within the bounds of the
  2024. * array are translated to -1.
  2025. */
  2026. private static final byte base64ToInt[] = {
  2027. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  2028. -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
  2029. -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
  2030. 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
  2031. 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
  2032. 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
  2033. 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
  2034. };
  2035. //--------------------------------------------------------------------
  2036. // Private stuff - Find / Set default class loader
  2037. //--------------------------------------------------------------------
  2038. private ClassLoader pushDefaultClassLoader() {
  2039. final Thread t = Thread.currentThread();
  2040. final ClassLoader old = t.getContextClassLoader();
  2041. if (defaultClassLoader != null)
  2042. AccessController.doPrivileged(new PrivilegedAction() {
  2043. public Object run() {
  2044. t.setContextClassLoader(defaultClassLoader);
  2045. return null;
  2046. }
  2047. });
  2048. return old;
  2049. }
  2050. private void popDefaultClassLoader(final ClassLoader old) {
  2051. AccessController.doPrivileged(new PrivilegedAction() {
  2052. public Object run() {
  2053. Thread.currentThread().setContextClassLoader(old);
  2054. return null;
  2055. }
  2056. });
  2057. }
  2058. //--------------------------------------------------------------------
  2059. // Private variables
  2060. //--------------------------------------------------------------------
  2061. /**
  2062. * @serial The RMIServer stub of the RMI JMX Connector server to
  2063. * which this client connector is (or will be) connected. This
  2064. * field can be null when <var>jmxServiceURL</var> is not
  2065. * null. This includes the case where <var>jmxServiceURL</var>
  2066. * contains a serialized RMIServer stub. If both
  2067. * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
  2068. * serialization will fail.
  2069. *
  2070. * @see #RMIConnector(RMIServer,Map)
  2071. **/
  2072. private final RMIServer rmiServer;
  2073. /**
  2074. * @serial The JMXServiceURL of the RMI JMX Connector server to
  2075. * which this client connector will be connected. This field can
  2076. * be null when <var>rmiServer</var> is not null. If both
  2077. * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
  2078. * serialization will fail.
  2079. *
  2080. * @see #RMIConnector(JMXServiceURL,Map)
  2081. **/
  2082. private final JMXServiceURL jmxServiceURL;
  2083. // ---------------------------------------------------------
  2084. // WARNING - WARNING - WARNING - WARNING - WARNING - WARNING
  2085. // ---------------------------------------------------------
  2086. // Any transient variable which needs to be initialized should
  2087. // be initialized in the method initTransient()
  2088. private transient Map env;
  2089. private transient ClassLoader defaultClassLoader;
  2090. private transient RMIConnection connection;
  2091. private transient String connectionId;
  2092. private long clientNotifID = 0;
  2093. private transient WeakHashMap rmbscMap;
  2094. private transient RMINotifClient rmiNotifClient;
  2095. // = new RMINotifClient(new Integer(0));
  2096. private transient long clientNotifCounter = 0;
  2097. private transient boolean connected;
  2098. // = false;
  2099. private transient boolean terminated;
  2100. // = false;
  2101. private transient int[] terminationLock;
  2102. private transient Exception closeException;
  2103. private transient NotificationBroadcasterSupport connectionBroadcaster;
  2104. private transient ClientCommunicatorAdmin communicatorAdmin;
  2105. /**
  2106. * A static WeakReference to an {@link org.omg.CORBA.ORB ORB} to
  2107. * connect unconnected stubs.
  2108. **/
  2109. private static WeakReference orb = null;
  2110. private static final ObjectName delegateName;
  2111. static {
  2112. try {
  2113. delegateName =
  2114. new ObjectName("JMImplementation:type=MBeanServerDelegate");
  2115. } catch (MalformedObjectNameException e) {
  2116. Error error = new Error("Can't initialize delegateName");
  2117. EnvHelp.initCause(error, e);
  2118. throw error;
  2119. }
  2120. }
  2121. // TRACES & DEBUG
  2122. //---------------
  2123. private static String objects(final Object[] objs) {
  2124. if (objs == null)
  2125. return "null";
  2126. else
  2127. return Arrays.asList(objs).toString();
  2128. }
  2129. private static String strings(final String[] strs) {
  2130. return objects(strs);
  2131. }
  2132. }