/*
* 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 javassist.tools.rmi;
import java.io.*;
import java.net.*;
import java.applet.
Applet;
import java.lang.reflect.*;
/**
* The object importer enables applets to call a method on a remote
* object running on the <code>Webserver</code> (the <b>main</b> class of this
* package).
*
* <p>To access the remote
* object, the applet first calls <code>lookupObject()</code> 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.
*
* <p>The methods remotely called by the applet must be <code>public</code>.
* This is true even if the applet's class and the remote object's classs
* belong to the same package.
*
* <p>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.
*
* <p>The parameters to a remote method is passed in the <i>call-by-value</i>
* manner. Thus all the parameter classes must implement
* <code>java.io.Serializable</code>. 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.
*
* <p>Because of the limitations of the current implementation,
* <ul>
* <li>The parameter objects cannot contain the proxy
* object as a field value.
* <li>If class <code>C</code> is of the remote object, then
* the applet cannot instantiate <code>C</code> locally or remotely.
* </ul>
*
* <p>All the exceptions thrown by the remote object are converted
* into <code>RemoteException</code>. Since this exception is a subclass
* of <code>RuntimeException</code>, the caller method does not need
* to catch the exception. However, good programs should catch
* the <code>RuntimeException</code>.
*
* @see javassist.tools.rmi.AppletServer
* @see javassist.tools.rmi.RemoteException
* @see 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.
*
* <p>Remote objects are imported from the web server that the given
* applet has been loaded from.
*
* @param applet the applet loaded from the <code>Webserver</code>.
*/
public
ObjectImporter(
Applet applet) {
URL codebase =
applet.
getCodeBase();
orgServername =
servername =
codebase.
getHost();
orgPort =
port =
codebase.
getPort();
}
/**
* Constructs an object importer.
*
* <p>If you run a program with <code>javassist.tools.web.Viewer</code>,
* you can construct an object importer as follows:
*
* <pre>
* Viewer v = (Viewer)this.getClass().getClassLoader();
* ObjectImporter oi = new ObjectImporter(v.getServer(), v.getPort());
* </pre>
*
* @see 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).
*
* <p>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]);
}
}