/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* <p>A connection to a remote RMI connector. Usually, such
* connections are made using {@link
* javax.management.remote.JMXConnectorFactory JMXConnectorFactory}.
* However, specialized applications can use this class directly, for
* example with an {@link RMIServer} stub obtained without going
* through JNDI.</p>
*
* @since 1.5
*/
IllegalArgumentException("rmiServer and jmxServiceURL both null");
this.jmxServiceURL = address;
if (environment == null) {
} else {
}
}
/**
* <p>Constructs an <code>RMIConnector</code> that will connect
* the RMI connector server with the given address.</p>
*
* <p>The address can refer directly to the connector server,
* using one of the following syntaxes:</p>
*
* <pre>
* service:jmx:rmi://<em>[host[:port]]</em>/stub/<em>encoded-stub</em>
* service:jmx:iiop://<em>[host[:port]]</em>/ior/<em>encoded-IOR</em>
* </pre>
*
* <p>(Here, the square brackets <code>[]</code> are not part of the
* address but indicate that the host and port are optional.)</p>
*
* <p>The address can instead indicate where to find an RMI stub
* through JNDI, using one of the following syntaxes:</p>
*
* <pre>
* service:jmx:rmi://<em>[host[:port]]</em>/jndi/<em>jndi-name</em>
* service:jmx:iiop://<em>[host[:port]]</em>/jndi/<em>jndi-name</em>
* </pre>
*
* <p>An implementation may also recognize additional address
* syntaxes, for example:</p>
*
* <pre>
* service:jmx:iiop://<em>[host[:port]]</em>/stub/<em>encoded-stub</em>
* </pre>
*
* @param url the address of the RMI connector server.
*
* @param environment additional attributes specifying how to make
* the connection. For JNDI-based addresses, these attributes can
* usefully include JNDI attributes recognized by {@link
* InitialContext#InitialContext(Hashtable) InitialContext}. This
* parameter can be null, which is equivalent to an empty Map.
*
* @exception IllegalArgumentException if <code>url</code>
* is null.
*/
}
/**
* <p>Constructs an <code>RMIConnector</code> using the given RMI stub.
*
* @param rmiServer an RMI stub representing the RMI connector server.
* @param environment additional attributes specifying how to make
* the connection. This parameter can be null, which is
* equivalent to an empty Map.
*
* @exception IllegalArgumentException if <code>rmiServer</code>
* is null.
*/
}
/**
* <p>Returns a string representation of this object. In general,
* the <code>toString</code> method returns a string that
* "textually represents" this object. The result should be a
* concise but informative representation that is easy for a
* person to read.</p>
*
* @return a String representation of this object.
**/
b.append(":");
}
if (jmxServiceURL != null) {
}
return b.toString();
}
/**
* <p>The address of this connector.</p>
*
* @return the address of this connector, or null if it
* does not have one.
*
* @since 1.6
*/
return jmxServiceURL;
}
//--------------------------------------------------------------------
// implements JMXConnector interface
//--------------------------------------------------------------------
}
throws IOException {
if (terminated) {
throw new IOException("Connector closed");
}
if (connected) {
return;
}
try {
if (environment != null) {
}
// Get RMIServer stub from directory or URL encoding if needed.
// Check for secure RMIServer stub if the corresponding
// client-side environment property is set to "true".
//
// Connect IIOP Stub if needed.
// Calling newClient on the RMIServer stub.
if (tracing)
try {
if (jmxServiceURL != null) {
"Protocol is rmi but JNDI scheme is iiop: " + jmxServiceURL);
throw mfe;
}
}
throw re;
}
// Always use one of:
// ClassLoader provided in Map at connect time,
// or contextClassLoader at connect time.
if (tracing)
connected = true;
// The connectionId variable is used in doStart(), when
// reconnecting, to identify the "old" connection.
//
this,
"Successful connection",
null);
} catch (IOException e) {
if (tracing)
throw e;
} catch (RuntimeException e) {
if (tracing)
throw e;
} catch (NamingException e) {
}
}
if (terminated || !connected) {
"] not connected.");
throw new IOException("Not connected");
}
// we do a remote call to have an IOException if the connection is broken.
// see the bug 4939578
return connection.getConnectionId();
}
throws IOException {
return getMBeanServerConnection(null);
}
public synchronized MBeanServerConnection
throws IOException {
if (terminated) {
"] already closed.");
throw new IOException("Connection closed");
} else if (!connected) {
"] is not connected.");
throw new IOException("Not connected");
}
return rmbsc;
}
return rmbsc;
}
public void
throw new NullPointerException("listener");
handback);
}
public void
throws ListenerNotFoundException {
throw new NullPointerException("listener");
}
public void
throws ListenerNotFoundException {
throw new NullPointerException("listener");
handback);
}
}
close(false);
}
// allows to do close after setting the flag "terminated" to true.
// It is necessary to avoid a deadlock, see 6296324
if (!intern) {
// Return if already cleanly closed.
//
if (terminated) {
if (closeException == null) {
return;
}
} else {
terminated = true;
}
}
// Already closed, but not cleanly. Attempt again.
//
if (tracing) {
}
}
if (connected) {
}
if (communicatorAdmin != null) {
}
if (rmiNotifClient != null) {
try {
" RMI Notification client terminated.");
} catch (RuntimeException x) {
closeException = x;
" Failed to terminate RMI Notification client: " + x);
}
}
if (connection != null) {
try {
connection.close();
} catch (NoSuchObjectException nse) {
// OK, the server maybe closed itself.
} catch (IOException e) {
closeException = e;
" Failed to close RMIServer: " + e);
}
}
// Clean up MBeanServerConnection table
//
/* Send notification of closure. We don't do this if the user
* never called connect() on the connector, because there's no
* connection id in that case. */
if (savedConnectionId != null) {
this,
"Client has been closed",
null);
}
// throw exception if needed
//
if (closeException != null) {
if (closeException instanceof IOException)
throw (IOException) closeException;
if (closeException instanceof RuntimeException)
throw (RuntimeException) closeException;
final IOException x =
}
}
// added for re-connection
boolean reconnect)
throws InstanceNotFoundException, IOException {
if (debug)
"(ObjectName,MarshalledObject,Subject)");
};
final Integer[] listenerIDs =
+ listenerIDs[0]);
return listenerIDs[0];
}
// added for re-connection
boolean reconnect)
throws InstanceNotFoundException, IOException {
if (debug)
"(ObjectName[],MarshalledObject[],Subject[])");
try {
} catch (NoSuchObjectException noe) {
// maybe reconnect
if (reconnect) {
} else {
throw noe;
}
} catch (IOException ioe) {
// send a failed notif if necessary
} finally {
}
+ " listener(s)");
return listenerIDs;
}
//--------------------------------------------------------------------
// Implementation of MBeanServerConnection
//--------------------------------------------------------------------
public RemoteMBeanServerConnection() {
this(null);
}
this.delegationSubject = delegationSubject;
}
throws ReflectionException,
name);
try {
name,
} catch (IOException ioe) {
name,
} finally {
}
}
throws ReflectionException,
+ name + ", loaderName="
+ loaderName + ")");
try {
name,
} catch (IOException ioe) {
name,
} finally {
}
}
throws ReflectionException,
+ name + ", params="
try {
name,
} catch (IOException ioe) {
name,
} finally {
}
}
throws ReflectionException,
"createMBean(String,ObjectName,ObjectName,Object[],String[])",
try {
name,
} catch (IOException ioe) {
name,
} finally {
}
}
throws InstanceNotFoundException,
try {
} catch (IOException ioe) {
} finally {
}
}
throws InstanceNotFoundException,
try {
} catch (IOException ioe) {
} finally {
}
}
throws IOException {
try {
} catch (IOException ioe) {
} finally {
}
}
throws IOException {
try {
} catch (IOException ioe) {
} finally {
}
}
throws IOException {
try {
} catch (IOException ioe) {
} finally {
}
}
throws IOException {
try {
} catch (IOException ioe) {
} finally {
}
}
throws MBeanException,
+ attribute);
try {
} catch (IOException ioe) {
} finally {
}
}
String[] attributes)
throws InstanceNotFoundException,
+ strings(attributes));
try {
} catch (IOException ioe) {
} finally {
}
}
throws InstanceNotFoundException,
+ attribute);
try {
} catch (IOException ioe) {
} finally {
}
}
throws InstanceNotFoundException,
+ attributes);
try {
} catch (IOException ioe) {
} finally {
}
}
throws InstanceNotFoundException,
"name=" + name
+ ", operationName=" + operationName
try {
} catch (IOException ioe) {
} finally {
}
}
throws IOException {
try {
} catch (IOException ioe) {
} finally {
}
}
try {
} catch (IOException ioe) {
} finally {
}
}
throws InstanceNotFoundException,
try {
} catch (IOException ioe) {
} finally {
}
}
throws InstanceNotFoundException,
", className=" + className);
try {
} catch (IOException ioe) {
} finally {
}
}
throws InstanceNotFoundException,
"(ObjectName,ObjectName,NotificationFilter,Object)",
try {
} catch (IOException ioe) {
} finally {
}
}
throws InstanceNotFoundException,
"(ObjectName,ObjectName)",
"name=" + name
+ ", listener=" + listener);
try {
} catch (IOException ioe) {
} finally {
}
}
throws InstanceNotFoundException,
"(ObjectName,ObjectName,NotificationFilter,Object)",
"name=" + name
+ ", listener=" + listener
+ ", filter=" + filter
+ ", handback=" + handback);
try {
} catch (IOException ioe) {
} finally {
}
}
// Specific Notification Handle ----------------------------------
throws InstanceNotFoundException,
if (debug)
"(ObjectName,NotificationListener,"+
"NotificationFilter,Object)",
"name=" + name
+ ", listener=" + listener
+ ", filter=" + filter
+ ", handback=" + handback);
final Integer listenerID =
delegationSubject,true);
}
throws InstanceNotFoundException,
"(ObjectName,NotificationListener)",
"name=" + name
+ ", listener=" + listener);
try {
ret,
} catch (IOException ioe) {
ret,
} finally {
}
}
throws InstanceNotFoundException,
if (debug)
"(ObjectName,NotificationListener,"+
"NotificationFilter,Object)",
"name=" + name
+ ", listener=" + listener
+ ", filter=" + filter
+ ", handback=" + handback);
"listenerID=" + ret);
try {
} catch (IOException ioe) {
} finally {
}
}
}
//--------------------------------------------------------------------
}
int maxNotifications,
long timeout)
throws IOException, ClassNotFoundException {
while (true) { // used for a successful re-connection
try {
timeout);
} catch (IOException ioe) {
// inform of IOException
try {
// The connection should be re-established.
continue;
} catch (IOException ee) {
// No more fetch, the Exception will be re-thrown.
break;
} // never reached
} // never reached
}
// specially treating for an UnmarshalException
if (org instanceof UnmarshalException) {
/* In Sun's RMI implementation, if a method return
contains an unserializable object, then we get
UnmarshalException wrapping WriteAbortedException
wrapping NotSerializableException. In that case we
extract the NotSerializableException so that our
caller can realize it should try to skip past the
notification that presumably caused it. It's not
certain that every other RMI implementation will
generate this exact exception sequence. If not, we
will not detect that the problem is due to an
unserializable object, and we will stop trying to
receive notifications from the server. It's not
clear we can do much better. */
}
} else if (org instanceof MarshalException) {
// IIOP will throw MarshalException wrapping a NotSerializableException
// when a server fails to serialize a response.
}
}
// Not serialization problem, simply re-throw the orginal exception
throw org;
}
throws IOException, InstanceNotFoundException {
new NotificationFilterSupport();
final ObjectName[] names =
try {
subjects);
} catch (IOException ioe) {
subjects);
}
return listenerIDs[0];
}
throws IOException, InstanceNotFoundException,
try {
null);
} catch (IOException ioe) {
null);
}
}
final JMXConnectionNotification n =
RMIConnector.this,
sendNotification(n);
}
}
super(period);
}
if (ioe instanceof NoSuchObjectException) {
// need to restart
super.gotIOException(ioe);
return;
}
// check if the connection is broken
try {
} catch (IOException ioexc) {
boolean toClose = false;
synchronized(this) {
if (!terminated) {
terminated = true;
toClose = true;
}
}
if (toClose) {
// we should close the connection,
// but send a failed notif at first
final Notification failedNotif =
this,
ioe);
try {
close(true);
} catch (Exception e) {
// OK.
// We are closing
}
}
}
// forward the exception
if (ioe instanceof ServerException) {
/* Need to unwrap the exception.
Some user-thrown exception at server side will be wrapped by
rmi into a ServerException.
For example, a RMIConnnectorServer will wrap a
ClassNotFoundException into a UnmarshalException, and rmi
will throw a ServerException at client side which wraps this
UnmarshalException.
No failed notif here.
*/
if (tt instanceof IOException) {
throw (IOException)tt;
} else if (tt instanceof RuntimeException) {
throw (RuntimeException)tt;
}
}
throw ioe;
}
int i;
for (i=0;i<len;i++) {
}
try {
for (i=0;i<len;i++) {
names[i],
listeners[i],
filters[i],
handbacks[i],
subjects[i]);
}
return;
} catch (InstanceNotFoundException infe) {
// OK, we will do one by one
}
int j = 0;
for (i=0;i<len;i++) {
try {
subjects[i],
false);
names[i],
listeners[i],
filters[i],
handbacks[i],
subjects[i]);
} catch (InstanceNotFoundException infe) {
"Can't reconnect listener for " +
names[i]);
}
}
if (j != len) {
clis = new ClientListenerInfo[j];
}
}
"Calling the method getDefaultDomain.");
}
// Get RMIServer stub from directory or URL encoding if needed.
try {
} catch (NamingException ne) {
}
// Connect IIOP Stub if needed.
// Calling newClient on the RMIServer stub.
// notif issues
this,
"Reconnected to server",
null);
}
protected void doStop() {
try {
close();
} catch (IOException ioe) {
"Failed to call the method close():" + ioe);
}
}
}
//--------------------------------------------------------------------
// Private stuff - Serialization
//--------------------------------------------------------------------
/**
* <p>In order to be usable, an IIOP stub must be connected to an ORB.
* The stub is automatically connected to the ORB if:
* <ul>
* <li> It was returned by the COS naming</li>
* <li> Its server counterpart has been registered in COS naming
* through JNDI.</li>
* </ul>
* Otherwise, it is not connected. A stub which is deserialized
* from Jini is not connected. A stub which is obtained from a
* non registered RMIIIOPServerImpl is not a connected.<br>
* A stub which is not connected can't be serialized, and thus
* can't be registered in Jini. A stub which is not connected can't
* be used to invoke methods on the server.
* <p>
* In order to palliate this, this method will connect the
* given stub if it is not yet connected. If the given
* <var>RMIServer</var> is not an instance of
* {@link javax.rmi.CORBA.Stub javax.rmi.CORBA.Stub}, then the
* method do nothing and simply returns that stub. Otherwise,
* this method will attempt to connect the stub to an ORB as
* follows:
* <ul>
* <p>This method looks in the provided <var>environment</var> for
* the "java.naming.corba.orb" property. If it is found, the
* referenced object (an {@link org.omg.CORBA.ORB ORB}) is used to
* connect the stub. Otherwise, a new org.omg.CORBA.ORB is created
* by calling {@link
* org.omg.CORBA.ORB#init(String[], Properties)
* org.omg.CORBA.ORB.init((String[])null,(Properties)null)}
* <p>The new created ORB is kept in a static
* {@link WeakReference} and can be reused for connecting other
* stubs. However, no reference is ever kept on the ORB provided
* in the <var>environment</var> map, if any.
* </ul>
* @param rmiServer A RMI Server Stub.
* @param environment An environment map, possibly containing an ORB.
* @return the given stub.
* @exception IllegalArgumentException if the
* <tt>java.naming.corba.orb</tt> property is specified and
* does not point to an {@link org.omg.CORBA.ORB ORB}.
* @exception IOException if the connection to the ORB failed.
**/
throws IOException {
try {
} catch (UnsupportedOperationException x) {
// BAD_OPERATION
}
}
return rmiServer;
}
/**
* Get the ORB specified by <var>environment</var>, or create a
* new one.
* <p>This method looks in the provided <var>environment</var> for
* the "java.naming.corba.orb" property. If it is found, the
* referenced object (an {@link org.omg.CORBA.ORB ORB}) is
* returned. Otherwise, a new org.omg.CORBA.ORB is created
* by calling {@link
* org.omg.CORBA.ORB#init(String[], java.util.Properties)
* org.omg.CORBA.ORB.init((String[])null,(Properties)null)}
* <p>The new created ORB is kept in a static
* {@link WeakReference} and can be reused for connecting other
* stubs. However, no reference is ever kept on the ORB provided
* in the <var>environment</var> map, if any.
* @param environment An environment map, possibly containing an ORB.
* @return An ORB.
* @exception IllegalArgumentException if the
* <tt>java.naming.corba.orb</tt> property is specified and
* does not point to an {@link org.omg.CORBA.ORB ORB}.
* @exception IOException if the ORB initialization failed.
**/
throws IOException {
if (environment != null) {
" must be an instance of org.omg.CORBA.ORB.");
}
return newOrb;
}
/**
* Read RMIConnector fields from an {@link java.io.ObjectInputStream
* ObjectInputStream}.
* Calls <code>s.defaultReadObject()</code> and then initializes
* all transient variables that need initializing.
* @param s The ObjectInputStream to read from.
* @exception InvalidObjectException if none of <var>rmiServer</var> stub
* or <var>jmxServiceURL</var> are set.
* @see #RMIConnector(JMXServiceURL,Map)
* @see #RMIConnector(RMIServer,Map)
**/
throws IOException, ClassNotFoundException {
s.defaultReadObject();
InvalidObjectException("rmiServer and jmxServiceURL both null");
}
/**
* Writes the RMIConnector fields to an {@link java.io.ObjectOutputStream
* ObjectOutputStream}.
* <p>Connects the underlying RMIServer stub to an ORB, if needed,
* before serializing it. This is done using the environment
* map that was provided to the constructor, if any, and as documented
* in {@link javax.management.remote.rmi}.</p>
* <p>This method then calls <code>s.defaultWriteObject()</code>.
* Usually, <var>rmiServer</var> is null if this object
* was constructed with a JMXServiceURL, and <var>jmxServiceURL</var>
* is null if this object is constructed with a RMIServer stub.
* <p>Note that the environment Map is not serialized, since the objects
* it contains are assumed to be contextual and relevant only
* with respect to the local environment (class loader, ORB, etc...).</p>
* <p>After an RMIConnector is deserialized, it is assumed that the
* user will call {@link #connect(Map)}, providing a new Map that
* can contain values which are contextually relevant to the new
* local environment.</p>
* <p>Since connection to the ORB is needed prior to serializing, and
* since the ORB to connect to is one of those contextual parameters,
* it is not recommended to re-serialize a just de-serialized object -
* as the de-serialized object has no map. Thus, when an RMIConnector
* object is needed for serialization or transmission to a remote
* application, it is recommended to obtain a new RMIConnector stub
* by calling {@link RMIConnectorServer#toJMXConnector(Map)}.</p>
* @param s The ObjectOutputStream to write to.
* @exception InvalidObjectException if none of <var>rmiServer</var> stub
* or <var>jmxServiceURL</var> are set.
* @see #RMIConnector(JMXServiceURL,Map)
* @see #RMIConnector(RMIServer,Map)
**/
throws IOException {
InvalidObjectException("rmiServer and jmxServiceURL both null.");
s.defaultWriteObject();
}
// Initialization of transient variables.
private void initTransients() {
connected = false;
terminated = false;
}
//--------------------------------------------------------------------
// Private stuff - Check if stub can be trusted.
//--------------------------------------------------------------------
// Check remote stub is from the expected class.
//
throw new SecurityException(
} else {
throw new SecurityException(
"Expecting a dynamic proxy instance with a " +
RemoteObjectInvocationHandler.class.getName() +
" invocation handler!");
else
}
}
// Check RemoteRef in stub is from the expected class
// "sun.rmi.server.UnicastRef2".
//
throw new SecurityException(
" remote reference in stub!");
// Check RMIClientSocketFactory in stub is from the expected class
// "javax.rmi.ssl.SslRMIClientSocketFactory".
//
throw new SecurityException(
" RMI client socket factory in stub!");
}
//--------------------------------------------------------------------
// Private stuff - RMIServer creation
//--------------------------------------------------------------------
throws NamingException, IOException {
if (isIiop) {
// Make sure java.naming.corba.orb is in the Map.
}
if (!IIOPHelper.isAvailable())
throw new IOException("iiop protocol not available");
} else {
"or /ior/: " + path;
throw new MalformedURLException(msg);
}
}
/**
* Lookup the RMIServer stub in a directory.
* @param jndiURL A JNDI URL indicating the location of the Stub
* (see {@link javax.management.remote.rmi}), e.g.:
* <ul><li><tt>rmi://registry-host:port/rmi-stub-name</tt></li>
* <li>or <tt>iiop://cosnaming-host:port/iiop-stub-name</tt></li>
* <li>or <tt>ldap://ldap-host:port/java-container-dn</tt></li>
* </ul>
* @param env the environment Map passed to the connector.
* @param isIiop true if the stub is expected to be an IIOP stub.
* @return The retrieved RMIServer stub.
* @exception NamingException if the stub couldn't be found.
**/
boolean isIiop)
throws NamingException {
if (isIiop)
return narrowIIOPServer(objref);
else
return narrowJRMPServer(objref);
}
}
try {
} catch (ClassCastException e) {
objref + ": " + e);
return null;
}
}
// could forbid "rmi:" URL here -- but do we need to?
}
throws IOException {
// could forbid "iiop:" URL here -- but do we need to?
final byte[] serialized;
try {
} catch (IllegalArgumentException e) {
throw new MalformedURLException("Bad BASE64 encoding: " +
e.getMessage());
}
final ObjectInputStream oin =
new ObjectInputStream(bin) :
try {
} catch (ClassNotFoundException e) {
throw new MalformedURLException("Class not found: " + e);
}
}
private static final class ObjectInputStreamWithLoader
extends ObjectInputStream {
throws IOException {
super(in);
}
throws IOException, ClassNotFoundException {
}
}
/*
The following section of code avoids a class loading problem
with RMI. The problem is that an RMI stub, when deserializing
a remote method return value or exception, will first of all
consult the first non-bootstrap class loader it finds in the
call stack. This can lead to behavior that is not portable
between implementations of the JMX Remote API. Notably, an
implementation on J2SE 1.4 will find the RMI stub's loader on
the stack. But in J2SE 5, this stub is loaded by the
bootstrap loader, so RMI will find the loader of the user code
that called an MBeanServerConnection method.
To avoid this problem, we take advantage of what the RMI stub
is doing internally. Each remote call will end up calling
ref.invoke(...), where ref is the RemoteRef parameter given to
the RMI stub's constructor. It is within this call that the
deserialization will happen. So we fabricate our own RemoteRef
that delegates everything to the "real" one but that is loaded
by a class loader that knows no other classes. The class
loader NoCallStackClassLoader does this: the RemoteRef is an
instance of the class named by proxyRefClassName, which is
fabricated by the class loader using byte code that is defined
by the string below.
The call stack when the deserialization happens is thus this:
MBeanServerConnection.getAttribute (or whatever)
-> RMIConnectionImpl_Stub.getAttribute
-> ProxyRef.invoke(...getAttribute...)
-> UnicastRef.invoke(...getAttribute...)
-> internal RMI stuff
Here UnicastRef is the RemoteRef created when the stub was
deserialized (which is of some RMI internal class). It and the
"internal RMI stuff" are loaded by the bootstrap loader, so are
transparent to the stack search. The first non-bootstrap
loader found is our ProxyRefLoader, as required.
In a future version of this code as integrated into J2SE 5,
this workaround could be replaced by direct access to the
internals of RMI. For now, we use the same code base for J2SE
and for the standalone Reference Implementation.
The byte code below encodes the following class, compiled using
J2SE 1.4.2 with the -g:none option.
package com.sun.jmx.remote.internal;
import java.lang.reflect.Method;
import java.rmi.Remote;
import java.rmi.server.RemoteRef;
import com.sun.jmx.remote.internal.ProxyRef;
public class PRef extends ProxyRef {
public PRef(RemoteRef ref) {
super(ref);
}
public Object invoke(Remote obj, Method method,
Object[] params, long opnum)
throws Exception {
return ref.invoke(obj, method, params, opnum);
}
}
*/
"com.sun.jmx.remote.internal.PRef";
static {
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"+
"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/"+
"\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"+
"\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"+
"\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";
final byte[] pRefByteCode =
new PrivilegedExceptionAction<Constructor<?>>() {
return c.getConstructor(RemoteRef.class);
}
};
Class<?> serverStubClass;
try {
} catch (Exception e) {
"Failed to instantiate " +
rmiServerImplStubClassName + ": " + e);
}
Constructor<?> constr;
try {
} catch (Exception e) {
"Failed to initialize proxy reference constructor "+
}
}
final Constructor<?> rmiConnectionImplStubConstructor =
return proxyStub;
}
/*
can't easily insert an object between the RMIConnection stub
org.omg.CORBA_2_3.portable.OutputStream for each request, and
writes the parameters to it. Then it calls
_invoke(OutputStream) which it inherits from CORBA's
ObjectImpl. That returns an
org.omg.CORBA_2_3.portable.InputStream. The return value is
read from this InputStream. So the stack during
deserialization looks like this:
MBeanServerConnection.getAttribute (or whatever)
-> _RMIConnection_Stub.getAttribute
-> Util.readAny (a CORBA method)
-> InputStream.read_any
-> internal CORBA stuff
What we would have *liked* to have done would be the same thing
org.omg.CORBA.portable.Delegate that simply forwards every
operation to the real original Delegate from the RMIConnection
stub, except that the InputStream returned by _invoke is
wrapped by a "ProxyInputStream" that is loaded by our
NoCallStackClassLoader.
Unfortunately, this doesn't work, at least with Sun's J2SE
1.4.2, because the CORBA code is not designed to allow you to
change Delegates arbitrarily. You get a ClassCastException
from code that expects the Delegate to implement an internal
interface.
So instead we do the following. We create a subclass of the
stub that overrides the _invoke method so as to wrap the
returned InputStream in a ProxyInputStream. We create a
subclass of ProxyInputStream using the NoCallStackClassLoader
and override its read_any and read_value(Class) methods.
(These are the only methods called during deserialization of
MBeanServerConnection return values.) We extract the Delegate
from the original stub and insert it into our subclass stub,
and away we go. The state of a stub consists solely of its
Delegate.
We also need to catch ApplicationException, which will encode
any exceptions declared in the throws clause of the called
method. Its InputStream needs to be wrapped in a
ProxyInputSteam too.
We override _releaseReply in the stub subclass so that it
replaces a ProxyInputStream argument with the original
InputStream. This avoids problems if the implementation of
_releaseReply ends up casting this InputStream to an
implementation-specific interface (which in Sun's J2SE 5 it
does).
It is not strictly necessary for the stub subclass to be loaded
by a NoCallStackClassLoader, since the call-stack search stops
at the ProxyInputStream subclass. However, it is convenient
for two reasons. One is that it means that the
ProxyInputStream subclass can be accessed directly, without
using reflection. The other is that it avoids build problems,
since usually stubs are created after other classes are
compiled, so we can't access them from this class without,
again, using reflection.
The strings below encode the following two Java classes,
compiled using javac -g:none.
package com.sun.jmx.remote.protocol.iiop;
import org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub;
import org.omg.CORBA.portable.ApplicationException;
import org.omg.CORBA.portable.InputStream;
import org.omg.CORBA.portable.OutputStream;
import org.omg.CORBA.portable.RemarshalException;
public class ProxyStub extends _RMIConnection_Stub {
public InputStream _invoke(OutputStream out)
throws ApplicationException, RemarshalException {
try {
return new PInputStream(super._invoke(out));
} catch (ApplicationException e) {
InputStream pis = new PInputStream(e.getInputStream());
throw new ApplicationException(e.getId(), pis);
}
}
public void _releaseReply(InputStream in) {
if (in != null)
in = ((PInputStream)in).getProxiedInputStream();
super._releaseReply(in);
}
}
package com.sun.jmx.remote.protocol.iiop;
public class PInputStream extends ProxyInputStream {
public PInputStream(org.omg.CORBA.portable.InputStream in) {
super(in);
}
public org.omg.CORBA.Any read_any() {
return in.read_any();
}
public java.io.Serializable read_value(Class clz) {
return narrow().read_value(clz);
}
}
*/
"org.omg.stub.javax.management.remote.rmi._RMIConnection_Stub";
"com.sun.jmx.remote.protocol.iiop.ProxyStub";
"com.sun.jmx.remote.protocol.iiop.ProxyInputStream";
"com.sun.jmx.remote.protocol.iiop.PInputStream";
static {
final String proxyStubByteCodeString =
"\312\376\272\276\0\0\0\63\0+\12\0\14\0\30\7\0\31\12\0\14\0\32\12"+
"\0\2\0\33\7\0\34\12\0\5\0\35\12\0\5\0\36\12\0\5\0\37\12\0\2\0 "+
"\12\0\14\0!\7\0\"\7\0#\1\0\6<init>\1\0\3()V\1\0\4Code\1\0\7_in"+
"/portable/InputStream;\1\0\15StackMapTable\7\0\34\1\0\12Except"+
"ble/ApplicationException\14\0%\0&\14\0'\0(\14\0\15\0)\14\0*\0&"+
"b\1\0)org/omg/CORBA/portable/RemarshalException\1\0\16getInput"+
"Stream\1\0&()Lorg/omg/CORBA/portable/InputStream;\1\0\5getId\1"+
"A/portable/InputStream;)V\1\0\25getProxiedInputStream\0!\0\13\0"+
"\14\0\0\0\0\0\3\0\1\0\15\0\16\0\1\0\17\0\0\0\21\0\1\0\1\0\0\0\5"+
"*\267\0\1\261\0\0\0\0\0\1\0\20\0\21\0\2\0\17\0\0\0G\0\4\0\4\0\0"+
"\0'\273\0\2Y*+\267\0\3\267\0\4\260M\273\0\2Y,\266\0\6\267\0\4N"+
"\273\0\5Y,\266\0\7-\267\0\10\277\0\1\0\0\0\14\0\15\0\5\0\1\0\22"+
"\0\0\0\6\0\1M\7\0\23\0\24\0\0\0\6\0\2\0\5\0\25\0\1\0\26\0\27\0"+
"\1\0\17\0\0\0'\0\2\0\2\0\0\0\22+\306\0\13+\300\0\2\266\0\11L*+"+
"\267\0\12\261\0\0\0\1\0\22\0\0\0\3\0\1\14\0\0";
final String pInputStreamByteCodeString =
"\312\376\272\276\0\0\0\63\0\36\12\0\7\0\17\11\0\6\0\20\12\0\21"+
"\0\22\12\0\6\0\23\12\0\24\0\25\7\0\26\7\0\27\1\0\6<init>\1\0'("+
"Lorg/omg/CORBA/portable/InputStream;)V\1\0\4Code\1\0\10read_an"+
"/Class;)Ljava/io/Serializable;\14\0\10\0\11\14\0\30\0\31\7\0\32"+
"le/InputStream;\1\0\"org/omg/CORBA/portable/InputStream\1\0\6n"+
"arrow\1\0*()Lorg/omg/CORBA_2_3/portable/InputStream;\1\0&org/o"+
"mg/CORBA_2_3/portable/InputStream\0!\0\6\0\7\0\0\0\0\0\3\0\1\0"+
"\10\0\11\0\1\0\12\0\0\0\22\0\2\0\2\0\0\0\6*+\267\0\1\261\0\0\0"+
"\0\0\1\0\13\0\14\0\1\0\12\0\0\0\24\0\1\0\1\0\0\0\10*\264\0\2\266"+
"\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"+
"\11*\266\0\4+\266\0\5\260\0\0\0\0\0\0";
final byte[] proxyStubByteCode =
final byte[] pInputStreamByteCode =
final String[] otherClassNames = {
};
if (IIOPHelper.isAvailable()) {
new PrivilegedExceptionAction<Class<?>>() {
}
};
try {
} catch (Exception e) {
"Unexpected exception making shadow IIOP stub class: "+e);
}
} else {
}
}
throws InstantiationException, IllegalAccessException {
return (RMIConnection) proxyStub;
}
boolean checkStub)
throws IOException {
try {
if (c.getClass() == rmiConnectionImplStubClass)
return shadowJrmpStub((RemoteObject) c);
return shadowIiopStub(c);
"stack search for classes: class loading semantics " +
"may be incorrect");
} catch (Exception e) {
"stack search for classes: class loading semantics " +
"may be incorrect: " + e);
// so just return the original stub, which will work for all
// but the most exotic class loading situations
}
return c;
}
throw new IllegalArgumentException(
"String length must be a multiple of four.");
int missingBytesInLastGroup = 0;
int numFullGroups = numGroups;
if (sLen != 0) {
}
}
// Translate all full groups from base64 to byte array elements
for (int i=0; i<numFullGroups; i++) {
}
// Translate partial group, if present
if (missingBytesInLastGroup != 0) {
if (missingBytesInLastGroup == 1) {
}
}
// assert inCursor == s.length()-missingBytesInLastGroup;
// assert outCursor == result.length;
return result;
}
/**
* Translates the specified character, which is assumed to be in the
* "Base 64 Alphabet" into its equivalent 6-bit positive integer.
*
* @throws IllegalArgumentException if
* c is not in the Base64 Alphabet.
*/
private static int base64toInt(char c) {
int result;
if (c >= base64ToInt.length)
result = -1;
else
result = base64ToInt[c];
if (result < 0)
throw new IllegalArgumentException("Illegal character " + c);
return result;
}
/**
* This array is a lookup table that translates unicode characters
* drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
* into their 6-bit positive integer equivalents. Characters that
* are not in the Base64 alphabet but fall within the bounds of the
* array are translated to -1.
*/
private static final byte base64ToInt[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
};
//--------------------------------------------------------------------
// Private stuff - Find / Set default class loader
//--------------------------------------------------------------------
if (defaultClassLoader != null)
return null;
}
});
return old;
}
return null;
}
});
}
//--------------------------------------------------------------------
// Private variables
//--------------------------------------------------------------------
/**
* @serial The RMIServer stub of the RMI JMX Connector server to
* which this client connector is (or will be) connected. This
* field can be null when <var>jmxServiceURL</var> is not
* null. This includes the case where <var>jmxServiceURL</var>
* contains a serialized RMIServer stub. If both
* <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
* serialization will fail.
*
* @see #RMIConnector(RMIServer,Map)
**/
/**
* @serial The JMXServiceURL of the RMI JMX Connector server to
* which this client connector will be connected. This field can
* be null when <var>rmiServer</var> is not null. If both
* <var>rmiServer</var> and <var>jmxServiceURL</var> are null then
* serialization will fail.
*
* @see #RMIConnector(JMXServiceURL,Map)
**/
// ---------------------------------------------------------
// WARNING - WARNING - WARNING - WARNING - WARNING - WARNING
// ---------------------------------------------------------
// Any transient variable which needs to be initialized should
// be initialized in the method initTransient()
// = new RMINotifClient(new Integer(0));
private transient boolean connected;
// = false;
private transient boolean terminated;
// = false;
/**
* A static WeakReference to an {@link org.omg.CORBA.ORB ORB} to
* connect unconnected stubs.
**/
// TRACES & DEBUG
//---------------
return "null";
else
}
}
}