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