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