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