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