scouter.javassist.tools.rmi.ObjectImporter Maven / Gradle / Ivy
/*
* Javassist, a Java-bytecode translator toolkit.
* Copyright (C) 1999- Shigeru Chiba. All Rights Reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. Alternatively, the contents of this file may be used under
* the terms of the GNU Lesser General Public License Version 2.1 or later,
* or the Apache License Version 2.0.
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*/
package scouter.javassist.tools.rmi;
import java.applet.Applet;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.net.Socket;
import java.net.URL;
import scouter.javassist.tools.rmi.ObjectNotFoundException;
import scouter.javassist.tools.rmi.Proxy;
import scouter.javassist.tools.rmi.RemoteException;
import scouter.javassist.tools.rmi.RemoteRef;
/**
* The object importer enables applets to call a method on a remote
* object running on the Webserver
(the main class of this
* package).
*
* To access the remote
* object, the applet first calls lookupObject()
and
* obtains a proxy object, which is a reference to that object.
* The class name of the proxy object is identical to that of
* the remote object.
* The proxy object provides the same set of methods as the remote object.
* If one of the methods is invoked on the proxy object,
* the invocation is delegated to the remote object.
* From the viewpoint of the applet, therefore, the two objects are
* identical. The applet can access the object on the server
* with the regular Java syntax without concern about the actual
* location.
*
*
The methods remotely called by the applet must be public
.
* This is true even if the applet's class and the remote object's classs
* belong to the same package.
*
*
If class X is a class of remote objects, a subclass of X must be
* also a class of remote objects. On the other hand, this restriction
* is not applied to the superclass of X. The class X does not have to
* contain a constructor taking no arguments.
*
*
The parameters to a remote method is passed in the call-by-value
* manner. Thus all the parameter classes must implement
* java.io.Serializable
. However, if the parameter is the
* proxy object, the reference to the remote object instead of a copy of
* the object is passed to the method.
*
*
Because of the limitations of the current implementation,
*
* - The parameter objects cannot contain the proxy
* object as a field value.
*
- If class
C
is of the remote object, then
* the applet cannot instantiate C
locally or remotely.
*
*
* All the exceptions thrown by the remote object are converted
* into RemoteException
. Since this exception is a subclass
* of RuntimeException
, the caller method does not need
* to catch the exception. However, good programs should catch
* the RuntimeException
.
*
* @see scouter.javassist.tools.rmi.AppletServer
* @see scouter.javassist.tools.rmi.RemoteException
* @see scouter.javassist.tools.web.Viewer
*/
public class ObjectImporter implements java.io.Serializable {
private final byte[] endofline = { 0x0d, 0x0a };
private String servername, orgServername;
private int port, orgPort;
protected byte[] lookupCommand = "POST /lookup HTTP/1.0".getBytes();
protected byte[] rmiCommand = "POST /rmi HTTP/1.0".getBytes();
/**
* Constructs an object importer.
*
*
Remote objects are imported from the web server that the given
* applet has been loaded from.
*
* @param applet the applet loaded from the Webserver
.
*/
public ObjectImporter(Applet applet) {
URL codebase = applet.getCodeBase();
orgServername = servername = codebase.getHost();
orgPort = port = codebase.getPort();
}
/**
* Constructs an object importer.
*
*
If you run a program with javassist.tools.web.Viewer
,
* you can construct an object importer as follows:
*
*
* Viewer v = (Viewer)this.getClass().getClassLoader();
* ObjectImporter oi = new ObjectImporter(v.getServer(), v.getPort());
*
*
* @see scouter.javassist.tools.web.Viewer
*/
public ObjectImporter(String servername, int port) {
this.orgServername = this.servername = servername;
this.orgPort = this.port = port;
}
/**
* Finds the object exported by a server with the specified name.
* If the object is not found, this method returns null.
*
* @param name the name of the exported object.
* @return the proxy object or null.
*/
public Object getObject(String name) {
try {
return lookupObject(name);
}
catch (ObjectNotFoundException e) {
return null;
}
}
/**
* Sets an http proxy server. After this method is called, the object
* importer connects a server through the http proxy server.
*/
public void setHttpProxy(String host, int port) {
String proxyHeader = "POST http://" + orgServername + ":" + orgPort;
String cmd = proxyHeader + "/lookup HTTP/1.0";
lookupCommand = cmd.getBytes();
cmd = proxyHeader + "/rmi HTTP/1.0";
rmiCommand = cmd.getBytes();
this.servername = host;
this.port = port;
}
/**
* Finds the object exported by the server with the specified name.
* It sends a POST request to the server (via an http proxy server
* if needed).
*
* @param name the name of the exported object.
* @return the proxy object.
*/
public Object lookupObject(String name) throws ObjectNotFoundException
{
try {
Socket sock = new Socket(servername, port);
OutputStream out = sock.getOutputStream();
out.write(lookupCommand);
out.write(endofline);
out.write(endofline);
ObjectOutputStream dout = new ObjectOutputStream(out);
dout.writeUTF(name);
dout.flush();
InputStream in = new BufferedInputStream(sock.getInputStream());
skipHeader(in);
ObjectInputStream din = new ObjectInputStream(in);
int n = din.readInt();
String classname = din.readUTF();
din.close();
dout.close();
sock.close();
if (n >= 0)
return createProxy(n, classname);
}
catch (Exception e) {
e.printStackTrace();
throw new ObjectNotFoundException(name, e);
}
throw new ObjectNotFoundException(name);
}
private static final Class[] proxyConstructorParamTypes
= new Class[] { ObjectImporter.class, int.class };
private Object createProxy(int oid, String classname) throws Exception {
Class c = Class.forName(classname);
Constructor cons = c.getConstructor(proxyConstructorParamTypes);
return cons.newInstance(new Object[] { this, new Integer(oid) });
}
/**
* Calls a method on a remote object.
* It sends a POST request to the server (via an http proxy server
* if needed).
*
* This method is called by only proxy objects.
*/
public Object call(int objectid, int methodid, Object[] args)
throws RemoteException
{
boolean result;
Object rvalue;
String errmsg;
try {
/* This method establishes a raw tcp connection for sending
* a POST message. Thus the object cannot communicate a
* remote object beyond a fire wall. To avoid this problem,
* the connection should be established with a mechanism
* collaborating a proxy server. Unfortunately, java.lang.URL
* does not seem to provide such a mechanism.
*
* You might think that using HttpURLConnection is a better
* way than constructing a raw tcp connection. Unfortunately,
* URL.openConnection() does not return an HttpURLConnection
* object in Netscape's JVM. It returns a
* netscape.net.URLConnection object.
*
* lookupObject() has the same problem.
*/
Socket sock = new Socket(servername, port);
OutputStream out = new BufferedOutputStream(
sock.getOutputStream());
out.write(rmiCommand);
out.write(endofline);
out.write(endofline);
ObjectOutputStream dout = new ObjectOutputStream(out);
dout.writeInt(objectid);
dout.writeInt(methodid);
writeParameters(dout, args);
dout.flush();
InputStream ins = new BufferedInputStream(sock.getInputStream());
skipHeader(ins);
ObjectInputStream din = new ObjectInputStream(ins);
result = din.readBoolean();
rvalue = null;
errmsg = null;
if (result)
rvalue = din.readObject();
else
errmsg = din.readUTF();
din.close();
dout.close();
sock.close();
if (rvalue instanceof RemoteRef) {
RemoteRef ref = (RemoteRef)rvalue;
rvalue = createProxy(ref.oid, ref.classname);
}
}
catch (ClassNotFoundException e) {
throw new RemoteException(e);
}
catch (IOException e) {
throw new RemoteException(e);
}
catch (Exception e) {
throw new RemoteException(e);
}
if (result)
return rvalue;
else
throw new RemoteException(errmsg);
}
private void skipHeader(InputStream in) throws IOException {
int len;
do {
int c;
len = 0;
while ((c = in.read()) >= 0 && c != 0x0d)
++len;
in.read(); /* skip 0x0a (LF) */
} while (len > 0);
}
private void writeParameters(ObjectOutputStream dout, Object[] params)
throws IOException
{
int n = params.length;
dout.writeInt(n);
for (int i = 0; i < n; ++i)
if (params[i] instanceof Proxy) {
Proxy p = (Proxy)params[i];
dout.writeObject(new RemoteRef(p._getObjectId()));
}
else
dout.writeObject(params[i]);
}
}