0001 /*
0002 * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.management.remote.rmi;
0027
0028 import com.sun.jmx.remote.internal.ClientCommunicatorAdmin;
0029 import com.sun.jmx.remote.internal.ClientListenerInfo;
0030 import com.sun.jmx.remote.internal.ClientNotifForwarder;
0031 import com.sun.jmx.remote.internal.ProxyInputStream;
0032 import com.sun.jmx.remote.internal.ProxyRef;
0033 import com.sun.jmx.remote.util.ClassLogger;
0034 import com.sun.jmx.remote.util.EnvHelp;
0035 import java.io.ByteArrayInputStream;
0036 import java.io.IOException;
0037 import java.io.InputStream;
0038 import java.io.InvalidObjectException;
0039 import java.io.NotSerializableException;
0040 import java.io.ObjectInputStream;
0041 import java.io.ObjectStreamClass;
0042 import java.io.Serializable;
0043 import java.io.WriteAbortedException;
0044 import java.lang.ref.WeakReference;
0045 import java.lang.reflect.Constructor;
0046 import java.lang.reflect.InvocationHandler;
0047 import java.lang.reflect.InvocationTargetException;
0048 import java.lang.reflect.Proxy;
0049 import java.net.MalformedURLException;
0050 import java.rmi.MarshalException;
0051 import java.rmi.MarshalledObject;
0052 import java.rmi.NoSuchObjectException;
0053 import java.rmi.Remote;
0054 import java.rmi.ServerException;
0055 import java.rmi.UnmarshalException;
0056 import java.rmi.server.RMIClientSocketFactory;
0057 import java.rmi.server.RemoteObject;
0058 import java.rmi.server.RemoteObjectInvocationHandler;
0059 import java.rmi.server.RemoteRef;
0060 import java.security.AccessController;
0061 import java.security.PrivilegedAction;
0062 import java.security.PrivilegedExceptionAction;
0063 import java.security.ProtectionDomain;
0064 import java.util.Arrays;
0065 import java.util.Collections;
0066 import java.util.HashMap;
0067 import java.util.Map;
0068 import java.util.Properties;
0069 import java.util.Set;
0070 import java.util.WeakHashMap;
0071 import javax.management.Attribute;
0072 import javax.management.AttributeList;
0073 import javax.management.AttributeNotFoundException;
0074 import javax.management.InstanceAlreadyExistsException;
0075 import javax.management.InstanceNotFoundException;
0076 import javax.management.IntrospectionException;
0077 import javax.management.InvalidAttributeValueException;
0078 import javax.management.ListenerNotFoundException;
0079 import javax.management.MBeanException;
0080 import javax.management.MBeanInfo;
0081 import javax.management.MBeanRegistrationException;
0082 import javax.management.MBeanServerConnection;
0083 import javax.management.MBeanServerDelegate;
0084 import javax.management.MBeanServerNotification;
0085 import javax.management.NotCompliantMBeanException;
0086 import javax.management.Notification;
0087 import javax.management.NotificationBroadcasterSupport;
0088 import javax.management.NotificationFilter;
0089 import javax.management.NotificationFilterSupport;
0090 import javax.management.NotificationListener;
0091 import javax.management.ObjectInstance;
0092 import javax.management.ObjectName;
0093 import javax.management.QueryExp;
0094 import javax.management.ReflectionException;
0095 import javax.management.remote.JMXConnectionNotification;
0096 import javax.management.remote.JMXConnector;
0097 import javax.management.remote.JMXConnectorFactory;
0098 import javax.management.remote.JMXServiceURL;
0099 import javax.management.remote.NotificationResult;
0100 import javax.management.remote.JMXAddressable;
0101 import javax.naming.InitialContext;
0102 import javax.naming.NamingException;
0103 import javax.rmi.CORBA.Stub;
0104 import javax.rmi.PortableRemoteObject;
0105 import javax.rmi.ssl.SslRMIClientSocketFactory;
0106 import javax.security.auth.Subject;
0107 import org.omg.CORBA.BAD_OPERATION;
0108 import org.omg.CORBA.ORB;
0109 import sun.rmi.server.UnicastRef2;
0110 import sun.rmi.transport.LiveRef;
0111
0112 /**
0113 * <p>A connection to a remote RMI connector. Usually, such
0114 * connections are made using {@link
0115 * javax.management.remote.JMXConnectorFactory JMXConnectorFactory}.
0116 * However, specialized applications can use this class directly, for
0117 * example with an {@link RMIServer} stub obtained without going
0118 * through JNDI.</p>
0119 *
0120 * @since 1.5
0121 */
0122 public class RMIConnector implements JMXConnector, Serializable,
0123 JMXAddressable {
0124
0125 private static final ClassLogger logger = new ClassLogger(
0126 "javax.management.remote.rmi", "RMIConnector");
0127
0128 private static final long serialVersionUID = 817323035842634473L;
0129
0130 private RMIConnector(RMIServer rmiServer, JMXServiceURL address,
0131 Map<String, ?> environment) {
0132 if (rmiServer == null && address == null)
0133 throw new IllegalArgumentException(
0134 "rmiServer and jmxServiceURL both null");
0135
0136 initTransients();
0137
0138 this .rmiServer = rmiServer;
0139 this .jmxServiceURL = address;
0140 if (environment == null) {
0141 this .env = Collections.emptyMap();
0142 } else {
0143 EnvHelp.checkAttributes(environment);
0144 this .env = Collections.unmodifiableMap(environment);
0145 }
0146 }
0147
0148 /**
0149 * <p>Constructs an <code>RMIConnector</code> that will connect
0150 * the RMI connector server with the given address.</p>
0151 *
0152 * <p>The address can refer directly to the connector server,
0153 * using one of the following syntaxes:</p>
0154 *
0155 * <pre>
0156 * service:jmx:rmi://<em>[host[:port]]</em>/stub/<em>encoded-stub</em>
0157 * service:jmx:iiop://<em>[host[:port]]</em>/ior/<em>encoded-IOR</em>
0158 * </pre>
0159 *
0160 * <p>(Here, the square brackets <code>[]</code> are not part of the
0161 * address but indicate that the host and port are optional.)</p>
0162 *
0163 * <p>The address can instead indicate where to find an RMI stub
0164 * through JNDI, using one of the following syntaxes:</p>
0165 *
0166 * <pre>
0167 * service:jmx:rmi://<em>[host[:port]]</em>/jndi/<em>jndi-name</em>
0168 * service:jmx:iiop://<em>[host[:port]]</em>/jndi/<em>jndi-name</em>
0169 * </pre>
0170 *
0171 * <p>An implementation may also recognize additional address
0172 * syntaxes, for example:</p>
0173 *
0174 * <pre>
0175 * service:jmx:iiop://<em>[host[:port]]</em>/stub/<em>encoded-stub</em>
0176 * </pre>
0177 *
0178 * @param url the address of the RMI connector server.
0179 *
0180 * @param environment additional attributes specifying how to make
0181 * the connection. For JNDI-based addresses, these attributes can
0182 * usefully include JNDI attributes recognized by {@link
0183 * InitialContext#InitialContext(Hashtable) InitialContext}. This
0184 * parameter can be null, which is equivalent to an empty Map.
0185 *
0186 * @exception IllegalArgumentException if <code>url</code>
0187 * is null.
0188 */
0189 public RMIConnector(JMXServiceURL url, Map<String, ?> environment) {
0190 this (null, url, environment);
0191 }
0192
0193 /**
0194 * <p>Constructs an <code>RMIConnector</code> using the given RMI stub.
0195 *
0196 * @param rmiServer an RMI stub representing the RMI connector server.
0197 * @param environment additional attributes specifying how to make
0198 * the connection. This parameter can be null, which is
0199 * equivalent to an empty Map.
0200 *
0201 * @exception IllegalArgumentException if <code>rmiServer</code>
0202 * is null.
0203 */
0204 public RMIConnector(RMIServer rmiServer, Map<String, ?> environment) {
0205 this (rmiServer, null, environment);
0206 }
0207
0208 /**
0209 * <p>Returns a string representation of this object. In general,
0210 * the <code>toString</code> method returns a string that
0211 * "textually represents" this object. The result should be a
0212 * concise but informative representation that is easy for a
0213 * person to read.</p>
0214 *
0215 * @return a String representation of this object.
0216 **/
0217 public String toString() {
0218 final StringBuilder b = new StringBuilder(this .getClass()
0219 .getName());
0220 b.append(":");
0221 if (rmiServer != null) {
0222 b.append(" rmiServer=").append(rmiServer.toString());
0223 }
0224 if (jmxServiceURL != null) {
0225 if (rmiServer != null)
0226 b.append(",");
0227 b.append(" jmxServiceURL=")
0228 .append(jmxServiceURL.toString());
0229 }
0230 return b.toString();
0231 }
0232
0233 /**
0234 * <p>The address of this connector.</p>
0235 *
0236 * @return the address of this connector, or null if it
0237 * does not have one.
0238 *
0239 * @since 1.6
0240 */
0241 public JMXServiceURL getAddress() {
0242 return jmxServiceURL;
0243 }
0244
0245 //--------------------------------------------------------------------
0246 // implements JMXConnector interface
0247 //--------------------------------------------------------------------
0248 public void connect() throws IOException {
0249 connect(null);
0250 }
0251
0252 public synchronized void connect(Map<String, ?> environment)
0253 throws IOException {
0254 final boolean tracing = logger.traceOn();
0255 String idstr = (tracing ? "[" + this .toString() + "]" : null);
0256
0257 if (terminated) {
0258 logger.trace("connect", idstr + " already closed.");
0259 throw new IOException("Connector closed");
0260 }
0261 if (connected) {
0262 logger.trace("connect", idstr + " already connected.");
0263 return;
0264 }
0265
0266 try {
0267 if (tracing)
0268 logger.trace("connect", idstr + " connecting...");
0269
0270 final Map<String, Object> usemap = new HashMap<String, Object>(
0271 (this .env == null) ? Collections
0272 .<String, Object> emptyMap() : this .env);
0273
0274 if (environment != null) {
0275 EnvHelp.checkAttributes(environment);
0276 usemap.putAll(environment);
0277 }
0278
0279 // Get RMIServer stub from directory or URL encoding if needed.
0280 if (tracing)
0281 logger.trace("connect", idstr + " finding stub...");
0282 RMIServer stub = (rmiServer != null) ? rmiServer
0283 : findRMIServer(jmxServiceURL, usemap);
0284
0285 // Check for secure RMIServer stub if the corresponding
0286 // client-side environment property is set to "true".
0287 //
0288 boolean checkStub = EnvHelp.computeBooleanFromString(
0289 usemap, "jmx.remote.x.check.stub");
0290 if (checkStub)
0291 checkStub(stub, rmiServerImplStubClass);
0292
0293 // Connect IIOP Stub if needed.
0294 if (tracing)
0295 logger.trace("connect", idstr + " connecting stub...");
0296 stub = connectStub(stub, usemap);
0297 idstr = (tracing ? "[" + this .toString() + "]" : null);
0298
0299 // Calling newClient on the RMIServer stub.
0300 if (tracing)
0301 logger.trace("connect", idstr
0302 + " getting connection...");
0303 Object credentials = usemap.get(CREDENTIALS);
0304 connection = getConnection(stub, credentials, checkStub);
0305
0306 // Always use one of:
0307 // ClassLoader provided in Map at connect time,
0308 // or contextClassLoader at connect time.
0309 if (tracing)
0310 logger.trace("connect", idstr
0311 + " getting class loader...");
0312 defaultClassLoader = EnvHelp
0313 .resolveClientClassLoader(usemap);
0314
0315 usemap.put(JMXConnectorFactory.DEFAULT_CLASS_LOADER,
0316 defaultClassLoader);
0317
0318 rmiNotifClient = new RMINotifClient(defaultClassLoader,
0319 usemap);
0320
0321 env = usemap;
0322 final long checkPeriod = EnvHelp
0323 .getConnectionCheckPeriod(usemap);
0324 communicatorAdmin = new RMIClientCommunicatorAdmin(
0325 checkPeriod);
0326
0327 connected = true;
0328
0329 // The connectionId variable is used in doStart(), when
0330 // reconnecting, to identify the "old" connection.
0331 //
0332 connectionId = getConnectionId();
0333
0334 Notification connectedNotif = new JMXConnectionNotification(
0335 JMXConnectionNotification.OPENED, this ,
0336 connectionId, clientNotifSeqNo++,
0337 "Successful connection", null);
0338 sendNotification(connectedNotif);
0339
0340 if (tracing)
0341 logger.trace("connect", idstr + " done...");
0342 } catch (IOException e) {
0343 if (tracing)
0344 logger.trace("connect", idstr + " failed to connect: "
0345 + e);
0346 throw e;
0347 } catch (RuntimeException e) {
0348 if (tracing)
0349 logger.trace("connect", idstr + " failed to connect: "
0350 + e);
0351 throw e;
0352 } catch (NamingException e) {
0353 final String msg = "Failed to retrieve RMIServer stub: "
0354 + e;
0355 if (tracing)
0356 logger.trace("connect", idstr + " " + msg);
0357 throw EnvHelp.initCause(new IOException(msg), e);
0358 }
0359 }
0360
0361 public synchronized String getConnectionId() throws IOException {
0362 if (terminated || !connected) {
0363 if (logger.traceOn())
0364 logger.trace("getConnectionId", "[" + this .toString()
0365 + "] not connected.");
0366
0367 throw new IOException("Not connected");
0368 }
0369
0370 // we do a remote call to have an IOException if the connection is broken.
0371 // see the bug 4939578
0372 return connection.getConnectionId();
0373 }
0374
0375 public synchronized MBeanServerConnection getMBeanServerConnection()
0376 throws IOException {
0377 return getMBeanServerConnection(null);
0378 }
0379
0380 public synchronized MBeanServerConnection getMBeanServerConnection(
0381 Subject delegationSubject) throws IOException {
0382
0383 if (terminated) {
0384 if (logger.traceOn())
0385 logger.trace("getMBeanServerConnection", "["
0386 + this .toString() + "] already closed.");
0387 throw new IOException("Connection closed");
0388 } else if (!connected) {
0389 if (logger.traceOn())
0390 logger.trace("getMBeanServerConnection", "["
0391 + this .toString() + "] is not connected.");
0392 throw new IOException("Not connected");
0393 }
0394
0395 MBeanServerConnection mbsc = rmbscMap.get(delegationSubject);
0396 if (mbsc != null)
0397 return mbsc;
0398
0399 mbsc = new RemoteMBeanServerConnection(delegationSubject);
0400 rmbscMap.put(delegationSubject, mbsc);
0401 return mbsc;
0402 }
0403
0404 public void addConnectionNotificationListener(
0405 NotificationListener listener, NotificationFilter filter,
0406 Object handback) {
0407 if (listener == null)
0408 throw new NullPointerException("listener");
0409 connectionBroadcaster.addNotificationListener(listener, filter,
0410 handback);
0411 }
0412
0413 public void removeConnectionNotificationListener(
0414 NotificationListener listener)
0415 throws ListenerNotFoundException {
0416 if (listener == null)
0417 throw new NullPointerException("listener");
0418 connectionBroadcaster.removeNotificationListener(listener);
0419 }
0420
0421 public void removeConnectionNotificationListener(
0422 NotificationListener listener, NotificationFilter filter,
0423 Object handback) throws ListenerNotFoundException {
0424 if (listener == null)
0425 throw new NullPointerException("listener");
0426 connectionBroadcaster.removeNotificationListener(listener,
0427 filter, handback);
0428 }
0429
0430 private void sendNotification(Notification n) {
0431 connectionBroadcaster.sendNotification(n);
0432 }
0433
0434 public synchronized void close() throws IOException {
0435 close(false);
0436 }
0437
0438 // allows to do close after setting the flag "terminated" to true.
0439 // It is necessary to avoid a deadlock, see 6296324
0440 private synchronized void close(boolean intern) throws IOException {
0441 final boolean tracing = logger.traceOn();
0442 final boolean debug = logger.debugOn();
0443 final String idstr = (tracing ? "[" + this .toString() + "]"
0444 : null);
0445
0446 if (!intern) {
0447 // Return if already cleanly closed.
0448 //
0449 if (terminated) {
0450 if (closeException == null) {
0451 if (tracing)
0452 logger.trace("close", idstr
0453 + " already closed.");
0454 return;
0455 }
0456 } else {
0457 terminated = true;
0458 }
0459 }
0460
0461 if (closeException != null && tracing) {
0462 // Already closed, but not cleanly. Attempt again.
0463 //
0464 if (tracing) {
0465 logger.trace("close", idstr + " had failed: "
0466 + closeException);
0467 logger.trace("close", idstr
0468 + " attempting to close again.");
0469 }
0470 }
0471
0472 String savedConnectionId = null;
0473 if (connected) {
0474 savedConnectionId = connectionId;
0475 }
0476
0477 closeException = null;
0478
0479 if (tracing)
0480 logger.trace("close", idstr + " closing.");
0481
0482 if (communicatorAdmin != null) {
0483 communicatorAdmin.terminate();
0484 }
0485
0486 if (rmiNotifClient != null) {
0487 try {
0488 rmiNotifClient.terminate();
0489 if (tracing)
0490 logger.trace("close", idstr
0491 + " RMI Notification client terminated.");
0492 } catch (RuntimeException x) {
0493 closeException = x;
0494 if (tracing)
0495 logger
0496 .trace(
0497 "close",
0498 idstr
0499 + " Failed to terminate RMI Notification client: "
0500 + x);
0501 if (debug)
0502 logger.debug("close", x);
0503 }
0504 }
0505
0506 if (connection != null) {
0507 try {
0508 connection.close();
0509 if (tracing)
0510 logger.trace("close", idstr + " closed.");
0511 } catch (NoSuchObjectException nse) {
0512 // OK, the server maybe closed itself.
0513 } catch (IOException e) {
0514 closeException = e;
0515 if (tracing)
0516 logger.trace("close", idstr
0517 + " Failed to close RMIServer: " + e);
0518 if (debug)
0519 logger.debug("close", e);
0520 }
0521 }
0522
0523 // Clean up MBeanServerConnection table
0524 //
0525 rmbscMap.clear();
0526
0527 /* Send notification of closure. We don't do this if the user
0528 * never called connect() on the connector, because there's no
0529 * connection id in that case. */
0530
0531 if (savedConnectionId != null) {
0532 Notification closedNotif = new JMXConnectionNotification(
0533 JMXConnectionNotification.CLOSED, this ,
0534 savedConnectionId, clientNotifSeqNo++,
0535 "Client has been closed", null);
0536 sendNotification(closedNotif);
0537 }
0538
0539 // throw exception if needed
0540 //
0541 if (closeException != null) {
0542 if (tracing)
0543 logger.trace("close", idstr + " failed to close: "
0544 + closeException);
0545 if (closeException instanceof IOException)
0546 throw (IOException) closeException;
0547 if (closeException instanceof RuntimeException)
0548 throw (RuntimeException) closeException;
0549 final IOException x = new IOException("Failed to close: "
0550 + closeException);
0551 throw EnvHelp.initCause(x, closeException);
0552 }
0553 }
0554
0555 // added for re-connection
0556 private Integer addListenerWithSubject(ObjectName name,
0557 MarshalledObject filter, Subject delegationSubject,
0558 boolean reconnect) throws InstanceNotFoundException,
0559 IOException {
0560
0561 final boolean debug = logger.debugOn();
0562 if (debug)
0563 logger.debug("addListenerWithSubject",
0564 "(ObjectName,MarshalledObject,Subject)");
0565
0566 final ObjectName[] names = new ObjectName[] { name };
0567 final MarshalledObject[] filters = new MarshalledObject[] { filter };
0568 final Subject[] delegationSubjects = new Subject[] { delegationSubject };
0569
0570 final Integer[] listenerIDs = addListenersWithSubjects(names,
0571 filters, delegationSubjects, reconnect);
0572
0573 if (debug)
0574 logger.debug("addListenerWithSubject", "listenerID="
0575 + listenerIDs[0]);
0576 return listenerIDs[0];
0577 }
0578
0579 // added for re-connection
0580 private Integer[] addListenersWithSubjects(ObjectName[] names,
0581 MarshalledObject[] filters, Subject[] delegationSubjects,
0582 boolean reconnect) throws InstanceNotFoundException,
0583 IOException {
0584
0585 final boolean debug = logger.debugOn();
0586 if (debug)
0587 logger.debug("addListenersWithSubjects",
0588 "(ObjectName[],MarshalledObject[],Subject[])");
0589
0590 final ClassLoader old = pushDefaultClassLoader();
0591 Integer[] listenerIDs = null;
0592
0593 try {
0594 listenerIDs = connection.addNotificationListeners(names,
0595 filters, delegationSubjects);
0596 } catch (NoSuchObjectException noe) {
0597 // maybe reconnect
0598 if (reconnect) {
0599 communicatorAdmin.gotIOException(noe);
0600
0601 listenerIDs = connection.addNotificationListeners(
0602 names, filters, delegationSubjects);
0603 } else {
0604 throw noe;
0605 }
0606 } catch (IOException ioe) {
0607 // send a failed notif if necessary
0608 communicatorAdmin.gotIOException(ioe);
0609 } finally {
0610 popDefaultClassLoader(old);
0611 }
0612
0613 if (debug)
0614 logger.debug("addListenersWithSubjects", "registered "
0615 + listenerIDs.length + " listener(s)");
0616 return listenerIDs;
0617 }
0618
0619 //--------------------------------------------------------------------
0620 // Implementation of MBeanServerConnection
0621 //--------------------------------------------------------------------
0622 private class RemoteMBeanServerConnection implements
0623 MBeanServerConnection {
0624
0625 private Subject delegationSubject;
0626
0627 public RemoteMBeanServerConnection() {
0628 this (null);
0629 }
0630
0631 public RemoteMBeanServerConnection(Subject delegationSubject) {
0632 this .delegationSubject = delegationSubject;
0633 }
0634
0635 public ObjectInstance createMBean(String className,
0636 ObjectName name) throws ReflectionException,
0637 InstanceAlreadyExistsException,
0638 MBeanRegistrationException, MBeanException,
0639 NotCompliantMBeanException, IOException {
0640 if (logger.debugOn())
0641 logger.debug("createMBean(String,ObjectName)",
0642 "className=" + className + ", name=" + name);
0643
0644 final ClassLoader old = pushDefaultClassLoader();
0645 try {
0646 return connection.createMBean(className, name,
0647 delegationSubject);
0648 } catch (IOException ioe) {
0649 communicatorAdmin.gotIOException(ioe);
0650
0651 return connection.createMBean(className, name,
0652 delegationSubject);
0653 } finally {
0654 popDefaultClassLoader(old);
0655 }
0656 }
0657
0658 public ObjectInstance createMBean(String className,
0659 ObjectName name, ObjectName loaderName)
0660 throws ReflectionException,
0661 InstanceAlreadyExistsException,
0662 MBeanRegistrationException, MBeanException,
0663 NotCompliantMBeanException, InstanceNotFoundException,
0664 IOException {
0665
0666 if (logger.debugOn())
0667 logger.debug(
0668 "createMBean(String,ObjectName,ObjectName)",
0669 "className=" + className + ", name=" + name
0670 + ", loaderName=" + loaderName + ")");
0671
0672 final ClassLoader old = pushDefaultClassLoader();
0673 try {
0674 return connection.createMBean(className, name,
0675 loaderName, delegationSubject);
0676
0677 } catch (IOException ioe) {
0678 communicatorAdmin.gotIOException(ioe);
0679
0680 return connection.createMBean(className, name,
0681 loaderName, delegationSubject);
0682
0683 } finally {
0684 popDefaultClassLoader(old);
0685 }
0686 }
0687
0688 public ObjectInstance createMBean(String className,
0689 ObjectName name, Object params[], String signature[])
0690 throws ReflectionException,
0691 InstanceAlreadyExistsException,
0692 MBeanRegistrationException, MBeanException,
0693 NotCompliantMBeanException, IOException {
0694 if (logger.debugOn())
0695 logger
0696 .debug(
0697 "createMBean(String,ObjectName,Object[],String[])",
0698 "className=" + className + ", name="
0699 + name + ", params="
0700 + objects(params)
0701 + ", signature="
0702 + strings(signature));
0703
0704 final MarshalledObject<Object[]> sParams = new MarshalledObject<Object[]>(
0705 params);
0706 final ClassLoader old = pushDefaultClassLoader();
0707 try {
0708 return connection.createMBean(className, name, sParams,
0709 signature, delegationSubject);
0710 } catch (IOException ioe) {
0711 communicatorAdmin.gotIOException(ioe);
0712
0713 return connection.createMBean(className, name, sParams,
0714 signature, delegationSubject);
0715 } finally {
0716 popDefaultClassLoader(old);
0717 }
0718 }
0719
0720 public ObjectInstance createMBean(String className,
0721 ObjectName name, ObjectName loaderName,
0722 Object params[], String signature[])
0723 throws ReflectionException,
0724 InstanceAlreadyExistsException,
0725 MBeanRegistrationException, MBeanException,
0726 NotCompliantMBeanException, InstanceNotFoundException,
0727 IOException {
0728 if (logger.debugOn())
0729 logger
0730 .debug(
0731 "createMBean(String,ObjectName,ObjectName,Object[],String[])",
0732 "className=" + className + ", name="
0733 + name + ", loaderName="
0734 + loaderName + ", params="
0735 + objects(params)
0736 + ", signature="
0737 + strings(signature));
0738
0739 final MarshalledObject<Object[]> sParams = new MarshalledObject<Object[]>(
0740 params);
0741 final ClassLoader old = pushDefaultClassLoader();
0742 try {
0743 return connection.createMBean(className, name,
0744 loaderName, sParams, signature,
0745 delegationSubject);
0746 } catch (IOException ioe) {
0747 communicatorAdmin.gotIOException(ioe);
0748
0749 return connection.createMBean(className, name,
0750 loaderName, sParams, signature,
0751 delegationSubject);
0752 } finally {
0753 popDefaultClassLoader(old);
0754 }
0755 }
0756
0757 public void unregisterMBean(ObjectName name)
0758 throws InstanceNotFoundException,
0759 MBeanRegistrationException, IOException {
0760 if (logger.debugOn())
0761 logger.debug("unregisterMBean", "name=" + name);
0762
0763 final ClassLoader old = pushDefaultClassLoader();
0764 try {
0765 connection.unregisterMBean(name, delegationSubject);
0766 } catch (IOException ioe) {
0767 communicatorAdmin.gotIOException(ioe);
0768
0769 connection.unregisterMBean(name, delegationSubject);
0770 } finally {
0771 popDefaultClassLoader(old);
0772 }
0773 }
0774
0775 public ObjectInstance getObjectInstance(ObjectName name)
0776 throws InstanceNotFoundException, IOException {
0777 if (logger.debugOn())
0778 logger.debug("getObjectInstance", "name=" + name);
0779
0780 final ClassLoader old = pushDefaultClassLoader();
0781 try {
0782 return connection.getObjectInstance(name,
0783 delegationSubject);
0784 } catch (IOException ioe) {
0785 communicatorAdmin.gotIOException(ioe);
0786
0787 return connection.getObjectInstance(name,
0788 delegationSubject);
0789 } finally {
0790 popDefaultClassLoader(old);
0791 }
0792 }
0793
0794 public Set<ObjectInstance> queryMBeans(ObjectName name,
0795 QueryExp query) throws IOException {
0796 if (logger.debugOn())
0797 logger.debug("queryMBeans", "name=" + name + ", query="
0798 + query);
0799
0800 final MarshalledObject<QueryExp> sQuery = new MarshalledObject<QueryExp>(
0801 query);
0802 final ClassLoader old = pushDefaultClassLoader();
0803 try {
0804 return connection.queryMBeans(name, sQuery,
0805 delegationSubject);
0806 } catch (IOException ioe) {
0807 communicatorAdmin.gotIOException(ioe);
0808
0809 return connection.queryMBeans(name, sQuery,
0810 delegationSubject);
0811 } finally {
0812 popDefaultClassLoader(old);
0813 }
0814 }
0815
0816 public Set<ObjectName> queryNames(ObjectName name,
0817 QueryExp query) throws IOException {
0818 if (logger.debugOn())
0819 logger.debug("queryNames", "name=" + name + ", query="
0820 + query);
0821
0822 final MarshalledObject<QueryExp> sQuery = new MarshalledObject<QueryExp>(
0823 query);
0824 final ClassLoader old = pushDefaultClassLoader();
0825 try {
0826 return connection.queryNames(name, sQuery,
0827 delegationSubject);
0828 } catch (IOException ioe) {
0829 communicatorAdmin.gotIOException(ioe);
0830
0831 return connection.queryNames(name, sQuery,
0832 delegationSubject);
0833 } finally {
0834 popDefaultClassLoader(old);
0835 }
0836 }
0837
0838 public boolean isRegistered(ObjectName name) throws IOException {
0839 if (logger.debugOn())
0840 logger.debug("isRegistered", "name=" + name);
0841
0842 final ClassLoader old = pushDefaultClassLoader();
0843 try {
0844 return connection.isRegistered(name, delegationSubject);
0845 } catch (IOException ioe) {
0846 communicatorAdmin.gotIOException(ioe);
0847
0848 return connection.isRegistered(name, delegationSubject);
0849 } finally {
0850 popDefaultClassLoader(old);
0851 }
0852 }
0853
0854 public Integer getMBeanCount() throws IOException {
0855 if (logger.debugOn())
0856 logger.debug("getMBeanCount", "");
0857
0858 final ClassLoader old = pushDefaultClassLoader();
0859 try {
0860 return connection.getMBeanCount(delegationSubject);
0861 } catch (IOException ioe) {
0862 communicatorAdmin.gotIOException(ioe);
0863
0864 return connection.getMBeanCount(delegationSubject);
0865 } finally {
0866 popDefaultClassLoader(old);
0867 }
0868 }
0869
0870 public Object getAttribute(ObjectName name, String attribute)
0871 throws MBeanException, AttributeNotFoundException,
0872 InstanceNotFoundException, ReflectionException,
0873 IOException {
0874 if (logger.debugOn())
0875 logger.debug("getAttribute", "name=" + name
0876 + ", attribute=" + attribute);
0877
0878 final ClassLoader old = pushDefaultClassLoader();
0879 try {
0880 return connection.getAttribute(name, attribute,
0881 delegationSubject);
0882 } catch (IOException ioe) {
0883 communicatorAdmin.gotIOException(ioe);
0884
0885 return connection.getAttribute(name, attribute,
0886 delegationSubject);
0887 } finally {
0888 popDefaultClassLoader(old);
0889 }
0890 }
0891
0892 public AttributeList getAttributes(ObjectName name,
0893 String[] attributes) throws InstanceNotFoundException,
0894 ReflectionException, IOException {
0895 if (logger.debugOn())
0896 logger.debug("getAttributes", "name=" + name
0897 + ", attributes=" + strings(attributes));
0898
0899 final ClassLoader old = pushDefaultClassLoader();
0900 try {
0901 return connection.getAttributes(name, attributes,
0902 delegationSubject);
0903
0904 } catch (IOException ioe) {
0905 communicatorAdmin.gotIOException(ioe);
0906
0907 return connection.getAttributes(name, attributes,
0908 delegationSubject);
0909 } finally {
0910 popDefaultClassLoader(old);
0911 }
0912 }
0913
0914 public void setAttribute(ObjectName name, Attribute attribute)
0915 throws InstanceNotFoundException,
0916 AttributeNotFoundException,
0917 InvalidAttributeValueException, MBeanException,
0918 ReflectionException, IOException {
0919
0920 if (logger.debugOn())
0921 logger.debug("setAttribute", "name=" + name
0922 + ", attribute=" + attribute);
0923
0924 final MarshalledObject<Attribute> sAttribute = new MarshalledObject<Attribute>(
0925 attribute);
0926 final ClassLoader old = pushDefaultClassLoader();
0927 try {
0928 connection.setAttribute(name, sAttribute,
0929 delegationSubject);
0930 } catch (IOException ioe) {
0931 communicatorAdmin.gotIOException(ioe);
0932
0933 connection.setAttribute(name, sAttribute,
0934 delegationSubject);
0935 } finally {
0936 popDefaultClassLoader(old);
0937 }
0938 }
0939
0940 public AttributeList setAttributes(ObjectName name,
0941 AttributeList attributes)
0942 throws InstanceNotFoundException, ReflectionException,
0943 IOException {
0944
0945 if (logger.debugOn())
0946 logger.debug("setAttributes", "name=" + name
0947 + ", attributes=" + attributes);
0948
0949 final MarshalledObject<AttributeList> sAttributes = new MarshalledObject<AttributeList>(
0950 attributes);
0951 final ClassLoader old = pushDefaultClassLoader();
0952 try {
0953 return connection.setAttributes(name, sAttributes,
0954 delegationSubject);
0955 } catch (IOException ioe) {
0956 communicatorAdmin.gotIOException(ioe);
0957
0958 return connection.setAttributes(name, sAttributes,
0959 delegationSubject);
0960 } finally {
0961 popDefaultClassLoader(old);
0962 }
0963 }
0964
0965 public Object invoke(ObjectName name, String operationName,
0966 Object params[], String signature[])
0967 throws InstanceNotFoundException, MBeanException,
0968 ReflectionException, IOException {
0969
0970 if (logger.debugOn())
0971 logger.debug("invoke", "name=" + name
0972 + ", operationName=" + operationName
0973 + ", params=" + objects(params)
0974 + ", signature=" + strings(signature));
0975
0976 final MarshalledObject<Object[]> sParams = new MarshalledObject<Object[]>(
0977 params);
0978 final ClassLoader old = pushDefaultClassLoader();
0979 try {
0980 return connection.invoke(name, operationName, sParams,
0981 signature, delegationSubject);
0982 } catch (IOException ioe) {
0983 communicatorAdmin.gotIOException(ioe);
0984
0985 return connection.invoke(name, operationName, sParams,
0986 signature, delegationSubject);
0987 } finally {
0988 popDefaultClassLoader(old);
0989 }
0990 }
0991
0992 public String getDefaultDomain() throws IOException {
0993 if (logger.debugOn())
0994 logger.debug("getDefaultDomain", "");
0995
0996 final ClassLoader old = pushDefaultClassLoader();
0997 try {
0998 return connection.getDefaultDomain(delegationSubject);
0999 } catch (IOException ioe) {
1000 communicatorAdmin.gotIOException(ioe);
1001
1002 return connection.getDefaultDomain(delegationSubject);
1003 } finally {
1004 popDefaultClassLoader(old);
1005 }
1006 }
1007
1008 public String[] getDomains() throws IOException {
1009 if (logger.debugOn())
1010 logger.debug("getDomains", "");
1011
1012 final ClassLoader old = pushDefaultClassLoader();
1013 try {
1014 return connection.getDomains(delegationSubject);
1015 } catch (IOException ioe) {
1016 communicatorAdmin.gotIOException(ioe);
1017
1018 return connection.getDomains(delegationSubject);
1019 } finally {
1020 popDefaultClassLoader(old);
1021 }
1022 }
1023
1024 public MBeanInfo getMBeanInfo(ObjectName name)
1025 throws InstanceNotFoundException,
1026 IntrospectionException, ReflectionException,
1027 IOException {
1028
1029 if (logger.debugOn())
1030 logger.debug("getMBeanInfo", "name=" + name);
1031 final ClassLoader old = pushDefaultClassLoader();
1032 try {
1033 return connection.getMBeanInfo(name, delegationSubject);
1034 } catch (IOException ioe) {
1035 communicatorAdmin.gotIOException(ioe);
1036
1037 return connection.getMBeanInfo(name, delegationSubject);
1038 } finally {
1039 popDefaultClassLoader(old);
1040 }
1041 }
1042
1043 public boolean isInstanceOf(ObjectName name, String className)
1044 throws InstanceNotFoundException, IOException {
1045 if (logger.debugOn())
1046 logger.debug("isInstanceOf", "name=" + name
1047 + ", className=" + className);
1048
1049 final ClassLoader old = pushDefaultClassLoader();
1050 try {
1051 return connection.isInstanceOf(name, className,
1052 delegationSubject);
1053 } catch (IOException ioe) {
1054 communicatorAdmin.gotIOException(ioe);
1055
1056 return connection.isInstanceOf(name, className,
1057 delegationSubject);
1058 } finally {
1059 popDefaultClassLoader(old);
1060 }
1061 }
1062
1063 public void addNotificationListener(ObjectName name,
1064 ObjectName listener, NotificationFilter filter,
1065 Object handback) throws InstanceNotFoundException,
1066 IOException {
1067
1068 if (logger.debugOn())
1069 logger
1070 .debug(
1071 "addNotificationListener"
1072 + "(ObjectName,ObjectName,NotificationFilter,Object)",
1073 "name=" + name + ", listener="
1074 + listener + ", filter="
1075 + filter + ", handback="
1076 + handback);
1077
1078 final MarshalledObject<NotificationFilter> sFilter = new MarshalledObject<NotificationFilter>(
1079 filter);
1080 final MarshalledObject<Object> sHandback = new MarshalledObject<Object>(
1081 handback);
1082 final ClassLoader old = pushDefaultClassLoader();
1083 try {
1084 connection.addNotificationListener(name, listener,
1085 sFilter, sHandback, delegationSubject);
1086 } catch (IOException ioe) {
1087 communicatorAdmin.gotIOException(ioe);
1088
1089 connection.addNotificationListener(name, listener,
1090 sFilter, sHandback, delegationSubject);
1091 } finally {
1092 popDefaultClassLoader(old);
1093 }
1094 }
1095
1096 public void removeNotificationListener(ObjectName name,
1097 ObjectName listener) throws InstanceNotFoundException,
1098 ListenerNotFoundException, IOException {
1099
1100 if (logger.debugOn())
1101 logger.debug("removeNotificationListener"
1102 + "(ObjectName,ObjectName)", "name=" + name
1103 + ", listener=" + listener);
1104
1105 final ClassLoader old = pushDefaultClassLoader();
1106 try {
1107 connection.removeNotificationListener(name, listener,
1108 delegationSubject);
1109 } catch (IOException ioe) {
1110 communicatorAdmin.gotIOException(ioe);
1111
1112 connection.removeNotificationListener(name, listener,
1113 delegationSubject);
1114 } finally {
1115 popDefaultClassLoader(old);
1116 }
1117 }
1118
1119 public void removeNotificationListener(ObjectName name,
1120 ObjectName listener, NotificationFilter filter,
1121 Object handback) throws InstanceNotFoundException,
1122 ListenerNotFoundException, IOException {
1123 if (logger.debugOn())
1124 logger
1125 .debug(
1126 "removeNotificationListener"
1127 + "(ObjectName,ObjectName,NotificationFilter,Object)",
1128 "name=" + name + ", listener="
1129 + listener + ", filter="
1130 + filter + ", handback="
1131 + handback);
1132
1133 final MarshalledObject<NotificationFilter> sFilter = new MarshalledObject<NotificationFilter>(
1134 filter);
1135 final MarshalledObject<Object> sHandback = new MarshalledObject<Object>(
1136 handback);
1137 final ClassLoader old = pushDefaultClassLoader();
1138 try {
1139 connection.removeNotificationListener(name, listener,
1140 sFilter, sHandback, delegationSubject);
1141 } catch (IOException ioe) {
1142 communicatorAdmin.gotIOException(ioe);
1143
1144 connection.removeNotificationListener(name, listener,
1145 sFilter, sHandback, delegationSubject);
1146 } finally {
1147 popDefaultClassLoader(old);
1148 }
1149 }
1150
1151 // Specific Notification Handle ----------------------------------
1152
1153 public void addNotificationListener(ObjectName name,
1154 NotificationListener listener,
1155 NotificationFilter filter, Object handback)
1156 throws InstanceNotFoundException, IOException {
1157
1158 final boolean debug = logger.debugOn();
1159 if (debug)
1160 logger.debug("addNotificationListener"
1161 + "(ObjectName,NotificationListener,"
1162 + "NotificationFilter,Object)", "name=" + name
1163 + ", listener=" + listener + ", filter="
1164 + filter + ", handback=" + handback);
1165
1166 final Integer listenerID = addListenerWithSubject(name,
1167 new MarshalledObject<NotificationFilter>(filter),
1168 delegationSubject, true);
1169 rmiNotifClient.addNotificationListener(listenerID, name,
1170 listener, filter, handback, delegationSubject);
1171 }
1172
1173 public void removeNotificationListener(ObjectName name,
1174 NotificationListener listener)
1175 throws InstanceNotFoundException,
1176 ListenerNotFoundException, IOException {
1177 final boolean debug = logger.debugOn();
1178
1179 if (debug)
1180 logger.debug("removeNotificationListener"
1181 + "(ObjectName,NotificationListener)", "name="
1182 + name + ", listener=" + listener);
1183
1184 final Integer[] ret = rmiNotifClient
1185 .removeNotificationListener(name, listener);
1186
1187 if (debug)
1188 logger.debug("removeNotificationListener",
1189 "listenerIDs=" + objects(ret));
1190
1191 final ClassLoader old = pushDefaultClassLoader();
1192
1193 try {
1194 connection.removeNotificationListeners(name, ret,
1195 delegationSubject);
1196 } catch (IOException ioe) {
1197 communicatorAdmin.gotIOException(ioe);
1198
1199 connection.removeNotificationListeners(name, ret,
1200 delegationSubject);
1201 } finally {
1202 popDefaultClassLoader(old);
1203 }
1204
1205 }
1206
1207 public void removeNotificationListener(ObjectName name,
1208 NotificationListener listener,
1209 NotificationFilter filter, Object handback)
1210 throws InstanceNotFoundException,
1211 ListenerNotFoundException, IOException {
1212 final boolean debug = logger.debugOn();
1213
1214 if (debug)
1215 logger.debug("removeNotificationListener"
1216 + "(ObjectName,NotificationListener,"
1217 + "NotificationFilter,Object)", "name=" + name
1218 + ", listener=" + listener + ", filter="
1219 + filter + ", handback=" + handback);
1220
1221 final Integer ret = rmiNotifClient
1222 .removeNotificationListener(name, listener, filter,
1223 handback);
1224
1225 if (debug)
1226 logger.debug("removeNotificationListener",
1227 "listenerID=" + ret);
1228
1229 final ClassLoader old = pushDefaultClassLoader();
1230 try {
1231 connection.removeNotificationListeners(name,
1232 new Integer[] { ret }, delegationSubject);
1233 } catch (IOException ioe) {
1234 communicatorAdmin.gotIOException(ioe);
1235
1236 connection.removeNotificationListeners(name,
1237 new Integer[] { ret }, delegationSubject);
1238 } finally {
1239 popDefaultClassLoader(old);
1240 }
1241
1242 }
1243 }
1244
1245 //--------------------------------------------------------------------
1246 private class RMINotifClient extends ClientNotifForwarder {
1247 public RMINotifClient(ClassLoader cl, Map env) {
1248 super (cl, env);
1249 }
1250
1251 protected NotificationResult fetchNotifs(
1252 long clientSequenceNumber, int maxNotifications,
1253 long timeout) throws IOException,
1254 ClassNotFoundException {
1255 IOException org;
1256
1257 while (true) { // used for a successful re-connection
1258 try {
1259 return connection.fetchNotifications(
1260 clientSequenceNumber, maxNotifications,
1261 timeout);
1262 } catch (IOException ioe) {
1263 org = ioe;
1264
1265 // inform of IOException
1266 try {
1267 communicatorAdmin.gotIOException(ioe);
1268
1269 // The connection should be re-established.
1270 continue;
1271 } catch (IOException ee) {
1272 // No more fetch, the Exception will be re-thrown.
1273 break;
1274 } // never reached
1275 } // never reached
1276 }
1277
1278 // specially treating for an UnmarshalException
1279 if (org instanceof UnmarshalException) {
1280 UnmarshalException ume = (UnmarshalException) org;
1281
1282 if (ume.detail instanceof ClassNotFoundException)
1283 throw (ClassNotFoundException) ume.detail;
1284
1285 /* In Sun's RMI implementation, if a method return
1286 contains an unserializable object, then we get
1287 UnmarshalException wrapping WriteAbortedException
1288 wrapping NotSerializableException. In that case we
1289 extract the NotSerializableException so that our
1290 caller can realize it should try to skip past the
1291 notification that presumably caused it. It's not
1292 certain that every other RMI implementation will
1293 generate this exact exception sequence. If not, we
1294 will not detect that the problem is due to an
1295 unserializable object, and we will stop trying to
1296 receive notifications from the server. It's not
1297 clear we can do much better. */
1298 if (ume.detail instanceof WriteAbortedException) {
1299 WriteAbortedException wae = (WriteAbortedException) ume.detail;
1300 if (wae.detail instanceof IOException)
1301 throw (IOException) wae.detail;
1302 }
1303 } else if (org instanceof MarshalException) {
1304 // IIOP will throw MarshalException wrapping a NotSerializableException
1305 // when a server fails to serialize a response.
1306 MarshalException me = (MarshalException) org;
1307 if (me.detail instanceof NotSerializableException) {
1308 throw (NotSerializableException) me.detail;
1309 }
1310 }
1311
1312 // Not serialization problem, simply re-throw the orginal exception
1313 throw org;
1314 }
1315
1316 protected Integer addListenerForMBeanRemovedNotif()
1317 throws IOException, InstanceNotFoundException {
1318 MarshalledObject<NotificationFilter> sFilter = null;
1319 NotificationFilterSupport clientFilter = new NotificationFilterSupport();
1320 clientFilter
1321 .enableType(MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
1322 sFilter = new MarshalledObject<NotificationFilter>(
1323 clientFilter);
1324
1325 Integer[] listenerIDs;
1326 final ObjectName[] names = new ObjectName[] { MBeanServerDelegate.DELEGATE_NAME };
1327 final MarshalledObject[] filters = new MarshalledObject[] { sFilter };
1328 final Subject[] subjects = new Subject[] { null };
1329 try {
1330 listenerIDs = connection.addNotificationListeners(
1331 names, filters, subjects);
1332
1333 } catch (IOException ioe) {
1334 communicatorAdmin.gotIOException(ioe);
1335
1336 listenerIDs = connection.addNotificationListeners(
1337 names, filters, subjects);
1338 }
1339 return listenerIDs[0];
1340 }
1341
1342 protected void removeListenerForMBeanRemovedNotif(Integer id)
1343 throws IOException, InstanceNotFoundException,
1344 ListenerNotFoundException {
1345 try {
1346 connection.removeNotificationListeners(
1347 MBeanServerDelegate.DELEGATE_NAME,
1348 new Integer[] { id }, null);
1349 } catch (IOException ioe) {
1350 communicatorAdmin.gotIOException(ioe);
1351
1352 connection.removeNotificationListeners(
1353 MBeanServerDelegate.DELEGATE_NAME,
1354 new Integer[] { id }, null);
1355 }
1356
1357 }
1358
1359 protected void lostNotifs(String message, long number) {
1360 final String notifType = JMXConnectionNotification.NOTIFS_LOST;
1361
1362 final JMXConnectionNotification n = new JMXConnectionNotification(
1363 notifType, RMIConnector.this , connectionId,
1364 clientNotifCounter++, message, new Long(number));
1365 sendNotification(n);
1366 }
1367 }
1368
1369 private class RMIClientCommunicatorAdmin extends
1370 ClientCommunicatorAdmin {
1371 public RMIClientCommunicatorAdmin(long period) {
1372 super (period);
1373 }
1374
1375 public void gotIOException(IOException ioe) throws IOException {
1376 if (ioe instanceof NoSuchObjectException) {
1377 // need to restart
1378 super .gotIOException(ioe);
1379
1380 return;
1381 }
1382
1383 // check if the connection is broken
1384 try {
1385 connection.getDefaultDomain(null);
1386 } catch (IOException ioexc) {
1387 boolean toClose = false;
1388
1389 synchronized (this ) {
1390 if (!terminated) {
1391 terminated = true;
1392
1393 toClose = true;
1394 }
1395 }
1396
1397 if (toClose) {
1398 // we should close the connection,
1399 // but send a failed notif at first
1400 final Notification failedNotif = new JMXConnectionNotification(
1401 JMXConnectionNotification.FAILED, this ,
1402 connectionId, clientNotifSeqNo++,
1403 "Failed to communicate with the server: "
1404 + ioe.toString(), ioe);
1405
1406 sendNotification(failedNotif);
1407
1408 try {
1409 close(true);
1410 } catch (Exception e) {
1411 // OK.
1412 // We are closing
1413 }
1414 }
1415 }
1416
1417 // forward the exception
1418 if (ioe instanceof ServerException) {
1419 /* Need to unwrap the exception.
1420 Some user-thrown exception at server side will be wrapped by
1421 rmi into a ServerException.
1422 For example, a RMIConnnectorServer will wrap a
1423 ClassNotFoundException into a UnmarshalException, and rmi
1424 will throw a ServerException at client side which wraps this
1425 UnmarshalException.
1426 No failed notif here.
1427 */
1428 Throwable tt = ((ServerException) ioe).detail;
1429
1430 if (tt instanceof IOException) {
1431 throw (IOException) tt;
1432 } else if (tt instanceof RuntimeException) {
1433 throw (RuntimeException) tt;
1434 }
1435 }
1436
1437 throw ioe;
1438 }
1439
1440 public void reconnectNotificationListeners(
1441 ClientListenerInfo[] old) throws IOException {
1442 final int len = old.length;
1443 int i;
1444
1445 ClientListenerInfo[] clis = new ClientListenerInfo[len];
1446
1447 final Subject[] subjects = new Subject[len];
1448 final ObjectName[] names = new ObjectName[len];
1449 final NotificationListener[] listeners = new NotificationListener[len];
1450 final NotificationFilter[] filters = new NotificationFilter[len];
1451 final MarshalledObject[] mFilters = new MarshalledObject[len];
1452 final Object[] handbacks = new Object[len];
1453
1454 for (i = 0; i < len; i++) {
1455 subjects[i] = old[i].getDelegationSubject();
1456 names[i] = old[i].getObjectName();
1457 listeners[i] = old[i].getListener();
1458 filters[i] = old[i].getNotificationFilter();
1459 mFilters[i] = new MarshalledObject<NotificationFilter>(
1460 filters[i]);
1461 handbacks[i] = old[i].getHandback();
1462 }
1463
1464 try {
1465 Integer[] ids = addListenersWithSubjects(names,
1466 mFilters, subjects, false);
1467
1468 for (i = 0; i < len; i++) {
1469 clis[i] = new ClientListenerInfo(ids[i], names[i],
1470 listeners[i], filters[i], handbacks[i],
1471 subjects[i]);
1472 }
1473
1474 rmiNotifClient.postReconnection(clis);
1475
1476 return;
1477 } catch (InstanceNotFoundException infe) {
1478 // OK, we will do one by one
1479 }
1480
1481 int j = 0;
1482 for (i = 0; i < len; i++) {
1483 try {
1484 Integer id = addListenerWithSubject(names[i],
1485 new MarshalledObject<NotificationFilter>(
1486 filters[i]), subjects[i], false);
1487
1488 clis[j++] = new ClientListenerInfo(id, names[i],
1489 listeners[i], filters[i], handbacks[i],
1490 subjects[i]);
1491 } catch (InstanceNotFoundException infe) {
1492 logger.warning("reconnectNotificationListeners",
1493 "Can't reconnect listener for " + names[i]);
1494 }
1495 }
1496
1497 if (j != len) {
1498 ClientListenerInfo[] tmp = clis;
1499 clis = new ClientListenerInfo[j];
1500 System.arraycopy(tmp, 0, clis, 0, j);
1501 }
1502
1503 rmiNotifClient.postReconnection(clis);
1504 }
1505
1506 protected void checkConnection() throws IOException {
1507 if (logger.debugOn())
1508 logger.debug(
1509 "RMIClientCommunicatorAdmin-checkConnection",
1510 "Calling the method getDefaultDomain.");
1511
1512 connection.getDefaultDomain(null);
1513 }
1514
1515 protected void doStart() throws IOException {
1516 // Get RMIServer stub from directory or URL encoding if needed.
1517 RMIServer stub = null;
1518 try {
1519 stub = (rmiServer != null) ? rmiServer : findRMIServer(
1520 jmxServiceURL, env);
1521 } catch (NamingException ne) {
1522 throw new IOException("Failed to get a RMI stub: " + ne);
1523 }
1524
1525 // Connect IIOP Stub if needed.
1526 stub = connectStub(stub, env);
1527
1528 // Calling newClient on the RMIServer stub.
1529 Object credentials = env.get(CREDENTIALS);
1530 connection = stub.newClient(credentials);
1531
1532 // notif issues
1533 final ClientListenerInfo[] old = rmiNotifClient
1534 .preReconnection();
1535
1536 reconnectNotificationListeners(old);
1537
1538 connectionId = getConnectionId();
1539
1540 Notification reconnectedNotif = new JMXConnectionNotification(
1541 JMXConnectionNotification.OPENED, this ,
1542 connectionId, clientNotifSeqNo++,
1543 "Reconnected to server", null);
1544 sendNotification(reconnectedNotif);
1545
1546 }
1547
1548 protected void doStop() {
1549 try {
1550 close();
1551 } catch (IOException ioe) {
1552 logger.warning("RMIClientCommunicatorAdmin-doStop",
1553 "Failed to call the method close():" + ioe);
1554 logger.debug("RMIClientCommunicatorAdmin-doStop", ioe);
1555 }
1556 }
1557 }
1558
1559 //--------------------------------------------------------------------
1560 // Private stuff - Serialization
1561 //--------------------------------------------------------------------
1562 /**
1563 * <p>In order to be usable, an IIOP stub must be connected to an ORB.
1564 * The stub is automatically connected to the ORB if:
1565 * <ul>
1566 * <li> It was returned by the COS naming</li>
1567 * <li> Its server counterpart has been registered in COS naming
1568 * through JNDI.</li>
1569 * </ul>
1570 * Otherwise, it is not connected. A stub which is deserialized
1571 * from Jini is not connected. A stub which is obtained from a
1572 * non registered RMIIIOPServerImpl is not a connected.<br>
1573 * A stub which is not connected can't be serialized, and thus
1574 * can't be registered in Jini. A stub which is not connected can't
1575 * be used to invoke methods on the server.
1576 * <p>
1577 * In order to palliate this, this method will connect the
1578 * given stub if it is not yet connected. If the given
1579 * <var>RMIServer</var> is not an instance of
1580 * {@link javax.rmi.CORBA.Stub javax.rmi.CORBA.Stub}, then the
1581 * method do nothing and simply returns that stub. Otherwise,
1582 * this method will attempt to connect the stub to an ORB as
1583 * follows:
1584 * <ul>
1585 * <p>This method looks in the provided <var>environment</var> for
1586 * the "java.naming.corba.orb" property. If it is found, the
1587 * referenced object (an {@link org.omg.CORBA.ORB ORB}) is used to
1588 * connect the stub. Otherwise, a new org.omg.CORBA.ORB is created
1589 * by calling {@link
1590 * org.omg.CORBA.ORB#init(String[], Properties)
1591 * org.omg.CORBA.ORB.init((String[])null,(Properties)null)}
1592 * <p>The new created ORB is kept in a static
1593 * {@link WeakReference} and can be reused for connecting other
1594 * stubs. However, no reference is ever kept on the ORB provided
1595 * in the <var>environment</var> map, if any.
1596 * </ul>
1597 * @param rmiServer A RMI Server Stub.
1598 * @param environment An environment map, possibly containing an ORB.
1599 * @return the given stub.
1600 * @exception IllegalArgumentException if the
1601 * <tt>java.naming.corba.orb</tt> property is specified and
1602 * does not point to an {@link org.omg.CORBA.ORB ORB}.
1603 * @exception IOException if the connection to the ORB failed.
1604 **/
1605 static RMIServer connectStub(RMIServer rmiServer, Map environment)
1606 throws IOException {
1607 if (rmiServer instanceof javax.rmi.CORBA.Stub) {
1608 javax.rmi.CORBA.Stub stub = (javax.rmi.CORBA.Stub) rmiServer;
1609 try {
1610 stub._orb();
1611 } catch (BAD_OPERATION x) {
1612 stub.connect(resolveOrb(environment));
1613 }
1614 }
1615 return rmiServer;
1616 }
1617
1618 /**
1619 * Get the ORB specified by <var>environment</var>, or create a
1620 * new one.
1621 * <p>This method looks in the provided <var>environment</var> for
1622 * the "java.naming.corba.orb" property. If it is found, the
1623 * referenced object (an {@link org.omg.CORBA.ORB ORB}) is
1624 * returned. Otherwise, a new org.omg.CORBA.ORB is created
1625 * by calling {@link
1626 * org.omg.CORBA.ORB#init(String[], java.util.Properties)
1627 * org.omg.CORBA.ORB.init((String[])null,(Properties)null)}
1628 * <p>The new created ORB is kept in a static
1629 * {@link WeakReference} and can be reused for connecting other
1630 * stubs. However, no reference is ever kept on the ORB provided
1631 * in the <var>environment</var> map, if any.
1632 * @param environment An environment map, possibly containing an ORB.
1633 * @return An ORB.
1634 * @exception IllegalArgumentException if the
1635 * <tt>java.naming.corba.orb</tt> property is specified and
1636 * does not point to an {@link org.omg.CORBA.ORB ORB}.
1637 * @exception IOException if the ORB initialization failed.
1638 **/
1639 static ORB resolveOrb(Map environment) throws IOException {
1640 if (environment != null) {
1641 final Object orb = environment.get(EnvHelp.DEFAULT_ORB);
1642 if (orb != null && !(orb instanceof ORB))
1643 throw new IllegalArgumentException(EnvHelp.DEFAULT_ORB
1644 + " must be an instance of org.omg.CORBA.ORB.");
1645 if (orb != null)
1646 return (ORB) orb;
1647 }
1648 final ORB orb = (RMIConnector.orb == null) ? null
1649 : RMIConnector.orb.get();
1650 if (orb != null)
1651 return orb;
1652
1653 final ORB newOrb = ORB.init((String[]) null, (Properties) null);
1654 RMIConnector.orb = new WeakReference<ORB>(newOrb);
1655 return newOrb;
1656 }
1657
1658 /**
1659 * Read RMIConnector fields from an {@link java.io.ObjectInputStream
1660 * ObjectInputStream}.
1661 * Calls <code>s.defaultReadObject()</code> and then initializes
1662 * all transient variables that need initializing.
1663 * @param s The ObjectInputStream to read from.
1664 * @exception InvalidObjectException if none of <var>rmiServer</var> stub
1665 * or <var>jmxServiceURL</var> are set.
1666 * @see #RMIConnector(JMXServiceURL,Map)
1667 * @see #RMIConnector(RMIServer,Map)
1668 **/
1669 private void readObject(java.io.ObjectInputStream s)
1670 throws IOException, ClassNotFoundException {
1671 s.defaultReadObject();
1672
1673 if (rmiServer == null && jmxServiceURL == null)
1674 throw new InvalidObjectException(
1675 "rmiServer and jmxServiceURL both null");
1676
1677 initTransients();
1678 }
1679
1680 /**
1681 * Writes the RMIConnector fields to an {@link java.io.ObjectOutputStream
1682 * ObjectOutputStream}.
1683 * <p>Connects the underlying RMIServer stub to an ORB, if needed,
1684 * before serializing it. This is done using the environment
1685 * map that was provided to the constructor, if any, and as documented
1686 * in {@link javax.management.remote.rmi}.</p>
1687 * <p>This method then calls <code>s.defaultWriteObject()</code>.
1688 * Usually, <var>rmiServer</var> is null if this object
1689 * was constructed with a JMXServiceURL, and <var>jmxServiceURL</var>
1690 * is null if this object is constructed with a RMIServer stub.
1691 * <p>Note that the environment Map is not serialized, since the objects
1692 * it contains are assumed to be contextual and relevant only
1693 * with respect to the local environment (class loader, ORB, etc...).</p>
1694 * <p>After an RMIConnector is deserialized, it is assumed that the
1695 * user will call {@link #connect(Map)}, providing a new Map that
1696 * can contain values which are contextually relevant to the new
1697 * local environment.</p>
1698 * <p>Since connection to the ORB is needed prior to serializing, and
1699 * since the ORB to connect to is one of those contextual parameters,
1700 * it is not recommended to re-serialize a just de-serialized object -
1701 * as the de-serialized object has no map. Thus, when an RMIConnector
1702 * object is needed for serialization or transmission to a remote
1703 * application, it is recommended to obtain a new RMIConnector stub
1704 * by calling {@link RMIConnectorServer#toJMXConnector(Map)}.</p>
1705 * @param s The ObjectOutputStream to write to.
1706 * @exception InvalidObjectException if none of <var>rmiServer</var> stub
1707 * or <var>jmxServiceURL</var> are set.
1708 * @see #RMIConnector(JMXServiceURL,Map)
1709 * @see #RMIConnector(RMIServer,Map)
1710 **/
1711 private void writeObject(java.io.ObjectOutputStream s)
1712 throws IOException {
1713 if (rmiServer == null && jmxServiceURL == null)
1714 throw new InvalidObjectException(
1715 "rmiServer and jmxServiceURL both null.");
1716 connectStub(this .rmiServer, env);
1717 s.defaultWriteObject();
1718 }
1719
1720 // Initialization of transient variables.
1721 private void initTransients() {
1722 rmbscMap = new WeakHashMap<Subject, MBeanServerConnection>();
1723 connected = false;
1724 terminated = false;
1725
1726 connectionBroadcaster = new NotificationBroadcasterSupport();
1727 }
1728
1729 //--------------------------------------------------------------------
1730 // Private stuff - Check if stub can be trusted.
1731 //--------------------------------------------------------------------
1732
1733 private static void checkStub(Remote stub, Class<?> stubClass) {
1734
1735 // Check remote stub is from the expected class.
1736 //
1737 if (stub.getClass() != stubClass) {
1738 if (!Proxy.isProxyClass(stub.getClass())) {
1739 throw new SecurityException("Expecting a "
1740 + stubClass.getName() + " stub!");
1741 } else {
1742 InvocationHandler handler = Proxy
1743 .getInvocationHandler(stub);
1744 if (handler.getClass() != RemoteObjectInvocationHandler.class)
1745 throw new SecurityException(
1746 "Expecting a dynamic proxy instance with a "
1747 + RemoteObjectInvocationHandler.class
1748 .getName()
1749 + " invocation handler!");
1750 else
1751 stub = (Remote) handler;
1752 }
1753 }
1754
1755 // Check RemoteRef in stub is from the expected class
1756 // "sun.rmi.server.UnicastRef2".
1757 //
1758 RemoteRef ref = ((RemoteObject) stub).getRef();
1759 if (ref.getClass() != UnicastRef2.class)
1760 throw new SecurityException("Expecting a "
1761 + UnicastRef2.class.getName()
1762 + " remote reference in stub!");
1763
1764 // Check RMIClientSocketFactory in stub is from the expected class
1765 // "javax.rmi.ssl.SslRMIClientSocketFactory".
1766 //
1767 LiveRef liveRef = ((UnicastRef2) ref).getLiveRef();
1768 RMIClientSocketFactory csf = liveRef.getClientSocketFactory();
1769 if (csf == null
1770 || csf.getClass() != SslRMIClientSocketFactory.class)
1771 throw new SecurityException("Expecting a "
1772 + SslRMIClientSocketFactory.class.getName()
1773 + " RMI client socket factory in stub!");
1774 }
1775
1776 //--------------------------------------------------------------------
1777 // Private stuff - RMIServer creation
1778 //--------------------------------------------------------------------
1779
1780 private RMIServer findRMIServer(JMXServiceURL directoryURL,
1781 Map<String, Object> environment) throws NamingException,
1782 IOException {
1783 final boolean isIiop = RMIConnectorServer.isIiopURL(
1784 directoryURL, true);
1785 if (isIiop) {
1786 // Make sure java.naming.corba.orb is in the Map.
1787 environment.put(EnvHelp.DEFAULT_ORB,
1788 resolveOrb(environment));
1789 }
1790
1791 String path = directoryURL.getURLPath();
1792 int end = path.indexOf(';');
1793 if (end < 0)
1794 end = path.length();
1795 if (path.startsWith("/jndi/"))
1796 return findRMIServerJNDI(path.substring(6, end),
1797 environment, isIiop);
1798 else if (path.startsWith("/stub/"))
1799 return findRMIServerJRMP(path.substring(6, end),
1800 environment, isIiop);
1801 else if (path.startsWith("/ior/"))
1802 return findRMIServerIIOP(path.substring(5, end),
1803 environment, isIiop);
1804 else {
1805 final String msg = "URL path must begin with /jndi/ or /stub/ "
1806 + "or /ior/: " + path;
1807 throw new MalformedURLException(msg);
1808 }
1809 }
1810
1811 /**
1812 * Lookup the RMIServer stub in a directory.
1813 * @param jndiURL A JNDI URL indicating the location of the Stub
1814 * (see {@link javax.management.remote.rmi}), e.g.:
1815 * <ul><li><tt>rmi://registry-host:port/rmi-stub-name</tt></li>
1816 * <li>or <tt>iiop://cosnaming-host:port/iiop-stub-name</tt></li>
1817 * <li>or <tt>ldap://ldap-host:port/java-container-dn</tt></li>
1818 * </ul>
1819 * @param env the environment Map passed to the connector.
1820 * @param isIiop true if the stub is expected to be an IIOP stub.
1821 * @return The retrieved RMIServer stub.
1822 * @exception NamingException if the stub couldn't be found.
1823 **/
1824 private RMIServer findRMIServerJNDI(String jndiURL,
1825 Map<String, ?> env, boolean isIiop) throws NamingException {
1826
1827 InitialContext ctx = new InitialContext(EnvHelp
1828 .mapToHashtable(env));
1829
1830 Object objref = ctx.lookup(jndiURL);
1831 ctx.close();
1832
1833 if (isIiop)
1834 return narrowIIOPServer(objref);
1835 else
1836 return narrowJRMPServer(objref);
1837 }
1838
1839 private static RMIServer narrowJRMPServer(Object objref) {
1840
1841 return (RMIServer) objref;
1842 }
1843
1844 private static RMIServer narrowIIOPServer(Object objref) {
1845 try {
1846 return (RMIServer) PortableRemoteObject.narrow(objref,
1847 RMIServer.class);
1848 } catch (ClassCastException e) {
1849 if (logger.traceOn())
1850 logger.trace("narrowIIOPServer",
1851 "Failed to narrow objref=" + objref + ": " + e);
1852 if (logger.debugOn())
1853 logger.debug("narrowIIOPServer", e);
1854 return null;
1855 }
1856 }
1857
1858 private RMIServer findRMIServerIIOP(String ior, Map env,
1859 boolean isIiop) {
1860 // could forbid "rmi:" URL here -- but do we need to?
1861 final ORB orb = (ORB) env.get(EnvHelp.DEFAULT_ORB);
1862 final Object stub = orb.string_to_object(ior);
1863 return (RMIServer) PortableRemoteObject.narrow(stub,
1864 RMIServer.class);
1865 }
1866
1867 private RMIServer findRMIServerJRMP(String base64, Map env,
1868 boolean isIiop) throws IOException {
1869 // could forbid "iiop:" URL here -- but do we need to?
1870 final byte[] serialized;
1871 try {
1872 serialized = base64ToByteArray(base64);
1873 } catch (IllegalArgumentException e) {
1874 throw new MalformedURLException("Bad BASE64 encoding: "
1875 + e.getMessage());
1876 }
1877 final ByteArrayInputStream bin = new ByteArrayInputStream(
1878 serialized);
1879
1880 final ClassLoader loader = EnvHelp
1881 .resolveClientClassLoader(env);
1882 final ObjectInputStream oin = (loader == null) ? new ObjectInputStream(
1883 bin)
1884 : new ObjectInputStreamWithLoader(bin, loader);
1885 final Object stub;
1886 try {
1887 stub = oin.readObject();
1888 } catch (ClassNotFoundException e) {
1889 throw new MalformedURLException("Class not found: " + e);
1890 }
1891 return (RMIServer) PortableRemoteObject.narrow(stub,
1892 RMIServer.class);
1893 }
1894
1895 private static final class ObjectInputStreamWithLoader extends
1896 ObjectInputStream {
1897 ObjectInputStreamWithLoader(InputStream in, ClassLoader cl)
1898 throws IOException {
1899 super (in);
1900 this .loader = cl;
1901 }
1902
1903 protected Class resolveClass(ObjectStreamClass classDesc)
1904 throws IOException, ClassNotFoundException {
1905 return Class.forName(classDesc.getName(), false, loader);
1906 }
1907
1908 private final ClassLoader loader;
1909 }
1910
1911 /*
1912 The following section of code avoids a class loading problem
1913 with RMI. The problem is that an RMI stub, when deserializing
1914 a remote method return value or exception, will first of all
1915 consult the first non-bootstrap class loader it finds in the
1916 call stack. This can lead to behavior that is not portable
1917 between implementations of the JMX Remote API. Notably, an
1918 implementation on J2SE 1.4 will find the RMI stub's loader on
1919 the stack. But in J2SE 5, this stub is loaded by the
1920 bootstrap loader, so RMI will find the loader of the user code
1921 that called an MBeanServerConnection method.
1922
1923 To avoid this problem, we take advantage of what the RMI stub
1924 is doing internally. Each remote call will end up calling
1925 ref.invoke(...), where ref is the RemoteRef parameter given to
1926 the RMI stub's constructor. It is within this call that the
1927 deserialization will happen. So we fabricate our own RemoteRef
1928 that delegates everything to the "real" one but that is loaded
1929 by a class loader that knows no other classes. The class
1930 loader NoCallStackClassLoader does this: the RemoteRef is an
1931 instance of the class named by proxyRefClassName, which is
1932 fabricated by the class loader using byte code that is defined
1933 by the string below.
1934
1935 The call stack when the deserialization happens is thus this:
1936 MBeanServerConnection.getAttribute (or whatever)
1937 -> RMIConnectionImpl_Stub.getAttribute
1938 -> ProxyRef.invoke(...getAttribute...)
1939 -> UnicastRef.invoke(...getAttribute...)
1940 -> internal RMI stuff
1941
1942 Here UnicastRef is the RemoteRef created when the stub was
1943 deserialized (which is of some RMI internal class). It and the
1944 "internal RMI stuff" are loaded by the bootstrap loader, so are
1945 transparent to the stack search. The first non-bootstrap
1946 loader found is our ProxyRefLoader, as required.
1947
1948 In a future version of this code as integrated into J2SE 5,
1949 this workaround could be replaced by direct access to the
1950 internals of RMI. For now, we use the same code base for J2SE
1951 and for the standalone Reference Implementation.
1952
1953 The byte code below encodes the following class, compiled using
1954 J2SE 1.4.2 with the -g:none option.
1955
1956 package com.sun.jmx.remote.internal;
1957
1958 import java.lang.reflect.Method;
1959 import java.rmi.Remote;
1960 import java.rmi.server.RemoteRef;
1961 import com.sun.jmx.remote.internal.ProxyRef;
1962
1963 public class PRef extends ProxyRef {
1964 public PRef(RemoteRef ref) {
1965 super(ref);
1966 }
1967
1968 public Object invoke(Remote obj, Method method,
1969 Object[] params, long opnum)
1970 throws Exception {
1971 return ref.invoke(obj, method, params, opnum);
1972 }
1973 }
1974 */
1975
1976 private static final String rmiServerImplStubClassName = RMIServer.class
1977 .getName()
1978 + "Impl_Stub";
1979 private static final Class rmiServerImplStubClass;
1980 private static final String rmiConnectionImplStubClassName = RMIConnection.class
1981 .getName()
1982 + "Impl_Stub";
1983 private static final Class<?> rmiConnectionImplStubClass;
1984 private static final String pRefClassName = "com.sun.jmx.remote.internal.PRef";
1985 private static final Constructor proxyRefConstructor;
1986 static {
1987 final String pRefByteCodeString = "\312\376\272\276\0\0\0.\0\27\12\0\5\0\15\11\0\4\0\16\13\0\17\0"
1988 + "\20\7\0\21\7\0\22\1\0\6<init>\1\0\36(Ljava/rmi/server/RemoteRef;"
1989 + ")V\1\0\4Code\1\0\6invoke\1\0S(Ljava/rmi/Remote;Ljava/lang/reflec"
1990 + "t/Method;[Ljava/lang/Object;J)Ljava/lang/Object;\1\0\12Exception"
1991 + "s\7\0\23\14\0\6\0\7\14\0\24\0\25\7\0\26\14\0\11\0\12\1\0\40com/"
1992 + "sun/jmx/remote/internal/PRef\1\0$com/sun/jmx/remote/internal/Pr"
1993 + "oxyRef\1\0\23java/lang/Exception\1\0\3ref\1\0\33Ljava/rmi/serve"
1994 + "r/RemoteRef;\1\0\31java/rmi/server/RemoteRef\0!\0\4\0\5\0\0\0\0"
1995 + "\0\2\0\1\0\6\0\7\0\1\0\10\0\0\0\22\0\2\0\2\0\0\0\6*+\267\0\1\261"
1996 + "\0\0\0\0\0\1\0\11\0\12\0\2\0\10\0\0\0\33\0\6\0\6\0\0\0\17*\264\0"
1997 + "\2+,-\26\4\271\0\3\6\0\260\0\0\0\0\0\13\0\0\0\4\0\1\0\14\0\0";
1998 final byte[] pRefByteCode = NoCallStackClassLoader
1999 .stringToBytes(pRefByteCodeString);
2000 PrivilegedExceptionAction<Constructor<?>> action = new PrivilegedExceptionAction<Constructor<?>>() {
2001 public Constructor<?> run() throws Exception {
2002 Class this Class = RMIConnector.class;
2003 ClassLoader this Loader = this Class.getClassLoader();
2004 ProtectionDomain this ProtectionDomain = this Class
2005 .getProtectionDomain();
2006 String[] otherClassNames = { ProxyRef.class.getName() };
2007 ClassLoader cl = new NoCallStackClassLoader(
2008 pRefClassName, pRefByteCode, otherClassNames,
2009 this Loader, this ProtectionDomain);
2010 Class<?> c = cl.loadClass(pRefClassName);
2011 return c.getConstructor(RemoteRef.class);
2012 }
2013 };
2014
2015 Class serverStubClass;
2016 try {
2017 serverStubClass = Class.forName(rmiServerImplStubClassName);
2018 } catch (Exception e) {
2019 logger.error("<clinit>", "Failed to instantiate "
2020 + rmiServerImplStubClassName + ": " + e);
2021 logger.debug("<clinit>", e);
2022 serverStubClass = null;
2023 }
2024 rmiServerImplStubClass = serverStubClass;
2025
2026 Class<?> stubClass;
2027 Constructor constr;
2028 try {
2029 stubClass = Class.forName(rmiConnectionImplStubClassName);
2030 constr = (Constructor) AccessController
2031 .doPrivileged(action);
2032 } catch (Exception e) {
2033 logger.error("<clinit>",
2034 "Failed to initialize proxy reference constructor "
2035 + "for " + rmiConnectionImplStubClassName
2036 + ": " + e);
2037 logger.debug("<clinit>", e);
2038 stubClass = null;
2039 constr = null;
2040 }
2041 rmiConnectionImplStubClass = stubClass;
2042 proxyRefConstructor = constr;
2043 }
2044
2045 private static RMIConnection shadowJrmpStub(RemoteObject stub)
2046 throws InstantiationException, IllegalAccessException,
2047 InvocationTargetException, ClassNotFoundException,
2048 NoSuchMethodException {
2049 RemoteRef ref = stub.getRef();
2050 RemoteRef proxyRef = (RemoteRef) proxyRefConstructor
2051 .newInstance(new Object[] { ref });
2052 final Class[] constrTypes = { RemoteRef.class };
2053 final Constructor rmiConnectionImplStubConstructor = rmiConnectionImplStubClass
2054 .getConstructor(constrTypes);
2055 Object[] args = { proxyRef };
2056 RMIConnection proxyStub = (RMIConnection) rmiConnectionImplStubConstructor
2057 .newInstance(args);
2058 return proxyStub;
2059 }
2060
2061 /*
2062 The following code performs a similar trick for RMI/IIOP to the
2063 one described above for RMI/JRMP. Unlike JRMP, though, we
2064 can't easily insert an object between the RMIConnection stub
2065 and the RMI/IIOP deserialization code, as explained below.
2066
2067 A method in an RMI/IIOP stub does the following. It makes an
2068 org.omg.CORBA_2_3.portable.OutputStream for each request, and
2069 writes the parameters to it. Then it calls
2070 _invoke(OutputStream) which it inherits from CORBA's
2071 ObjectImpl. That returns an
2072 org.omg.CORBA_2_3.portable.InputStream. The return value is
2073 read from this InputStream. So the stack during
2074 deserialization looks like this:
2075
2076 MBeanServerConnection.getAttribute (or whatever)
2077 -> _RMIConnection_Stub.getAttribute
2078 -> Util.readAny (a CORBA method)
2079 -> InputStream.read_any
2080 -> internal CORBA stuff
2081
2082 What we would have *liked* to have done would be the same thing
2083 as for RMI/JRMP. We create a "ProxyDelegate" that is an
2084 org.omg.CORBA.portable.Delegate that simply forwards every
2085 operation to the real original Delegate from the RMIConnection
2086 stub, except that the InputStream returned by _invoke is
2087 wrapped by a "ProxyInputStream" that is loaded by our
2088 NoCallStackClassLoader.
2089
2090 Unfortunately, this doesn't work, at least with Sun's J2SE
2091 1.4.2, because the CORBA code is not designed to allow you to
2092 change Delegates arbitrarily. You get a ClassCastException
2093 from code that expects the Delegate to implement an internal
2094 interface.
2095
2096 So instead we do the following. We create a subclass of the
2097 stub that overrides the _invoke method so as to wrap the
2098 returned InputStream in a ProxyInputStream. We create a
2099 subclass of ProxyInputStream using the NoCallStackClassLoader
2100 and override its read_any and read_value(Class) methods.
2101 (These are the only methods called during deserialization of
2102 MBeanServerConnection return values.) We extract the Delegate
2103 from the original stub and insert it into our subclass stub,
2104 and away we go. The state of a stub consists solely of its
2105 Delegate.
2106
2107 We also need to catch ApplicationException, which will encode
2108 any exceptions declared in the throws clause of the called
2109 method. Its InputStream needs to be wrapped in a
2110 ProxyInputSteam too.
2111
2112 We override _releaseReply in the stub subclass so that it
2113 replaces a ProxyInputStream argument with the original
2114 InputStream. This avoids problems if the implementation of
2115 _releaseReply ends up casting this InputStream to an
2116 implementation-specific interface (which in Sun's J2SE 5 it
2117 does).
2118
2119 It is not strictly necessary for the stub subclass to be loaded
2120 by a NoCallStackClassLoader, since the call-stack search stops
2121 at the ProxyInputStream subclass. However, it is convenient
2122 for two reasons. One is that it means that the
2123 ProxyInputStream subclass can be accessed directly, without
2124 using reflection. The other is that it avoids build problems,
2125 since usually stubs are created after other classes are
2126 compiled, so we can't access them from this class without,
2127 again, using reflection.
2128
2129 The strings below encode the following two Java classes,
2130 compiled using J2SE 1.4.2 with javac -g:none.
2131
2132 package com.sun.jmx.remote.internal;
2133
2134 import org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub;
2135
2136 import org.omg.CORBA.portable.ApplicationException;
2137 import org.omg.CORBA.portable.InputStream;
2138 import org.omg.CORBA.portable.OutputStream;
2139 import org.omg.CORBA.portable.RemarshalException;
2140
2141 public class ProxyStub extends _RMIConnection_Stub {
2142 public InputStream _invoke(OutputStream out)
2143 throws ApplicationException, RemarshalException {
2144 try {
2145 return new PInputStream(super._invoke(out));
2146 } catch (ApplicationException e) {
2147 InputStream pis = new PInputStream(e.getInputStream());
2148 throw new ApplicationException(e.getId(), pis);
2149 }
2150 }
2151
2152 public void _releaseReply(InputStream in) {
2153 PInputStream pis = (PInputStream) in;
2154 super._releaseReply(pis.getProxiedInputStream());
2155 }
2156 }
2157
2158 package com.sun.jmx.remote.internal;
2159
2160 public class PInputStream extends ProxyInputStream {
2161 public PInputStream(org.omg.CORBA.portable.InputStream in) {
2162 super(in);
2163 }
2164
2165 public org.omg.CORBA.Any read_any() {
2166 return in.read_any();
2167 }
2168
2169 public java.io.Serializable read_value(Class clz) {
2170 return narrow().read_value(clz);
2171 }
2172 }
2173
2174
2175 */
2176 private static final String iiopConnectionStubClassName = "org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub";
2177 private static final String proxyStubClassName = "com.sun.jmx.remote.internal.ProxyStub";
2178 private static final String pInputStreamClassName = "com.sun.jmx.remote.internal.PInputStream";
2179 private static final Class proxyStubClass;
2180 static {
2181 final String proxyStubByteCodeString = "\312\376\272\276\0\0\0.\0)\12\0\14\0\26\7\0\27\12\0\14\0\30\12"
2182 + "\0\2\0\31\7\0\32\12\0\5\0\33\12\0\5\0\34\12\0\5\0\35\12\0\2\0"
2183 + "\36\12\0\14\0\37\7\0\40\7\0!\1\0\6<init>\1\0\3()V\1\0\4Code\1"
2184 + "\0\7_invoke\1\0K(Lorg/omg/CORBA/portable/OutputStream;)Lorg/o"
2185 + "mg/CORBA/portable/InputStream;\1\0\12Exceptions\7\0\"\1\0\15_"
2186 + "releaseReply\1\0'(Lorg/omg/CORBA/portable/InputStream;)V\14\0"
2187 + "\15\0\16\1\0(com/sun/jmx/remote/internal/PInputStream\14\0\20"
2188 + "\0\21\14\0\15\0\25\1\0+org/omg/CORBA/portable/ApplicationExce"
2189 + "ption\14\0#\0$\14\0%\0&\14\0\15\0'\14\0(\0$\14\0\24\0\25\1\0%"
2190 + "com/sun/jmx/remote/internal/ProxyStub\1\0<org/omg/stub/javax/"
2191 + "management/remote/rmi/_RMIConnection_Stub\1\0)org/omg/CORBA/p"
2192 + "ortable/RemarshalException\1\0\16getInputStream\1\0&()Lorg/om"
2193 + "g/CORBA/portable/InputStream;\1\0\5getId\1\0\24()Ljava/lang/S"
2194 + "tring;\1\09(Ljava/lang/String;Lorg/omg/CORBA/portable/InputSt"
2195 + "ream;)V\1\0\25getProxiedInputStream\0!\0\13\0\14\0\0\0\0\0\3\0"
2196 + "\1\0\15\0\16\0\1\0\17\0\0\0\21\0\1\0\1\0\0\0\5*\267\0\1\261\0"
2197 + "\0\0\0\0\1\0\20\0\21\0\2\0\17\0\0\0;\0\4\0\4\0\0\0'\273\0\2Y*"
2198 + "+\267\0\3\267\0\4\260M\273\0\2Y,\266\0\6\267\0\4N\273\0\5Y,\266"
2199 + "\0\7-\267\0\10\277\0\1\0\0\0\14\0\15\0\5\0\0\0\22\0\0\0\6\0\2"
2200 + "\0\5\0\23\0\1\0\24\0\25\0\1\0\17\0\0\0\36\0\2\0\2\0\0\0\22+\306"
2201 + "\0\13+\300\0\2\266\0\11L*+\267\0\12\261\0\0\0\0\0\0";
2202 final String pInputStreamByteCodeString = "\312\376\272\276\0\0\0.\0\36\12\0\7\0\17\11\0\6\0\20\12\0\21\0"
2203 + "\22\12\0\6\0\23\12\0\24\0\25\7\0\26\7\0\27\1\0\6<init>\1\0'(L"
2204 + "org/omg/CORBA/portable/InputStream;)V\1\0\4Code\1\0\10read_an"
2205 + "y\1\0\25()Lorg/omg/CORBA/Any;\1\0\12read_value\1\0)(Ljava/lan"
2206 + "g/Class;)Ljava/io/Serializable;\14\0\10\0\11\14\0\30\0\31\7\0"
2207 + "\32\14\0\13\0\14\14\0\33\0\34\7\0\35\14\0\15\0\16\1\0(com/sun"
2208 + "/jmx/remote/internal/PInputStream\1\0,com/sun/jmx/remote/inte"
2209 + "rnal/ProxyInputStream\1\0\2in\1\0$Lorg/omg/CORBA/portable/Inp"
2210 + "utStream;\1\0\"org/omg/CORBA/portable/InputStream\1\0\6narrow"
2211 + "\1\0*()Lorg/omg/CORBA_2_3/portable/InputStream;\1\0&org/omg/C"
2212 + "ORBA_2_3/portable/InputStream\0!\0\6\0\7\0\0\0\0\0\3\0\1\0\10"
2213 + "\0\11\0\1\0\12\0\0\0\22\0\2\0\2\0\0\0\6*+\267\0\1\261\0\0\0\0"
2214 + "\0\1\0\13\0\14\0\1\0\12\0\0\0\24\0\1\0\1\0\0\0\10*\264\0\2\266"
2215 + "\0\3\260\0\0\0\0\0\1\0\15\0\16\0\1\0\12\0\0\0\25\0\2\0\2\0\0\0"
2216 + "\11*\266\0\4+\266\0\5\260\0\0\0\0\0\0";
2217 final byte[] proxyStubByteCode = NoCallStackClassLoader
2218 .stringToBytes(proxyStubByteCodeString);
2219 final byte[] pInputStreamByteCode = NoCallStackClassLoader
2220 .stringToBytes(pInputStreamByteCodeString);
2221 final String[] classNames = { proxyStubClassName,
2222 pInputStreamClassName };
2223 final byte[][] byteCodes = { proxyStubByteCode,
2224 pInputStreamByteCode };
2225 final String[] otherClassNames = { iiopConnectionStubClassName,
2226 ProxyInputStream.class.getName(), };
2227 PrivilegedExceptionAction<Class<?>> action = new PrivilegedExceptionAction<Class<?>>() {
2228 public Class<?> run() throws Exception {
2229
2230 Class this Class = RMIConnector.class;
2231 ClassLoader this Loader = this Class.getClassLoader();
2232 ProtectionDomain this ProtectionDomain = this Class
2233 .getProtectionDomain();
2234 ClassLoader cl = new NoCallStackClassLoader(classNames,
2235 byteCodes, otherClassNames, this Loader,
2236 this ProtectionDomain);
2237 return cl.loadClass(proxyStubClassName);
2238 }
2239 };
2240 Class<?> stubClass;
2241 try {
2242 stubClass = AccessController.doPrivileged(action);
2243 } catch (Exception e) {
2244 logger.error("<clinit>",
2245 "Unexpected exception making shadow IIOP stub class: "
2246 + e);
2247 logger.debug("<clinit>", e);
2248 stubClass = null;
2249 }
2250 proxyStubClass = stubClass;
2251 }
2252
2253 private static RMIConnection shadowIiopStub(Stub stub)
2254 throws InstantiationException, IllegalAccessException {
2255 Stub proxyStub = (Stub) proxyStubClass.newInstance();
2256 proxyStub._set_delegate(stub._get_delegate());
2257 return (RMIConnection) proxyStub;
2258 }
2259
2260 private static RMIConnection getConnection(RMIServer server,
2261 Object credentials, boolean checkStub) throws IOException {
2262 RMIConnection c = server.newClient(credentials);
2263 if (checkStub)
2264 checkStub(c, rmiConnectionImplStubClass);
2265 try {
2266 if (c.getClass() == rmiConnectionImplStubClass)
2267 return shadowJrmpStub((RemoteObject) c);
2268 if (c.getClass().getName().equals(
2269 iiopConnectionStubClassName))
2270 return shadowIiopStub((Stub) c);
2271 logger
2272 .trace(
2273 "getConnection",
2274 "Did not wrap "
2275 + c.getClass()
2276 + " to foil "
2277 + "stack search for classes: class loading semantics "
2278 + "may be incorrect");
2279 } catch (Exception e) {
2280 logger
2281 .error(
2282 "getConnection",
2283 "Could not wrap "
2284 + c.getClass()
2285 + " to foil "
2286 + "stack search for classes: class loading semantics "
2287 + "may be incorrect: " + e);
2288 logger.debug("getConnection", e);
2289 // so just return the original stub, which will work for all
2290 // but the most exotic class loading situations
2291 }
2292 return c;
2293 }
2294
2295 private static byte[] base64ToByteArray(String s) {
2296 int sLen = s.length();
2297 int numGroups = sLen / 4;
2298 if (4 * numGroups != sLen)
2299 throw new IllegalArgumentException(
2300 "String length must be a multiple of four.");
2301 int missingBytesInLastGroup = 0;
2302 int numFullGroups = numGroups;
2303 if (sLen != 0) {
2304 if (s.charAt(sLen - 1) == '=') {
2305 missingBytesInLastGroup++;
2306 numFullGroups--;
2307 }
2308 if (s.charAt(sLen - 2) == '=')
2309 missingBytesInLastGroup++;
2310 }
2311 byte[] result = new byte[3 * numGroups
2312 - missingBytesInLastGroup];
2313
2314 // Translate all full groups from base64 to byte array elements
2315 int inCursor = 0, outCursor = 0;
2316 for (int i = 0; i < numFullGroups; i++) {
2317 int ch0 = base64toInt(s.charAt(inCursor++));
2318 int ch1 = base64toInt(s.charAt(inCursor++));
2319 int ch2 = base64toInt(s.charAt(inCursor++));
2320 int ch3 = base64toInt(s.charAt(inCursor++));
2321 result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
2322 result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
2323 result[outCursor++] = (byte) ((ch2 << 6) | ch3);
2324 }
2325
2326 // Translate partial group, if present
2327 if (missingBytesInLastGroup != 0) {
2328 int ch0 = base64toInt(s.charAt(inCursor++));
2329 int ch1 = base64toInt(s.charAt(inCursor++));
2330 result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
2331
2332 if (missingBytesInLastGroup == 1) {
2333 int ch2 = base64toInt(s.charAt(inCursor++));
2334 result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
2335 }
2336 }
2337 // assert inCursor == s.length()-missingBytesInLastGroup;
2338 // assert outCursor == result.length;
2339 return result;
2340 }
2341
2342 /**
2343 * Translates the specified character, which is assumed to be in the
2344 * "Base 64 Alphabet" into its equivalent 6-bit positive integer.
2345 *
2346 * @throws IllegalArgumentException if
2347 * c is not in the Base64 Alphabet.
2348 */
2349 private static int base64toInt(char c) {
2350 int result;
2351
2352 if (c >= base64ToInt.length)
2353 result = -1;
2354 else
2355 result = base64ToInt[c];
2356
2357 if (result < 0)
2358 throw new IllegalArgumentException("Illegal character " + c);
2359 return result;
2360 }
2361
2362 /**
2363 * This array is a lookup table that translates unicode characters
2364 * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
2365 * into their 6-bit positive integer equivalents. Characters that
2366 * are not in the Base64 alphabet but fall within the bounds of the
2367 * array are translated to -1.
2368 */
2369 private static final byte base64ToInt[] = { -1, -1, -1, -1, -1, -1,
2370 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2371 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2372 -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
2373 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0,
2374 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
2375 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26,
2376 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
2377 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };
2378
2379 //--------------------------------------------------------------------
2380 // Private stuff - Find / Set default class loader
2381 //--------------------------------------------------------------------
2382 private ClassLoader pushDefaultClassLoader() {
2383 final Thread t = Thread.currentThread();
2384 final ClassLoader old = t.getContextClassLoader();
2385 if (defaultClassLoader != null)
2386 AccessController.doPrivileged(new PrivilegedAction<Void>() {
2387 public Void run() {
2388 t.setContextClassLoader(defaultClassLoader);
2389 return null;
2390 }
2391 });
2392 return old;
2393 }
2394
2395 private void popDefaultClassLoader(final ClassLoader old) {
2396 AccessController.doPrivileged(new PrivilegedAction<Void>() {
2397 public Void run() {
2398 Thread.currentThread().setContextClassLoader(old);
2399 return null;
2400 }
2401 });
2402 }
2403
2404 //--------------------------------------------------------------------
2405 // Private variables
2406 //--------------------------------------------------------------------
2407 /**
2408 * @serial The RMIServer stub of the RMI JMX Connector server to
2409 * which this client connector is (or will be) connected. This
2410 * field can be null when <var>jmxServiceURL</var> is not
2411 * null. This includes the case where <var>jmxServiceURL</var>
2412 * contains a serialized RMIServer stub. If both
2413 * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
2414 * serialization will fail.
2415 *
2416 * @see #RMIConnector(RMIServer,Map)
2417 **/
2418 private final RMIServer rmiServer;
2419
2420 /**
2421 * @serial The JMXServiceURL of the RMI JMX Connector server to
2422 * which this client connector will be connected. This field can
2423 * be null when <var>rmiServer</var> is not null. If both
2424 * <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
2425 * serialization will fail.
2426 *
2427 * @see #RMIConnector(JMXServiceURL,Map)
2428 **/
2429 private final JMXServiceURL jmxServiceURL;
2430
2431 // ---------------------------------------------------------
2432 // WARNING - WARNING - WARNING - WARNING - WARNING - WARNING
2433 // ---------------------------------------------------------
2434 // Any transient variable which needs to be initialized should
2435 // be initialized in the method initTransient()
2436 private transient Map<String, Object> env;
2437 private transient ClassLoader defaultClassLoader;
2438 private transient RMIConnection connection;
2439 private transient String connectionId;
2440
2441 private transient long clientNotifSeqNo = 0;
2442
2443 private transient WeakHashMap<Subject, MBeanServerConnection> rmbscMap;
2444
2445 private transient RMINotifClient rmiNotifClient;
2446 // = new RMINotifClient(new Integer(0));
2447
2448 private transient long clientNotifCounter = 0;
2449
2450 private transient boolean connected;
2451 // = false;
2452 private transient boolean terminated;
2453 // = false;
2454
2455 private transient Exception closeException;
2456
2457 private transient NotificationBroadcasterSupport connectionBroadcaster;
2458
2459 private transient ClientCommunicatorAdmin communicatorAdmin;
2460
2461 /**
2462 * A static WeakReference to an {@link org.omg.CORBA.ORB ORB} to
2463 * connect unconnected stubs.
2464 **/
2465 private static WeakReference<ORB> orb = null;
2466
2467 // TRACES & DEBUG
2468 //---------------
2469 private static String objects(final Object[] objs) {
2470 if (objs == null)
2471 return "null";
2472 else
2473 return Arrays.asList(objs).toString();
2474 }
2475
2476 private static String strings(final String[] strs) {
2477 return objects(strs);
2478 }
2479 }
|