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.lang.ref.WeakReference; 45 import java.lang.reflect.Constructor; 46 import java.lang.reflect.InvocationHandler; 47 import java.lang.reflect.InvocationTargetException; 48 import java.lang.reflect.Proxy; 49 import java.net.MalformedURLException; 50 import java.rmi.MarshalException; 51 import java.rmi.MarshalledObject; 52 import java.rmi.NoSuchObjectException; 53 import java.rmi.Remote; 54 import java.rmi.ServerException; 55 import java.rmi.UnmarshalException; 56 import java.rmi.server.RMIClientSocketFactory; 57 import java.rmi.server.RemoteObject; 58 import java.rmi.server.RemoteObjectInvocationHandler; 59 import java.rmi.server.RemoteRef; 60 import java.security.AccessController; 61 import java.security.PrivilegedAction; 62 import java.security.PrivilegedExceptionAction; 63 import java.security.PrivilegedActionException; 64 import java.security.ProtectionDomain; 65 import java.util.Arrays; 66 import java.util.Collections; 67 import java.util.HashMap; 68 import java.util.Map; 69 import java.util.Properties; 70 import java.util.Set; 71 import java.util.WeakHashMap; 72 import java.util.stream.Collectors; 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 + ", signature=" + strings(signature)); 716 717 final MarshalledObject<Object[]> sParams = 718 new MarshalledObject<Object[]>(params); 719 final ClassLoader old = pushDefaultClassLoader(); 720 try { 721 return connection.createMBean(className, 722 name, 723 sParams, 724 signature, 725 delegationSubject); 726 } catch (IOException ioe) { 727 communicatorAdmin.gotIOException(ioe); 728 729 return connection.createMBean(className, 730 name, 731 sParams, 732 signature, 733 delegationSubject); 734 } finally { 735 popDefaultClassLoader(old); 736 } 737 } 738 739 public ObjectInstance createMBean(String className, 740 ObjectName name, 741 ObjectName loaderName, 742 Object params[], 743 String signature[]) 744 throws ReflectionException, 745 InstanceAlreadyExistsException, 746 MBeanRegistrationException, 747 MBeanException, 748 NotCompliantMBeanException, 749 InstanceNotFoundException, 750 IOException { 751 if (logger.debugOn()) logger.debug( 752 "createMBean(String,ObjectName,ObjectName,Object[],String[])", 753 "className=" + className + ", name=" + name + ", loaderName=" 754 + loaderName + ", signature=" + strings(signature)); 755 756 final MarshalledObject<Object[]> sParams = 757 new MarshalledObject<Object[]>(params); 758 final ClassLoader old = pushDefaultClassLoader(); 759 try { 760 return connection.createMBean(className, 761 name, 762 loaderName, 763 sParams, 764 signature, 765 delegationSubject); 766 } catch (IOException ioe) { 767 communicatorAdmin.gotIOException(ioe); 768 769 return connection.createMBean(className, 770 name, 771 loaderName, 772 sParams, 773 signature, 774 delegationSubject); 775 } finally { 776 popDefaultClassLoader(old); 777 } 778 } 779 780 public void unregisterMBean(ObjectName name) 781 throws InstanceNotFoundException, 782 MBeanRegistrationException, 783 IOException { 784 if (logger.debugOn()) 785 logger.debug("unregisterMBean", "name=" + name); 786 787 final ClassLoader old = pushDefaultClassLoader(); 788 try { 789 connection.unregisterMBean(name, delegationSubject); 790 } catch (IOException ioe) { 791 communicatorAdmin.gotIOException(ioe); 792 793 connection.unregisterMBean(name, delegationSubject); 794 } finally { 795 popDefaultClassLoader(old); 796 } 797 } 798 799 public ObjectInstance getObjectInstance(ObjectName name) 800 throws InstanceNotFoundException, 801 IOException { 802 if (logger.debugOn()) 803 logger.debug("getObjectInstance", "name=" + name); 804 805 final ClassLoader old = pushDefaultClassLoader(); 806 try { 807 return connection.getObjectInstance(name, delegationSubject); 808 } catch (IOException ioe) { 809 communicatorAdmin.gotIOException(ioe); 810 811 return connection.getObjectInstance(name, delegationSubject); 812 } finally { 813 popDefaultClassLoader(old); 814 } 815 } 816 817 public Set<ObjectInstance> queryMBeans(ObjectName name, 818 QueryExp query) 819 throws IOException { 820 if (logger.debugOn()) logger.debug("queryMBeans", 821 "name=" + name + ", query=" + query); 822 823 final MarshalledObject<QueryExp> sQuery = 824 new MarshalledObject<QueryExp>(query); 825 final ClassLoader old = pushDefaultClassLoader(); 826 try { 827 return connection.queryMBeans(name, sQuery, delegationSubject); 828 } catch (IOException ioe) { 829 communicatorAdmin.gotIOException(ioe); 830 831 return connection.queryMBeans(name, sQuery, delegationSubject); 832 } finally { 833 popDefaultClassLoader(old); 834 } 835 } 836 837 public Set<ObjectName> queryNames(ObjectName name, 838 QueryExp query) 839 throws IOException { 840 if (logger.debugOn()) logger.debug("queryNames", 841 "name=" + name + ", query=" + query); 842 843 final MarshalledObject<QueryExp> sQuery = 844 new MarshalledObject<QueryExp>(query); 845 final ClassLoader old = pushDefaultClassLoader(); 846 try { 847 return connection.queryNames(name, sQuery, delegationSubject); 848 } catch (IOException ioe) { 849 communicatorAdmin.gotIOException(ioe); 850 851 return connection.queryNames(name, sQuery, delegationSubject); 852 } finally { 853 popDefaultClassLoader(old); 854 } 855 } 856 857 public boolean isRegistered(ObjectName name) 858 throws IOException { 859 if (logger.debugOn()) 860 logger.debug("isRegistered", "name=" + name); 861 862 final ClassLoader old = pushDefaultClassLoader(); 863 try { 864 return connection.isRegistered(name, delegationSubject); 865 } catch (IOException ioe) { 866 communicatorAdmin.gotIOException(ioe); 867 868 return connection.isRegistered(name, delegationSubject); 869 } finally { 870 popDefaultClassLoader(old); 871 } 872 } 873 874 public Integer getMBeanCount() 875 throws IOException { 876 if (logger.debugOn()) logger.debug("getMBeanCount", ""); 877 878 final ClassLoader old = pushDefaultClassLoader(); 879 try { 880 return connection.getMBeanCount(delegationSubject); 881 } catch (IOException ioe) { 882 communicatorAdmin.gotIOException(ioe); 883 884 return connection.getMBeanCount(delegationSubject); 885 } finally { 886 popDefaultClassLoader(old); 887 } 888 } 889 890 public Object getAttribute(ObjectName name, 891 String attribute) 892 throws MBeanException, 893 AttributeNotFoundException, 894 InstanceNotFoundException, 895 ReflectionException, 896 IOException { 897 if (logger.debugOn()) logger.debug("getAttribute", 898 "name=" + name + ", attribute=" 899 + attribute); 900 901 final ClassLoader old = pushDefaultClassLoader(); 902 try { 903 return connection.getAttribute(name, 904 attribute, 905 delegationSubject); 906 } catch (IOException ioe) { 907 communicatorAdmin.gotIOException(ioe); 908 909 return connection.getAttribute(name, 910 attribute, 911 delegationSubject); 912 } finally { 913 popDefaultClassLoader(old); 914 } 915 } 916 917 public AttributeList getAttributes(ObjectName name, 918 String[] attributes) 919 throws InstanceNotFoundException, 920 ReflectionException, 921 IOException { 922 if (logger.debugOn()) logger.debug("getAttributes", 923 "name=" + name + ", attributes=" 924 + strings(attributes)); 925 926 final ClassLoader old = pushDefaultClassLoader(); 927 try { 928 return connection.getAttributes(name, 929 attributes, 930 delegationSubject); 931 932 } catch (IOException ioe) { 933 communicatorAdmin.gotIOException(ioe); 934 935 return connection.getAttributes(name, 936 attributes, 937 delegationSubject); 938 } finally { 939 popDefaultClassLoader(old); 940 } 941 } 942 943 944 public void setAttribute(ObjectName name, 945 Attribute attribute) 946 throws InstanceNotFoundException, 947 AttributeNotFoundException, 948 InvalidAttributeValueException, 949 MBeanException, 950 ReflectionException, 951 IOException { 952 953 if (logger.debugOn()) logger.debug("setAttribute", 954 "name=" + name + ", attribute name=" 955 + attribute.getName()); 956 957 final MarshalledObject<Attribute> sAttribute = 958 new MarshalledObject<Attribute>(attribute); 959 final ClassLoader old = pushDefaultClassLoader(); 960 try { 961 connection.setAttribute(name, sAttribute, delegationSubject); 962 } catch (IOException ioe) { 963 communicatorAdmin.gotIOException(ioe); 964 965 connection.setAttribute(name, sAttribute, delegationSubject); 966 } finally { 967 popDefaultClassLoader(old); 968 } 969 } 970 971 public AttributeList setAttributes(ObjectName name, 972 AttributeList attributes) 973 throws InstanceNotFoundException, 974 ReflectionException, 975 IOException { 976 977 if (logger.debugOn()) { 978 logger.debug("setAttributes", 979 "name=" + name + ", attribute names=" 980 + getAttributesNames(attributes)); 981 } 982 983 final MarshalledObject<AttributeList> sAttributes = 984 new MarshalledObject<AttributeList>(attributes); 985 final ClassLoader old = pushDefaultClassLoader(); 986 try { 987 return connection.setAttributes(name, 988 sAttributes, 989 delegationSubject); 990 } catch (IOException ioe) { 991 communicatorAdmin.gotIOException(ioe); 992 993 return connection.setAttributes(name, 994 sAttributes, 995 delegationSubject); 996 } finally { 997 popDefaultClassLoader(old); 998 } 999 } 1000 1001 1002 public Object invoke(ObjectName name, 1003 String operationName, 1004 Object params[], 1005 String signature[]) 1006 throws InstanceNotFoundException, 1007 MBeanException, 1008 ReflectionException, 1009 IOException { 1010 1011 if (logger.debugOn()) logger.debug("invoke", 1012 "name=" + name 1013 + ", operationName=" + operationName 1014 + ", signature=" + strings(signature)); 1015 1016 final MarshalledObject<Object[]> sParams = 1017 new MarshalledObject<Object[]>(params); 1018 final ClassLoader old = pushDefaultClassLoader(); 1019 try { 1020 return connection.invoke(name, 1021 operationName, 1022 sParams, 1023 signature, 1024 delegationSubject); 1025 } catch (IOException ioe) { 1026 communicatorAdmin.gotIOException(ioe); 1027 1028 return connection.invoke(name, 1029 operationName, 1030 sParams, 1031 signature, 1032 delegationSubject); 1033 } finally { 1034 popDefaultClassLoader(old); 1035 } 1036 } 1037 1038 1039 public String getDefaultDomain() 1040 throws IOException { 1041 if (logger.debugOn()) logger.debug("getDefaultDomain", ""); 1042 1043 final ClassLoader old = pushDefaultClassLoader(); 1044 try { 1045 return connection.getDefaultDomain(delegationSubject); 1046 } catch (IOException ioe) { 1047 communicatorAdmin.gotIOException(ioe); 1048 1049 return connection.getDefaultDomain(delegationSubject); 1050 } finally { 1051 popDefaultClassLoader(old); 1052 } 1053 } 1054 1055 public String[] getDomains() throws IOException { 1056 if (logger.debugOn()) logger.debug("getDomains", ""); 1057 1058 final ClassLoader old = pushDefaultClassLoader(); 1059 try { 1060 return connection.getDomains(delegationSubject); 1061 } catch (IOException ioe) { 1062 communicatorAdmin.gotIOException(ioe); 1063 1064 return connection.getDomains(delegationSubject); 1065 } finally { 1066 popDefaultClassLoader(old); 1067 } 1068 } 1069 1070 public MBeanInfo getMBeanInfo(ObjectName name) 1071 throws InstanceNotFoundException, 1072 IntrospectionException, 1073 ReflectionException, 1074 IOException { 1075 1076 if (logger.debugOn()) logger.debug("getMBeanInfo", "name=" + name); 1077 final ClassLoader old = pushDefaultClassLoader(); 1078 try { 1079 return connection.getMBeanInfo(name, delegationSubject); 1080 } catch (IOException ioe) { 1081 communicatorAdmin.gotIOException(ioe); 1082 1083 return connection.getMBeanInfo(name, delegationSubject); 1084 } finally { 1085 popDefaultClassLoader(old); 1086 } 1087 } 1088 1089 1090 public boolean isInstanceOf(ObjectName name, 1091 String className) 1092 throws InstanceNotFoundException, 1093 IOException { 1094 if (logger.debugOn()) 1095 logger.debug("isInstanceOf", "name=" + name + 1096 ", className=" + className); 1097 1098 final ClassLoader old = pushDefaultClassLoader(); 1099 try { 1100 return connection.isInstanceOf(name, 1101 className, 1102 delegationSubject); 1103 } catch (IOException ioe) { 1104 communicatorAdmin.gotIOException(ioe); 1105 1106 return connection.isInstanceOf(name, 1107 className, 1108 delegationSubject); 1109 } finally { 1110 popDefaultClassLoader(old); 1111 } 1112 } 1113 1114 public void addNotificationListener(ObjectName name, 1115 ObjectName listener, 1116 NotificationFilter filter, 1117 Object handback) 1118 throws InstanceNotFoundException, 1119 IOException { 1120 1121 if (logger.debugOn()) 1122 logger.debug("addNotificationListener" + 1123 "(ObjectName,ObjectName,NotificationFilter,Object)", 1124 "name=" + name + ", listener=" + listener 1125 + ", filter=" + filter + ", handback=" + handback); 1126 1127 final MarshalledObject<NotificationFilter> sFilter = 1128 new MarshalledObject<NotificationFilter>(filter); 1129 final MarshalledObject<Object> sHandback = 1130 new MarshalledObject<Object>(handback); 1131 final ClassLoader old = pushDefaultClassLoader(); 1132 try { 1133 connection.addNotificationListener(name, 1134 listener, 1135 sFilter, 1136 sHandback, 1137 delegationSubject); 1138 } catch (IOException ioe) { 1139 communicatorAdmin.gotIOException(ioe); 1140 1141 connection.addNotificationListener(name, 1142 listener, 1143 sFilter, 1144 sHandback, 1145 delegationSubject); 1146 } finally { 1147 popDefaultClassLoader(old); 1148 } 1149 } 1150 1151 public void removeNotificationListener(ObjectName name, 1152 ObjectName listener) 1153 throws InstanceNotFoundException, 1154 ListenerNotFoundException, 1155 IOException { 1156 1157 if (logger.debugOn()) logger.debug("removeNotificationListener" + 1158 "(ObjectName,ObjectName)", 1159 "name=" + name 1160 + ", listener=" + listener); 1161 1162 final ClassLoader old = pushDefaultClassLoader(); 1163 try { 1164 connection.removeNotificationListener(name, 1165 listener, 1166 delegationSubject); 1167 } catch (IOException ioe) { 1168 communicatorAdmin.gotIOException(ioe); 1169 1170 connection.removeNotificationListener(name, 1171 listener, 1172 delegationSubject); 1173 } finally { 1174 popDefaultClassLoader(old); 1175 } 1176 } 1177 1178 public void removeNotificationListener(ObjectName name, 1179 ObjectName listener, 1180 NotificationFilter filter, 1181 Object handback) 1182 throws InstanceNotFoundException, 1183 ListenerNotFoundException, 1184 IOException { 1185 if (logger.debugOn()) 1186 logger.debug("removeNotificationListener" + 1187 "(ObjectName,ObjectName,NotificationFilter,Object)", 1188 "name=" + name 1189 + ", listener=" + listener 1190 + ", filter=" + filter 1191 + ", handback=" + handback); 1192 1193 final MarshalledObject<NotificationFilter> sFilter = 1194 new MarshalledObject<NotificationFilter>(filter); 1195 final MarshalledObject<Object> sHandback = 1196 new MarshalledObject<Object>(handback); 1197 final ClassLoader old = pushDefaultClassLoader(); 1198 try { 1199 connection.removeNotificationListener(name, 1200 listener, 1201 sFilter, 1202 sHandback, 1203 delegationSubject); 1204 } catch (IOException ioe) { 1205 communicatorAdmin.gotIOException(ioe); 1206 1207 connection.removeNotificationListener(name, 1208 listener, 1209 sFilter, 1210 sHandback, 1211 delegationSubject); 1212 } finally { 1213 popDefaultClassLoader(old); 1214 } 1215 } 1216 1217 // Specific Notification Handle ---------------------------------- 1218 1219 public void addNotificationListener(ObjectName name, 1220 NotificationListener listener, 1221 NotificationFilter filter, 1222 Object handback) 1223 throws InstanceNotFoundException, 1224 IOException { 1225 1226 final boolean debug = logger.debugOn(); 1227 1228 if (debug) 1229 logger.debug("addNotificationListener" + 1230 "(ObjectName,NotificationListener,"+ 1231 "NotificationFilter,Object)", 1232 "name=" + name 1233 + ", listener=" + listener 1234 + ", filter=" + filter 1235 + ", handback=" + handback); 1236 1237 final Integer listenerID = 1238 addListenerWithSubject(name, 1239 new MarshalledObject<NotificationFilter>(filter), 1240 delegationSubject,true); 1241 rmiNotifClient.addNotificationListener(listenerID, name, listener, 1242 filter, handback, 1243 delegationSubject); 1244 } 1245 1246 public void removeNotificationListener(ObjectName name, 1247 NotificationListener listener) 1248 throws InstanceNotFoundException, 1249 ListenerNotFoundException, 1250 IOException { 1251 1252 final boolean debug = logger.debugOn(); 1253 1254 if (debug) logger.debug("removeNotificationListener"+ 1255 "(ObjectName,NotificationListener)", 1256 "name=" + name 1257 + ", listener=" + listener); 1258 1259 final Integer[] ret = 1260 rmiNotifClient.removeNotificationListener(name, listener); 1261 1262 if (debug) logger.debug("removeNotificationListener", 1263 "listenerIDs=" + objects(ret)); 1264 1265 final ClassLoader old = pushDefaultClassLoader(); 1266 1267 try { 1268 connection.removeNotificationListeners(name, 1269 ret, 1270 delegationSubject); 1271 } catch (IOException ioe) { 1272 communicatorAdmin.gotIOException(ioe); 1273 1274 connection.removeNotificationListeners(name, 1275 ret, 1276 delegationSubject); 1277 } finally { 1278 popDefaultClassLoader(old); 1279 } 1280 1281 } 1282 1283 public void removeNotificationListener(ObjectName name, 1284 NotificationListener listener, 1285 NotificationFilter filter, 1286 Object handback) 1287 throws InstanceNotFoundException, 1288 ListenerNotFoundException, 1289 IOException { 1290 final boolean debug = logger.debugOn(); 1291 1292 if (debug) 1293 logger.debug("removeNotificationListener"+ 1294 "(ObjectName,NotificationListener,"+ 1295 "NotificationFilter,Object)", 1296 "name=" + name 1297 + ", listener=" + listener 1298 + ", filter=" + filter 1299 + ", handback=" + handback); 1300 1301 final Integer ret = 1302 rmiNotifClient.removeNotificationListener(name, listener, 1303 filter, handback); 1304 1305 if (debug) logger.debug("removeNotificationListener", 1306 "listenerID=" + ret); 1307 1308 final ClassLoader old = pushDefaultClassLoader(); 1309 try { 1310 connection.removeNotificationListeners(name, 1311 new Integer[] {ret}, 1312 delegationSubject); 1313 } catch (IOException ioe) { 1314 communicatorAdmin.gotIOException(ioe); 1315 1316 connection.removeNotificationListeners(name, 1317 new Integer[] {ret}, 1318 delegationSubject); 1319 } finally { 1320 popDefaultClassLoader(old); 1321 } 1322 1323 } 1324 } 1325 1326 //-------------------------------------------------------------------- 1327 private class RMINotifClient extends ClientNotifForwarder { 1328 public RMINotifClient(ClassLoader cl, Map<String, ?> env) { 1329 super(cl, env); 1330 } 1331 1332 protected NotificationResult fetchNotifs(long clientSequenceNumber, 1333 int maxNotifications, 1334 long timeout) 1335 throws IOException, ClassNotFoundException { 1336 1337 boolean retried = false; 1338 while (true) { // used for a successful re-connection 1339 // or a transient network problem 1340 try { 1341 return connection.fetchNotifications(clientSequenceNumber, 1342 maxNotifications, 1343 timeout); // return normally 1344 } catch (IOException ioe) { 1345 // Examine the chain of exceptions to determine whether this 1346 // is a deserialization issue. If so - we propagate the 1347 // appropriate exception to the caller, who will then 1348 // proceed with fetching notifications one by one 1349 rethrowDeserializationException(ioe); 1350 1351 try { 1352 communicatorAdmin.gotIOException(ioe); 1353 // reconnection OK, back to "while" to do again 1354 } catch (IOException ee) { 1355 boolean toClose = false; 1356 1357 synchronized (this) { 1358 if (terminated) { 1359 // the connection is closed. 1360 throw ioe; 1361 } else if (retried) { 1362 toClose = true; 1363 } 1364 } 1365 1366 if (toClose) { 1367 // JDK-8049303 1368 // We received an IOException - but the communicatorAdmin 1369 // did not close the connection - possibly because 1370 // the original exception was raised by a transient network 1371 // problem? 1372 // We already know that this exception is not due to a deserialization 1373 // issue as we already took care of that before involving the 1374 // communicatorAdmin. Moreover - we already made one retry attempt 1375 // at fetching the same batch of notifications - and the 1376 // problem persisted. 1377 // Since trying again doesn't seem to solve the issue, we will now 1378 // close the connection. Doing otherwise might cause the 1379 // NotifFetcher thread to die silently. 1380 final Notification failedNotif = 1381 new JMXConnectionNotification( 1382 JMXConnectionNotification.FAILED, 1383 this, 1384 connectionId, 1385 clientNotifSeqNo++, 1386 "Failed to communicate with the server: " + ioe.toString(), 1387 ioe); 1388 1389 sendNotification(failedNotif); 1390 1391 try { 1392 close(true); 1393 } catch (Exception e) { 1394 // OK. 1395 // We are closing 1396 } 1397 throw ioe; // the connection is closed here. 1398 } else { 1399 // JDK-8049303 possible transient network problem, 1400 // let's try one more time 1401 retried = true; 1402 } 1403 } 1404 } 1405 } 1406 } 1407 1408 private void rethrowDeserializationException(IOException ioe) 1409 throws ClassNotFoundException, IOException { 1410 // specially treating for an UnmarshalException 1411 if (ioe instanceof UnmarshalException) { 1412 throw ioe; // the fix of 6937053 made ClientNotifForwarder.fetchNotifs 1413 // fetch one by one with UnmarshalException 1414 } else if (ioe instanceof MarshalException) { 1415 // IIOP will throw MarshalException wrapping a NotSerializableException 1416 // when a server fails to serialize a response. 1417 MarshalException me = (MarshalException)ioe; 1418 if (me.detail instanceof NotSerializableException) { 1419 throw (NotSerializableException)me.detail; 1420 } 1421 } 1422 1423 // Not serialization problem, return. 1424 } 1425 1426 protected Integer addListenerForMBeanRemovedNotif() 1427 throws IOException, InstanceNotFoundException { 1428 NotificationFilterSupport clientFilter = 1429 new NotificationFilterSupport(); 1430 clientFilter.enableType( 1431 MBeanServerNotification.UNREGISTRATION_NOTIFICATION); 1432 MarshalledObject<NotificationFilter> sFilter = 1433 new MarshalledObject<NotificationFilter>(clientFilter); 1434 1435 Integer[] listenerIDs; 1436 final ObjectName[] names = 1437 new ObjectName[] {MBeanServerDelegate.DELEGATE_NAME}; 1438 final MarshalledObject<NotificationFilter>[] filters = 1439 Util.cast(new MarshalledObject<?>[] {sFilter}); 1440 final Subject[] subjects = new Subject[] {null}; 1441 try { 1442 listenerIDs = 1443 connection.addNotificationListeners(names, 1444 filters, 1445 subjects); 1446 1447 } catch (IOException ioe) { 1448 communicatorAdmin.gotIOException(ioe); 1449 1450 listenerIDs = 1451 connection.addNotificationListeners(names, 1452 filters, 1453 subjects); 1454 } 1455 return listenerIDs[0]; 1456 } 1457 1458 protected void removeListenerForMBeanRemovedNotif(Integer id) 1459 throws IOException, InstanceNotFoundException, 1460 ListenerNotFoundException { 1461 try { 1462 connection.removeNotificationListeners( 1463 MBeanServerDelegate.DELEGATE_NAME, 1464 new Integer[] {id}, 1465 null); 1466 } catch (IOException ioe) { 1467 communicatorAdmin.gotIOException(ioe); 1468 1469 connection.removeNotificationListeners( 1470 MBeanServerDelegate.DELEGATE_NAME, 1471 new Integer[] {id}, 1472 null); 1473 } 1474 1475 } 1476 1477 protected void lostNotifs(String message, long number) { 1478 final String notifType = JMXConnectionNotification.NOTIFS_LOST; 1479 1480 final JMXConnectionNotification n = 1481 new JMXConnectionNotification(notifType, 1482 RMIConnector.this, 1483 connectionId, 1484 clientNotifCounter++, 1485 message, 1486 Long.valueOf(number)); 1487 sendNotification(n); 1488 } 1489 } 1490 1491 private class RMIClientCommunicatorAdmin extends ClientCommunicatorAdmin { 1492 public RMIClientCommunicatorAdmin(long period) { 1493 super(period); 1494 } 1495 1496 @Override 1497 public void gotIOException(IOException ioe) throws IOException { 1498 if (ioe instanceof NoSuchObjectException) { 1499 // need to restart 1500 super.gotIOException(ioe); 1501 1502 return; 1503 } 1504 1505 // check if the connection is broken 1506 try { 1507 connection.getDefaultDomain(null); 1508 } catch (IOException ioexc) { 1509 boolean toClose = false; 1510 1511 synchronized(this) { 1512 if (!terminated) { 1513 terminated = true; 1514 1515 toClose = true; 1516 } 1517 } 1518 1519 if (toClose) { 1520 // we should close the connection, 1521 // but send a failed notif at first 1522 final Notification failedNotif = 1523 new JMXConnectionNotification( 1524 JMXConnectionNotification.FAILED, 1525 this, 1526 connectionId, 1527 clientNotifSeqNo++, 1528 "Failed to communicate with the server: "+ioe.toString(), 1529 ioe); 1530 1531 sendNotification(failedNotif); 1532 1533 try { 1534 close(true); 1535 } catch (Exception e) { 1536 // OK. 1537 // We are closing 1538 } 1539 } 1540 } 1541 1542 // forward the exception 1543 if (ioe instanceof ServerException) { 1544 /* Need to unwrap the exception. 1545 Some user-thrown exception at server side will be wrapped by 1546 rmi into a ServerException. 1547 For example, a RMIConnnectorServer will wrap a 1548 ClassNotFoundException into a UnmarshalException, and rmi 1549 will throw a ServerException at client side which wraps this 1550 UnmarshalException. 1551 No failed notif here. 1552 */ 1553 Throwable tt = ((ServerException)ioe).detail; 1554 1555 if (tt instanceof IOException) { 1556 throw (IOException)tt; 1557 } else if (tt instanceof RuntimeException) { 1558 throw (RuntimeException)tt; 1559 } 1560 } 1561 1562 throw ioe; 1563 } 1564 1565 public void reconnectNotificationListeners(ClientListenerInfo[] old) throws IOException { 1566 final int len = old.length; 1567 int i; 1568 1569 ClientListenerInfo[] clis = new ClientListenerInfo[len]; 1570 1571 final Subject[] subjects = new Subject[len]; 1572 final ObjectName[] names = new ObjectName[len]; 1573 final NotificationListener[] listeners = new NotificationListener[len]; 1574 final NotificationFilter[] filters = new NotificationFilter[len]; 1575 final MarshalledObject<NotificationFilter>[] mFilters = 1576 Util.cast(new MarshalledObject<?>[len]); 1577 final Object[] handbacks = new Object[len]; 1578 1579 for (i=0;i<len;i++) { 1580 subjects[i] = old[i].getDelegationSubject(); 1581 names[i] = old[i].getObjectName(); 1582 listeners[i] = old[i].getListener(); 1583 filters[i] = old[i].getNotificationFilter(); 1584 mFilters[i] = new MarshalledObject<NotificationFilter>(filters[i]); 1585 handbacks[i] = old[i].getHandback(); 1586 } 1587 1588 try { 1589 Integer[] ids = addListenersWithSubjects(names,mFilters,subjects,false); 1590 1591 for (i=0;i<len;i++) { 1592 clis[i] = new ClientListenerInfo(ids[i], 1593 names[i], 1594 listeners[i], 1595 filters[i], 1596 handbacks[i], 1597 subjects[i]); 1598 } 1599 1600 rmiNotifClient.postReconnection(clis); 1601 1602 return; 1603 } catch (InstanceNotFoundException infe) { 1604 // OK, we will do one by one 1605 } 1606 1607 int j = 0; 1608 for (i=0;i<len;i++) { 1609 try { 1610 Integer id = addListenerWithSubject(names[i], 1611 new MarshalledObject<NotificationFilter>(filters[i]), 1612 subjects[i], 1613 false); 1614 1615 clis[j++] = new ClientListenerInfo(id, 1616 names[i], 1617 listeners[i], 1618 filters[i], 1619 handbacks[i], 1620 subjects[i]); 1621 } catch (InstanceNotFoundException infe) { 1622 logger.warning("reconnectNotificationListeners", 1623 "Can't reconnect listener for " + 1624 names[i]); 1625 } 1626 } 1627 1628 if (j != len) { 1629 ClientListenerInfo[] tmp = clis; 1630 clis = new ClientListenerInfo[j]; 1631 System.arraycopy(tmp, 0, clis, 0, j); 1632 } 1633 1634 rmiNotifClient.postReconnection(clis); 1635 } 1636 1637 protected void checkConnection() throws IOException { 1638 if (logger.debugOn()) 1639 logger.debug("RMIClientCommunicatorAdmin-checkConnection", 1640 "Calling the method getDefaultDomain."); 1641 1642 connection.getDefaultDomain(null); 1643 } 1644 1645 protected void doStart() throws IOException { 1646 // Get RMIServer stub from directory or URL encoding if needed. 1647 RMIServer stub; 1648 try { 1649 stub = (rmiServer!=null)?rmiServer: 1650 findRMIServer(jmxServiceURL, env); 1651 } catch (NamingException ne) { 1652 throw new IOException("Failed to get a RMI stub: "+ne); 1653 } 1654 1655 // Connect IIOP Stub if needed. 1656 stub = connectStub(stub,env); 1657 1658 // Calling newClient on the RMIServer stub. 1659 Object credentials = env.get(CREDENTIALS); 1660 connection = stub.newClient(credentials); 1661 1662 // notif issues 1663 final ClientListenerInfo[] old = rmiNotifClient.preReconnection(); 1664 1665 reconnectNotificationListeners(old); 1666 1667 connectionId = getConnectionId(); 1668 1669 Notification reconnectedNotif = 1670 new JMXConnectionNotification(JMXConnectionNotification.OPENED, 1671 this, 1672 connectionId, 1673 clientNotifSeqNo++, 1674 "Reconnected to server", 1675 null); 1676 sendNotification(reconnectedNotif); 1677 1678 } 1679 1680 protected void doStop() { 1681 try { 1682 close(); 1683 } catch (IOException ioe) { 1684 logger.warning("RMIClientCommunicatorAdmin-doStop", 1685 "Failed to call the method close():" + ioe); 1686 logger.debug("RMIClientCommunicatorAdmin-doStop",ioe); 1687 } 1688 } 1689 } 1690 1691 //-------------------------------------------------------------------- 1692 // Private stuff - Serialization 1693 //-------------------------------------------------------------------- 1694 /** 1695 * <p>In order to be usable, an IIOP stub must be connected to an ORB. 1696 * The stub is automatically connected to the ORB if: 1697 * <ul> 1698 * <li> It was returned by the COS naming</li> 1699 * <li> Its server counterpart has been registered in COS naming 1700 * through JNDI.</li> 1701 * </ul> 1702 * Otherwise, it is not connected. A stub which is deserialized 1703 * from Jini is not connected. A stub which is obtained from a 1704 * non registered RMIIIOPServerImpl is not a connected.<br> 1705 * A stub which is not connected can't be serialized, and thus 1706 * can't be registered in Jini. A stub which is not connected can't 1707 * be used to invoke methods on the server. 1708 * <p> 1709 * In order to palliate this, this method will connect the 1710 * given stub if it is not yet connected. If the given 1711 * <var>RMIServer</var> is not an instance of 1712 * {@link javax.rmi.CORBA.Stub javax.rmi.CORBA.Stub}, then the 1713 * method do nothing and simply returns that stub. Otherwise, 1714 * this method will attempt to connect the stub to an ORB as 1715 * follows: 1716 * <ul> 1717 * <li>This method looks in the provided <var>environment</var> for 1718 * the "java.naming.corba.orb" property. If it is found, the 1719 * referenced object (an {@link org.omg.CORBA.ORB ORB}) is used to 1720 * connect the stub. Otherwise, a new org.omg.CORBA.ORB is created 1721 * by calling {@link 1722 * org.omg.CORBA.ORB#init(String[], Properties) 1723 * org.omg.CORBA.ORB.init((String[])null,(Properties)null)}</li> 1724 * <li>The new created ORB is kept in a static 1725 * {@link WeakReference} and can be reused for connecting other 1726 * stubs. However, no reference is ever kept on the ORB provided 1727 * in the <var>environment</var> map, if any.</li> 1728 * </ul> 1729 * @param rmiServer A RMI Server Stub. 1730 * @param environment An environment map, possibly containing an ORB. 1731 * @return the given stub. 1732 * @exception IllegalArgumentException if the 1733 * <tt>java.naming.corba.orb</tt> property is specified and 1734 * does not point to an {@link org.omg.CORBA.ORB ORB}. 1735 * @exception IOException if the connection to the ORB failed. 1736 **/ 1737 static RMIServer connectStub(RMIServer rmiServer, 1738 Map<String, ?> environment) 1739 throws IOException { 1740 if (IIOPHelper.isStub(rmiServer)) { 1741 try { 1742 IIOPHelper.getOrb(rmiServer); 1743 } catch (UnsupportedOperationException x) { 1744 // BAD_OPERATION 1745 IIOPHelper.connect(rmiServer, resolveOrb(environment)); 1746 } 1747 } 1748 return rmiServer; 1749 } 1750 1751 /** 1752 * Get the ORB specified by <var>environment</var>, or create a 1753 * new one. 1754 * <p>This method looks in the provided <var>environment</var> for 1755 * the "java.naming.corba.orb" property. If it is found, the 1756 * referenced object (an {@link org.omg.CORBA.ORB ORB}) is 1757 * returned. Otherwise, a new org.omg.CORBA.ORB is created 1758 * by calling {@link 1759 * org.omg.CORBA.ORB#init(String[], java.util.Properties) 1760 * org.omg.CORBA.ORB.init((String[])null,(Properties)null)} 1761 * <p>The new created ORB is kept in a static 1762 * {@link WeakReference} and can be reused for connecting other 1763 * stubs. However, no reference is ever kept on the ORB provided 1764 * in the <var>environment</var> map, if any. 1765 * @param environment An environment map, possibly containing an ORB. 1766 * @return An ORB. 1767 * @exception IllegalArgumentException if the 1768 * <tt>java.naming.corba.orb</tt> property is specified and 1769 * does not point to an {@link org.omg.CORBA.ORB ORB}. 1770 * @exception IOException if the ORB initialization failed. 1771 **/ 1772 static Object resolveOrb(Map<String, ?> environment) 1773 throws IOException { 1774 if (environment != null) { 1775 final Object orb = environment.get(EnvHelp.DEFAULT_ORB); 1776 if (orb != null && !(IIOPHelper.isOrb(orb))) 1777 throw new IllegalArgumentException(EnvHelp.DEFAULT_ORB + 1778 " must be an instance of org.omg.CORBA.ORB."); 1779 if (orb != null) return orb; 1780 } 1781 final Object orb = 1782 (RMIConnector.orb==null)?null:RMIConnector.orb.get(); 1783 if (orb != null) return orb; 1784 1785 final Object newOrb = 1786 IIOPHelper.createOrb((String[])null, (Properties)null); 1787 RMIConnector.orb = new WeakReference<Object>(newOrb); 1788 return newOrb; 1789 } 1790 1791 /** 1792 * Read RMIConnector fields from an {@link java.io.ObjectInputStream 1793 * ObjectInputStream}. 1794 * Calls <code>s.defaultReadObject()</code> and then initializes 1795 * all transient variables that need initializing. 1796 * @param s The ObjectInputStream to read from. 1797 * @exception InvalidObjectException if none of <var>rmiServer</var> stub 1798 * or <var>jmxServiceURL</var> are set. 1799 * @see #RMIConnector(JMXServiceURL,Map) 1800 * @see #RMIConnector(RMIServer,Map) 1801 **/ 1802 private void readObject(java.io.ObjectInputStream s) 1803 throws IOException, ClassNotFoundException { 1804 s.defaultReadObject(); 1805 1806 if (rmiServer == null && jmxServiceURL == null) throw new 1807 InvalidObjectException("rmiServer and jmxServiceURL both null"); 1808 1809 initTransients(); 1810 } 1811 1812 /** 1813 * Writes the RMIConnector fields to an {@link java.io.ObjectOutputStream 1814 * ObjectOutputStream}. 1815 * <p>Connects the underlying RMIServer stub to an ORB, if needed, 1816 * before serializing it. This is done using the environment 1817 * map that was provided to the constructor, if any, and as documented 1818 * in {@link javax.management.remote.rmi}.</p> 1819 * <p>This method then calls <code>s.defaultWriteObject()</code>. 1820 * Usually, <var>rmiServer</var> is null if this object 1821 * was constructed with a JMXServiceURL, and <var>jmxServiceURL</var> 1822 * is null if this object is constructed with a RMIServer stub. 1823 * <p>Note that the environment Map is not serialized, since the objects 1824 * it contains are assumed to be contextual and relevant only 1825 * with respect to the local environment (class loader, ORB, etc...).</p> 1826 * <p>After an RMIConnector is deserialized, it is assumed that the 1827 * user will call {@link #connect(Map)}, providing a new Map that 1828 * can contain values which are contextually relevant to the new 1829 * local environment.</p> 1830 * <p>Since connection to the ORB is needed prior to serializing, and 1831 * since the ORB to connect to is one of those contextual parameters, 1832 * it is not recommended to re-serialize a just de-serialized object - 1833 * as the de-serialized object has no map. Thus, when an RMIConnector 1834 * object is needed for serialization or transmission to a remote 1835 * application, it is recommended to obtain a new RMIConnector stub 1836 * by calling {@link RMIConnectorServer#toJMXConnector(Map)}.</p> 1837 * @param s The ObjectOutputStream to write to. 1838 * @exception InvalidObjectException if none of <var>rmiServer</var> stub 1839 * or <var>jmxServiceURL</var> are set. 1840 * @see #RMIConnector(JMXServiceURL,Map) 1841 * @see #RMIConnector(RMIServer,Map) 1842 **/ 1843 private void writeObject(java.io.ObjectOutputStream s) 1844 throws IOException { 1845 if (rmiServer == null && jmxServiceURL == null) throw new 1846 InvalidObjectException("rmiServer and jmxServiceURL both null."); 1847 connectStub(this.rmiServer,env); 1848 s.defaultWriteObject(); 1849 } 1850 1851 // Initialization of transient variables. 1852 private void initTransients() { 1853 rmbscMap = new WeakHashMap<Subject, WeakReference<MBeanServerConnection>>(); 1854 connected = false; 1855 terminated = false; 1856 1857 connectionBroadcaster = new NotificationBroadcasterSupport(); 1858 } 1859 1860 //-------------------------------------------------------------------- 1861 // Private stuff - Check if stub can be trusted. 1862 //-------------------------------------------------------------------- 1863 1864 private static void checkStub(Remote stub, 1865 Class<?> stubClass) { 1866 1867 // Check remote stub is from the expected class. 1868 // 1869 if (stub.getClass() != stubClass) { 1870 if (!Proxy.isProxyClass(stub.getClass())) { 1871 throw new SecurityException( 1872 "Expecting a " + stubClass.getName() + " stub!"); 1873 } else { 1874 InvocationHandler handler = Proxy.getInvocationHandler(stub); 1875 if (handler.getClass() != RemoteObjectInvocationHandler.class) 1876 throw new SecurityException( 1877 "Expecting a dynamic proxy instance with a " + 1878 RemoteObjectInvocationHandler.class.getName() + 1879 " invocation handler!"); 1880 else 1881 stub = (Remote) handler; 1882 } 1883 } 1884 1885 // Check RemoteRef in stub is from the expected class 1886 // "sun.rmi.server.UnicastRef2". 1887 // 1888 RemoteRef ref = ((RemoteObject)stub).getRef(); 1889 if (ref.getClass() != UnicastRef2.class) 1890 throw new SecurityException( 1891 "Expecting a " + UnicastRef2.class.getName() + 1892 " remote reference in stub!"); 1893 1894 // Check RMIClientSocketFactory in stub is from the expected class 1895 // "javax.rmi.ssl.SslRMIClientSocketFactory". 1896 // 1897 LiveRef liveRef = ((UnicastRef2)ref).getLiveRef(); 1898 RMIClientSocketFactory csf = liveRef.getClientSocketFactory(); 1899 if (csf == null || csf.getClass() != SslRMIClientSocketFactory.class) 1900 throw new SecurityException( 1901 "Expecting a " + SslRMIClientSocketFactory.class.getName() + 1902 " RMI client socket factory in stub!"); 1903 } 1904 1905 //-------------------------------------------------------------------- 1906 // Private stuff - RMIServer creation 1907 //-------------------------------------------------------------------- 1908 1909 private RMIServer findRMIServer(JMXServiceURL directoryURL, 1910 Map<String, Object> environment) 1911 throws NamingException, IOException { 1912 final boolean isIiop = RMIConnectorServer.isIiopURL(directoryURL,true); 1913 if (isIiop) { 1914 // Make sure java.naming.corba.orb is in the Map. 1915 environment.put(EnvHelp.DEFAULT_ORB,resolveOrb(environment)); 1916 } 1917 1918 String path = directoryURL.getURLPath(); 1919 int end = path.indexOf(';'); 1920 if (end < 0) end = path.length(); 1921 if (path.startsWith("/jndi/")) 1922 return findRMIServerJNDI(path.substring(6,end), environment, isIiop); 1923 else if (path.startsWith("/stub/")) 1924 return findRMIServerJRMP(path.substring(6,end), environment, isIiop); 1925 else if (path.startsWith("/ior/")) { 1926 if (!IIOPHelper.isAvailable()) 1927 throw new IOException("iiop protocol not available"); 1928 return findRMIServerIIOP(path.substring(5,end), environment, isIiop); 1929 } else { 1930 final String msg = "URL path must begin with /jndi/ or /stub/ " + 1931 "or /ior/: " + path; 1932 throw new MalformedURLException(msg); 1933 } 1934 } 1935 1936 /** 1937 * Lookup the RMIServer stub in a directory. 1938 * @param jndiURL A JNDI URL indicating the location of the Stub 1939 * (see {@link javax.management.remote.rmi}), e.g.: 1940 * <ul><li><tt>rmi://registry-host:port/rmi-stub-name</tt></li> 1941 * <li>or <tt>iiop://cosnaming-host:port/iiop-stub-name</tt></li> 1942 * <li>or <tt>ldap://ldap-host:port/java-container-dn</tt></li> 1943 * </ul> 1944 * @param env the environment Map passed to the connector. 1945 * @param isIiop true if the stub is expected to be an IIOP stub. 1946 * @return The retrieved RMIServer stub. 1947 * @exception NamingException if the stub couldn't be found. 1948 **/ 1949 private RMIServer findRMIServerJNDI(String jndiURL, Map<String, ?> env, 1950 boolean isIiop) 1951 throws NamingException { 1952 1953 InitialContext ctx = new InitialContext(EnvHelp.mapToHashtable(env)); 1954 1955 Object objref = ctx.lookup(jndiURL); 1956 ctx.close(); 1957 1958 if (isIiop) 1959 return narrowIIOPServer(objref); 1960 else 1961 return narrowJRMPServer(objref); 1962 } 1963 1964 private static RMIServer narrowJRMPServer(Object objref) { 1965 1966 return (RMIServer) objref; 1967 } 1968 1969 private static RMIServer narrowIIOPServer(Object objref) { 1970 try { 1971 return IIOPHelper.narrow(objref, RMIServer.class); 1972 } catch (ClassCastException e) { 1973 if (logger.traceOn()) 1974 logger.trace("narrowIIOPServer","Failed to narrow objref=" + 1975 objref + ": " + e); 1976 if (logger.debugOn()) logger.debug("narrowIIOPServer",e); 1977 return null; 1978 } 1979 } 1980 1981 private RMIServer findRMIServerIIOP(String ior, Map<String, ?> env, boolean isIiop) { 1982 // could forbid "rmi:" URL here -- but do we need to? 1983 final Object orb = env.get(EnvHelp.DEFAULT_ORB); 1984 final Object stub = IIOPHelper.stringToObject(orb, ior); 1985 return IIOPHelper.narrow(stub, RMIServer.class); 1986 } 1987 1988 private RMIServer findRMIServerJRMP(String base64, Map<String, ?> env, boolean isIiop) 1989 throws IOException { 1990 // could forbid "iiop:" URL here -- but do we need to? 1991 final byte[] serialized; 1992 try { 1993 serialized = base64ToByteArray(base64); 1994 } catch (IllegalArgumentException e) { 1995 throw new MalformedURLException("Bad BASE64 encoding: " + 1996 e.getMessage()); 1997 } 1998 final ByteArrayInputStream bin = new ByteArrayInputStream(serialized); 1999 2000 final ClassLoader loader = EnvHelp.resolveClientClassLoader(env); 2001 final ObjectInputStream oin = 2002 (loader == null) ? 2003 new ObjectInputStream(bin) : 2004 new ObjectInputStreamWithLoader(bin, loader); 2005 final Object stub; 2006 try { 2007 stub = oin.readObject(); 2008 } catch (ClassNotFoundException e) { 2009 throw new MalformedURLException("Class not found: " + e); 2010 } 2011 return (RMIServer)stub; 2012 } 2013 2014 private static final class ObjectInputStreamWithLoader 2015 extends ObjectInputStream { 2016 ObjectInputStreamWithLoader(InputStream in, ClassLoader cl) 2017 throws IOException { 2018 super(in); 2019 this.loader = cl; 2020 } 2021 2022 @Override 2023 protected Class<?> resolveClass(ObjectStreamClass classDesc) 2024 throws IOException, ClassNotFoundException { 2025 String name = classDesc.getName(); 2026 ReflectUtil.checkPackageAccess(name); 2027 return Class.forName(name, false, loader); 2028 } 2029 2030 private final ClassLoader loader; 2031 } 2032 2033 private MBeanServerConnection getConnectionWithSubject(Subject delegationSubject) { 2034 MBeanServerConnection conn = null; 2035 2036 if (delegationSubject == null) { 2037 if (nullSubjectConnRef == null 2038 || (conn = nullSubjectConnRef.get()) == null) { 2039 conn = new RemoteMBeanServerConnection(null); 2040 nullSubjectConnRef = new WeakReference(conn); 2041 } 2042 } else { 2043 WeakReference<MBeanServerConnection> wr = rmbscMap.get(delegationSubject); 2044 if (wr == null || (conn = wr.get()) == null) { 2045 conn = new RemoteMBeanServerConnection(delegationSubject); 2046 rmbscMap.put(delegationSubject, new WeakReference(conn)); 2047 } 2048 } 2049 return conn; 2050 } 2051 2052 /* 2053 The following section of code avoids a class loading problem 2054 with RMI. The problem is that an RMI stub, when deserializing 2055 a remote method return value or exception, will first of all 2056 consult the first non-bootstrap class loader it finds in the 2057 call stack. This can lead to behavior that is not portable 2058 between implementations of the JMX Remote API. Notably, an 2059 implementation on J2SE 1.4 will find the RMI stub's loader on 2060 the stack. But in J2SE 5, this stub is loaded by the 2061 bootstrap loader, so RMI will find the loader of the user code 2062 that called an MBeanServerConnection method. 2063 2064 To avoid this problem, we take advantage of what the RMI stub 2065 is doing internally. Each remote call will end up calling 2066 ref.invoke(...), where ref is the RemoteRef parameter given to 2067 the RMI stub's constructor. It is within this call that the 2068 deserialization will happen. So we fabricate our own RemoteRef 2069 that delegates everything to the "real" one but that is loaded 2070 by a class loader that knows no other classes. The class 2071 loader NoCallStackClassLoader does this: the RemoteRef is an 2072 instance of the class named by proxyRefClassName, which is 2073 fabricated by the class loader using byte code that is defined 2074 by the string below. 2075 2076 The call stack when the deserialization happens is thus this: 2077 MBeanServerConnection.getAttribute (or whatever) 2078 -> RMIConnectionImpl_Stub.getAttribute 2079 -> ProxyRef.invoke(...getAttribute...) 2080 -> UnicastRef.invoke(...getAttribute...) 2081 -> internal RMI stuff 2082 2083 Here UnicastRef is the RemoteRef created when the stub was 2084 deserialized (which is of some RMI internal class). It and the 2085 "internal RMI stuff" are loaded by the bootstrap loader, so are 2086 transparent to the stack search. The first non-bootstrap 2087 loader found is our ProxyRefLoader, as required. 2088 2089 In a future version of this code as integrated into J2SE 5, 2090 this workaround could be replaced by direct access to the 2091 internals of RMI. For now, we use the same code base for J2SE 2092 and for the standalone Reference Implementation. 2093 2094 The byte code below encodes the following class, compiled using 2095 J2SE 1.4.2 with the -g:none option. 2096 2097 package com.sun.jmx.remote.internal; 2098 2099 import java.lang.reflect.Method; 2100 import java.rmi.Remote; 2101 import java.rmi.server.RemoteRef; 2102 import com.sun.jmx.remote.internal.ProxyRef; 2103 2104 public class PRef extends ProxyRef { 2105 public PRef(RemoteRef ref) { 2106 super(ref); 2107 } 2108 2109 public Object invoke(Remote obj, Method method, 2110 Object[] params, long opnum) 2111 throws Exception { 2112 return ref.invoke(obj, method, params, opnum); 2113 } 2114 } 2115 */ 2116 2117 private static final String rmiServerImplStubClassName = 2118 RMIServer.class.getName() + "Impl_Stub"; 2119 private static final Class<?> rmiServerImplStubClass; 2120 private static final String rmiConnectionImplStubClassName = 2121 RMIConnection.class.getName() + "Impl_Stub"; 2122 private static final Class<?> rmiConnectionImplStubClass; 2123 private static final String pRefClassName = 2124 "com.sun.jmx.remote.internal.PRef"; 2125 private static final Constructor<?> proxyRefConstructor; 2126 static { 2127 final String pRefByteCodeString = 2128 "\312\376\272\276\0\0\0.\0\27\12\0\5\0\15\11\0\4\0\16\13\0\17\0"+ 2129 "\20\7\0\21\7\0\22\1\0\6<init>\1\0\36(Ljava/rmi/server/RemoteRef;"+ 2130 ")V\1\0\4Code\1\0\6invoke\1\0S(Ljava/rmi/Remote;Ljava/lang/reflec"+ 2131 "t/Method;[Ljava/lang/Object;J)Ljava/lang/Object;\1\0\12Exception"+ 2132 "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/"+ 2133 "sun/jmx/remote/internal/PRef\1\0$com/sun/jmx/remote/internal/Pr"+ 2134 "oxyRef\1\0\23java/lang/Exception\1\0\3ref\1\0\33Ljava/rmi/serve"+ 2135 "r/RemoteRef;\1\0\31java/rmi/server/RemoteRef\0!\0\4\0\5\0\0\0\0"+ 2136 "\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"+ 2137 "\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"+ 2138 "\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"; 2139 final byte[] pRefByteCode = 2140 NoCallStackClassLoader.stringToBytes(pRefByteCodeString); 2141 PrivilegedExceptionAction<Constructor<?>> action = 2142 new PrivilegedExceptionAction<Constructor<?>>() { 2143 public Constructor<?> run() throws Exception { 2144 Class thisClass = RMIConnector.class; 2145 ClassLoader thisLoader = thisClass.getClassLoader(); 2146 ProtectionDomain thisProtectionDomain = 2147 thisClass.getProtectionDomain(); 2148 String[] otherClassNames = {ProxyRef.class.getName()}; 2149 ClassLoader cl = 2150 new NoCallStackClassLoader(pRefClassName, 2151 pRefByteCode, 2152 otherClassNames, 2153 thisLoader, 2154 thisProtectionDomain); 2155 Class<?> c = cl.loadClass(pRefClassName); 2156 return c.getConstructor(RemoteRef.class); 2157 } 2158 }; 2159 2160 Class<?> serverStubClass; 2161 try { 2162 serverStubClass = Class.forName(rmiServerImplStubClassName); 2163 } catch (Exception e) { 2164 logger.error("<clinit>", 2165 "Failed to instantiate " + 2166 rmiServerImplStubClassName + ": " + e); 2167 logger.debug("<clinit>",e); 2168 serverStubClass = null; 2169 } 2170 rmiServerImplStubClass = serverStubClass; 2171 2172 Class<?> stubClass; 2173 Constructor<?> constr; 2174 try { 2175 stubClass = Class.forName(rmiConnectionImplStubClassName); 2176 constr = (Constructor<?>) AccessController.doPrivileged(action); 2177 } catch (Exception e) { 2178 logger.error("<clinit>", 2179 "Failed to initialize proxy reference constructor "+ 2180 "for " + rmiConnectionImplStubClassName + ": " + e); 2181 logger.debug("<clinit>",e); 2182 stubClass = null; 2183 constr = null; 2184 } 2185 rmiConnectionImplStubClass = stubClass; 2186 proxyRefConstructor = constr; 2187 } 2188 2189 private static RMIConnection shadowJrmpStub(RemoteObject stub) 2190 throws InstantiationException, IllegalAccessException, 2191 InvocationTargetException, ClassNotFoundException, 2192 NoSuchMethodException { 2193 RemoteRef ref = stub.getRef(); 2194 RemoteRef proxyRef = (RemoteRef) 2195 proxyRefConstructor.newInstance(new Object[] {ref}); 2196 final Constructor<?> rmiConnectionImplStubConstructor = 2197 rmiConnectionImplStubClass.getConstructor(RemoteRef.class); 2198 Object[] args = {proxyRef}; 2199 RMIConnection proxyStub = (RMIConnection) 2200 rmiConnectionImplStubConstructor.newInstance(args); 2201 return proxyStub; 2202 } 2203 2204 /* 2205 The following code performs a similar trick for RMI/IIOP to the 2206 one described above for RMI/JRMP. Unlike JRMP, though, we 2207 can't easily insert an object between the RMIConnection stub 2208 and the RMI/IIOP deserialization code, as explained below. 2209 2210 A method in an RMI/IIOP stub does the following. It makes an 2211 org.omg.CORBA_2_3.portable.OutputStream for each request, and 2212 writes the parameters to it. Then it calls 2213 _invoke(OutputStream) which it inherits from CORBA's 2214 ObjectImpl. That returns an 2215 org.omg.CORBA_2_3.portable.InputStream. The return value is 2216 read from this InputStream. So the stack during 2217 deserialization looks like this: 2218 2219 MBeanServerConnection.getAttribute (or whatever) 2220 -> _RMIConnection_Stub.getAttribute 2221 -> Util.readAny (a CORBA method) 2222 -> InputStream.read_any 2223 -> internal CORBA stuff 2224 2225 What we would have *liked* to have done would be the same thing 2226 as for RMI/JRMP. We create a "ProxyDelegate" that is an 2227 org.omg.CORBA.portable.Delegate that simply forwards every 2228 operation to the real original Delegate from the RMIConnection 2229 stub, except that the InputStream returned by _invoke is 2230 wrapped by a "ProxyInputStream" that is loaded by our 2231 NoCallStackClassLoader. 2232 2233 Unfortunately, this doesn't work, at least with Sun's J2SE 2234 1.4.2, because the CORBA code is not designed to allow you to 2235 change Delegates arbitrarily. You get a ClassCastException 2236 from code that expects the Delegate to implement an internal 2237 interface. 2238 2239 So instead we do the following. We create a subclass of the 2240 stub that overrides the _invoke method so as to wrap the 2241 returned InputStream in a ProxyInputStream. We create a 2242 subclass of ProxyInputStream using the NoCallStackClassLoader 2243 and override its read_any and read_value(Class) methods. 2244 (These are the only methods called during deserialization of 2245 MBeanServerConnection return values.) We extract the Delegate 2246 from the original stub and insert it into our subclass stub, 2247 and away we go. The state of a stub consists solely of its 2248 Delegate. 2249 2250 We also need to catch ApplicationException, which will encode 2251 any exceptions declared in the throws clause of the called 2252 method. Its InputStream needs to be wrapped in a 2253 ProxyInputSteam too. 2254 2255 We override _releaseReply in the stub subclass so that it 2256 replaces a ProxyInputStream argument with the original 2257 InputStream. This avoids problems if the implementation of 2258 _releaseReply ends up casting this InputStream to an 2259 implementation-specific interface (which in Sun's J2SE 5 it 2260 does). 2261 2262 It is not strictly necessary for the stub subclass to be loaded 2263 by a NoCallStackClassLoader, since the call-stack search stops 2264 at the ProxyInputStream subclass. However, it is convenient 2265 for two reasons. One is that it means that the 2266 ProxyInputStream subclass can be accessed directly, without 2267 using reflection. The other is that it avoids build problems, 2268 since usually stubs are created after other classes are 2269 compiled, so we can't access them from this class without, 2270 again, using reflection. 2271 2272 The strings below encode the following two Java classes, 2273 compiled using javac -g:none. 2274 2275 package com.sun.jmx.remote.protocol.iiop; 2276 2277 import org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub; 2278 2279 import org.omg.CORBA.portable.ApplicationException; 2280 import org.omg.CORBA.portable.InputStream; 2281 import org.omg.CORBA.portable.OutputStream; 2282 import org.omg.CORBA.portable.RemarshalException; 2283 2284 public class ProxyStub extends _RMIConnection_Stub { 2285 public InputStream _invoke(OutputStream out) 2286 throws ApplicationException, RemarshalException { 2287 try { 2288 return new PInputStream(super._invoke(out)); 2289 } catch (ApplicationException e) { 2290 InputStream pis = new PInputStream(e.getInputStream()); 2291 throw new ApplicationException(e.getId(), pis); 2292 } 2293 } 2294 2295 public void _releaseReply(InputStream in) { 2296 if (in != null) 2297 in = ((PInputStream)in).getProxiedInputStream(); 2298 super._releaseReply(in); 2299 } 2300 } 2301 2302 package com.sun.jmx.remote.protocol.iiop; 2303 2304 public class PInputStream extends ProxyInputStream { 2305 public PInputStream(org.omg.CORBA.portable.InputStream in) { 2306 super(in); 2307 } 2308 2309 public org.omg.CORBA.Any read_any() { 2310 return in.read_any(); 2311 } 2312 2313 public java.io.Serializable read_value(Class clz) { 2314 return narrow().read_value(clz); 2315 } 2316 } 2317 2318 2319 */ 2320 private static final String iiopConnectionStubClassName = 2321 "org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub"; 2322 private static final String proxyStubClassName = 2323 "com.sun.jmx.remote.protocol.iiop.ProxyStub"; 2324 private static final String ProxyInputStreamClassName = 2325 "com.sun.jmx.remote.protocol.iiop.ProxyInputStream"; 2326 private static final String pInputStreamClassName = 2327 "com.sun.jmx.remote.protocol.iiop.PInputStream"; 2328 private static final Class<?> proxyStubClass; 2329 static { 2330 final String proxyStubByteCodeString = 2331 "\312\376\272\276\0\0\0\63\0+\12\0\14\0\30\7\0\31\12\0\14\0\32\12"+ 2332 "\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 "+ 2333 "\12\0\14\0!\7\0\"\7\0#\1\0\6<init>\1\0\3()V\1\0\4Code\1\0\7_in"+ 2334 "voke\1\0K(Lorg/omg/CORBA/portable/OutputStream;)Lorg/omg/CORBA"+ 2335 "/portable/InputStream;\1\0\15StackMapTable\7\0\34\1\0\12Except"+ 2336 "ions\7\0$\1\0\15_releaseReply\1\0'(Lorg/omg/CORBA/portable/Inp"+ 2337 "utStream;)V\14\0\15\0\16\1\0-com/sun/jmx/remote/protocol/iiop/"+ 2338 "PInputStream\14\0\20\0\21\14\0\15\0\27\1\0+org/omg/CORBA/porta"+ 2339 "ble/ApplicationException\14\0%\0&\14\0'\0(\14\0\15\0)\14\0*\0&"+ 2340 "\14\0\26\0\27\1\0*com/sun/jmx/remote/protocol/iiop/ProxyStub\1"+ 2341 "\0<org/omg/stub/javax/management/remote/rmi/_RMIConnection_Stu"+ 2342 "b\1\0)org/omg/CORBA/portable/RemarshalException\1\0\16getInput"+ 2343 "Stream\1\0&()Lorg/omg/CORBA/portable/InputStream;\1\0\5getId\1"+ 2344 "\0\24()Ljava/lang/String;\1\09(Ljava/lang/String;Lorg/omg/CORB"+ 2345 "A/portable/InputStream;)V\1\0\25getProxiedInputStream\0!\0\13\0"+ 2346 "\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"+ 2347 "*\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"+ 2348 "\0'\273\0\2Y*+\267\0\3\267\0\4\260M\273\0\2Y,\266\0\6\267\0\4N"+ 2349 "\273\0\5Y,\266\0\7-\267\0\10\277\0\1\0\0\0\14\0\15\0\5\0\1\0\22"+ 2350 "\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"+ 2351 "\1\0\17\0\0\0'\0\2\0\2\0\0\0\22+\306\0\13+\300\0\2\266\0\11L*+"+ 2352 "\267\0\12\261\0\0\0\1\0\22\0\0\0\3\0\1\14\0\0"; 2353 final String pInputStreamByteCodeString = 2354 "\312\376\272\276\0\0\0\63\0\36\12\0\7\0\17\11\0\6\0\20\12\0\21"+ 2355 "\0\22\12\0\6\0\23\12\0\24\0\25\7\0\26\7\0\27\1\0\6<init>\1\0'("+ 2356 "Lorg/omg/CORBA/portable/InputStream;)V\1\0\4Code\1\0\10read_an"+ 2357 "y\1\0\25()Lorg/omg/CORBA/Any;\1\0\12read_value\1\0)(Ljava/lang"+ 2358 "/Class;)Ljava/io/Serializable;\14\0\10\0\11\14\0\30\0\31\7\0\32"+ 2359 "\14\0\13\0\14\14\0\33\0\34\7\0\35\14\0\15\0\16\1\0-com/sun/jmx"+ 2360 "/remote/protocol/iiop/PInputStream\1\0\61com/sun/jmx/remote/pr"+ 2361 "otocol/iiop/ProxyInputStream\1\0\2in\1\0$Lorg/omg/CORBA/portab"+ 2362 "le/InputStream;\1\0\"org/omg/CORBA/portable/InputStream\1\0\6n"+ 2363 "arrow\1\0*()Lorg/omg/CORBA_2_3/portable/InputStream;\1\0&org/o"+ 2364 "mg/CORBA_2_3/portable/InputStream\0!\0\6\0\7\0\0\0\0\0\3\0\1\0"+ 2365 "\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"+ 2366 "\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"+ 2367 "\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"+ 2368 "\11*\266\0\4+\266\0\5\260\0\0\0\0\0\0"; 2369 final byte[] proxyStubByteCode = 2370 NoCallStackClassLoader.stringToBytes(proxyStubByteCodeString); 2371 final byte[] pInputStreamByteCode = 2372 NoCallStackClassLoader.stringToBytes(pInputStreamByteCodeString); 2373 final String[] classNames={proxyStubClassName, pInputStreamClassName}; 2374 final byte[][] byteCodes = {proxyStubByteCode, pInputStreamByteCode}; 2375 final String[] otherClassNames = { 2376 iiopConnectionStubClassName, 2377 ProxyInputStreamClassName, 2378 }; 2379 if (IIOPHelper.isAvailable()) { 2380 PrivilegedExceptionAction<Class<?>> action = 2381 new PrivilegedExceptionAction<Class<?>>() { 2382 public Class<?> run() throws Exception { 2383 Class thisClass = RMIConnector.class; 2384 ClassLoader thisLoader = thisClass.getClassLoader(); 2385 ProtectionDomain thisProtectionDomain = 2386 thisClass.getProtectionDomain(); 2387 ClassLoader cl = 2388 new NoCallStackClassLoader(classNames, 2389 byteCodes, 2390 otherClassNames, 2391 thisLoader, 2392 thisProtectionDomain); 2393 return cl.loadClass(proxyStubClassName); 2394 } 2395 }; 2396 Class<?> stubClass; 2397 try { 2398 stubClass = AccessController.doPrivileged(action); 2399 } catch (Exception e) { 2400 logger.error("<clinit>", 2401 "Unexpected exception making shadow IIOP stub class: "+e); 2402 logger.debug("<clinit>",e); 2403 stubClass = null; 2404 } 2405 proxyStubClass = stubClass; 2406 } else { 2407 proxyStubClass = null; 2408 } 2409 } 2410 2411 private static RMIConnection shadowIiopStub(Object stub) 2412 throws InstantiationException, IllegalAccessException { 2413 Object proxyStub = null; 2414 try { 2415 proxyStub = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() { 2416 public Object run() throws Exception { 2417 return proxyStubClass.newInstance(); 2418 } 2419 }); 2420 } catch (PrivilegedActionException e) { 2421 throw new InternalError(); 2422 } 2423 IIOPHelper.setDelegate(proxyStub, IIOPHelper.getDelegate(stub)); 2424 return (RMIConnection) proxyStub; 2425 } 2426 private static RMIConnection getConnection(RMIServer server, 2427 Object credentials, 2428 boolean checkStub) 2429 throws IOException { 2430 RMIConnection c = server.newClient(credentials); 2431 if (checkStub) checkStub(c, rmiConnectionImplStubClass); 2432 try { 2433 if (c.getClass() == rmiConnectionImplStubClass) 2434 return shadowJrmpStub((RemoteObject) c); 2435 if (c.getClass().getName().equals(iiopConnectionStubClassName)) 2436 return shadowIiopStub(c); 2437 logger.trace("getConnection", 2438 "Did not wrap " + c.getClass() + " to foil " + 2439 "stack search for classes: class loading semantics " + 2440 "may be incorrect"); 2441 } catch (Exception e) { 2442 logger.error("getConnection", 2443 "Could not wrap " + c.getClass() + " to foil " + 2444 "stack search for classes: class loading semantics " + 2445 "may be incorrect: " + e); 2446 logger.debug("getConnection",e); 2447 // so just return the original stub, which will work for all 2448 // but the most exotic class loading situations 2449 } 2450 return c; 2451 } 2452 2453 private static byte[] base64ToByteArray(String s) { 2454 int sLen = s.length(); 2455 int numGroups = sLen/4; 2456 if (4*numGroups != sLen) 2457 throw new IllegalArgumentException( 2458 "String length must be a multiple of four."); 2459 int missingBytesInLastGroup = 0; 2460 int numFullGroups = numGroups; 2461 if (sLen != 0) { 2462 if (s.charAt(sLen-1) == '=') { 2463 missingBytesInLastGroup++; 2464 numFullGroups--; 2465 } 2466 if (s.charAt(sLen-2) == '=') 2467 missingBytesInLastGroup++; 2468 } 2469 byte[] result = new byte[3*numGroups - missingBytesInLastGroup]; 2470 2471 // Translate all full groups from base64 to byte array elements 2472 int inCursor = 0, outCursor = 0; 2473 for (int i=0; i<numFullGroups; i++) { 2474 int ch0 = base64toInt(s.charAt(inCursor++)); 2475 int ch1 = base64toInt(s.charAt(inCursor++)); 2476 int ch2 = base64toInt(s.charAt(inCursor++)); 2477 int ch3 = base64toInt(s.charAt(inCursor++)); 2478 result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4)); 2479 result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); 2480 result[outCursor++] = (byte) ((ch2 << 6) | ch3); 2481 } 2482 2483 // Translate partial group, if present 2484 if (missingBytesInLastGroup != 0) { 2485 int ch0 = base64toInt(s.charAt(inCursor++)); 2486 int ch1 = base64toInt(s.charAt(inCursor++)); 2487 result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4)); 2488 2489 if (missingBytesInLastGroup == 1) { 2490 int ch2 = base64toInt(s.charAt(inCursor++)); 2491 result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2)); 2492 } 2493 } 2494 // assert inCursor == s.length()-missingBytesInLastGroup; 2495 // assert outCursor == result.length; 2496 return result; 2497 } 2498 2499 /** 2500 * Translates the specified character, which is assumed to be in the 2501 * "Base 64 Alphabet" into its equivalent 6-bit positive integer. 2502 * 2503 * @throws IllegalArgumentException if 2504 * c is not in the Base64 Alphabet. 2505 */ 2506 private static int base64toInt(char c) { 2507 int result; 2508 2509 if (c >= base64ToInt.length) 2510 result = -1; 2511 else 2512 result = base64ToInt[c]; 2513 2514 if (result < 0) 2515 throw new IllegalArgumentException("Illegal character " + c); 2516 return result; 2517 } 2518 2519 /** 2520 * This array is a lookup table that translates unicode characters 2521 * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045) 2522 * into their 6-bit positive integer equivalents. Characters that 2523 * are not in the Base64 alphabet but fall within the bounds of the 2524 * array are translated to -1. 2525 */ 2526 private static final byte base64ToInt[] = { 2527 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2528 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2529 -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 2530 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 2531 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 2532 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 2533 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 2534 }; 2535 2536 //-------------------------------------------------------------------- 2537 // Private stuff - Find / Set default class loader 2538 //-------------------------------------------------------------------- 2539 private ClassLoader pushDefaultClassLoader() { 2540 final Thread t = Thread.currentThread(); 2541 final ClassLoader old = t.getContextClassLoader(); 2542 if (defaultClassLoader != null) 2543 AccessController.doPrivileged(new PrivilegedAction<Void>() { 2544 public Void run() { 2545 t.setContextClassLoader(defaultClassLoader); 2546 return null; 2547 } 2548 }); 2549 return old; 2550 } 2551 2552 private void popDefaultClassLoader(final ClassLoader old) { 2553 AccessController.doPrivileged(new PrivilegedAction<Void>() { 2554 public Void run() { 2555 Thread.currentThread().setContextClassLoader(old); 2556 return null; 2557 } 2558 }); 2559 } 2560 2561 //-------------------------------------------------------------------- 2562 // Private variables 2563 //-------------------------------------------------------------------- 2564 /** 2565 * @serial The RMIServer stub of the RMI JMX Connector server to 2566 * which this client connector is (or will be) connected. This 2567 * field can be null when <var>jmxServiceURL</var> is not 2568 * null. This includes the case where <var>jmxServiceURL</var> 2569 * contains a serialized RMIServer stub. If both 2570 * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then 2571 * serialization will fail. 2572 * 2573 * @see #RMIConnector(RMIServer,Map) 2574 **/ 2575 private final RMIServer rmiServer; 2576 2577 /** 2578 * @serial The JMXServiceURL of the RMI JMX Connector server to 2579 * which this client connector will be connected. This field can 2580 * be null when <var>rmiServer</var> is not null. If both 2581 * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then 2582 * serialization will fail. 2583 * 2584 * @see #RMIConnector(JMXServiceURL,Map) 2585 **/ 2586 private final JMXServiceURL jmxServiceURL; 2587 2588 // --------------------------------------------------------- 2589 // WARNING - WARNING - WARNING - WARNING - WARNING - WARNING 2590 // --------------------------------------------------------- 2591 // Any transient variable which needs to be initialized should 2592 // be initialized in the method initTransient() 2593 private transient Map<String, Object> env; 2594 private transient ClassLoader defaultClassLoader; 2595 private transient RMIConnection connection; 2596 private transient String connectionId; 2597 2598 private transient long clientNotifSeqNo = 0; 2599 2600 private transient WeakHashMap<Subject, WeakReference<MBeanServerConnection>> rmbscMap; 2601 private transient WeakReference<MBeanServerConnection> nullSubjectConnRef = null; 2602 2603 private transient RMINotifClient rmiNotifClient; 2604 // = new RMINotifClient(new Integer(0)); 2605 2606 private transient long clientNotifCounter = 0; 2607 2608 private transient boolean connected; 2609 // = false; 2610 private transient boolean terminated; 2611 // = false; 2612 2613 private transient Exception closeException; 2614 2615 private transient NotificationBroadcasterSupport connectionBroadcaster; 2616 2617 private transient ClientCommunicatorAdmin communicatorAdmin; 2618 2619 /** 2620 * A static WeakReference to an {@link org.omg.CORBA.ORB ORB} to 2621 * connect unconnected stubs. 2622 **/ 2623 private static volatile WeakReference<Object> orb = null; 2624 2625 // TRACES & DEBUG 2626 //--------------- 2627 private static String objects(final Object[] objs) { 2628 if (objs == null) 2629 return "null"; 2630 else 2631 return Arrays.asList(objs).toString(); 2632 } 2633 2634 private static String strings(final String[] strs) { 2635 return objects(strs); 2636 } 2637 2638 static String getAttributesNames(AttributeList attributes) { 2639 return attributes != null ? 2640 attributes.asList().stream() 2641 .map(Attribute::getName) 2642 .collect(Collectors.joining(", ", "[", "]")) 2643 : "[]"; 2644 } 2645 }