/*
* 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.web;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
/**
* A sample applet viewer.
*
* <p>This is a sort of applet viewer that can run any program even if
* the main class is not a subclass of <code>Applet</code>.
* This viewwer first calls <code>main()</code> in the main class.
*
* <p>To run, you should type:
*
* <pre>% java javassist.tools.web.Viewer <i>host port</i> Main arg1, ...</pre>
*
* <p>This command calls <code>Main.main()</code> with <code>arg1,...</code>
* All classes including <code>Main</code> are fetched from
* a server http://<i>host</i>:<i>port</i>.
* Only the class file for <code>Viewer</code> must exist
* on a local file system at the client side; even other
* <code>javassist.*</code> classes are not needed at the client side.
* <code>Viewer</code> uses only Java core API classes.
*
* <p>Note: since a <code>Viewer</code> object is a class loader,
* a program loaded by this object can call a method in <code>Viewer</code>.
* For example, you can write something like this:
*
* <pre>
* Viewer v = (Viewer)this.getClass().getClassLoader();
* String port = v.getPort();
* </pre>
*
*/
public class Viewer extends ClassLoader {
private String server;
private int port;
/**
* Starts a program.
*/
public static void main(String[] args) throws Throwable {
if (args.length >= 3) {
Viewer cl = new Viewer(args[0], Integer.parseInt(args[1]));
String[] args2 = new String[args.length - 3];
System.arraycopy(args, 3, args2, 0, args.length - 3);
cl.run(args[2], args2);
}
else
System.err.println(
"Usage: java javassist.tools.web.Viewer <host> <port> class [args ...]");
}
/**
* Constructs a viewer.
*
* @param host server name
* @param p port number
*/
public Viewer(String host, int p) {
server = host;
port = p;
}
/**
* Returns the server name.
*/
public String getServer() { return server; }
/**
* Returns the port number.
*/
public int getPort() { return port; }
/**
* Invokes main() in the class specified by <code>classname</code>.
*
* @param classname executed class
* @param args the arguments passed to <code>main()</code>.
*/
public void run(String classname, String[] args)
throws Throwable
{
Class c = loadClass(classname);
try {
c.getDeclaredMethod("main", new Class[] { String[].class })
.invoke(null, new Object[] { args });
}
catch (java.lang.reflect.InvocationTargetException e) {
throw e.getTargetException();
}
}
/**
* Requests the class loader to load a class.
*/
protected synchronized Class loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
Class c = findLoadedClass(name);
if (c == null)
c = findClass(name);
if (c == null)
throw new ClassNotFoundException(name);
if (resolve)
resolveClass(c);
return c;
}
/**
* Finds the specified class. The implementation in this class
* fetches the class from the http server. If the class is
* either <code>java.*</code>, <code>javax.*</code>, or
* <code>Viewer</code>, then it is loaded by the parent class
* loader.
*
* <p>This method can be overridden by a subclass of
* <code>Viewer</code>.
*/
protected Class findClass(String name) throws ClassNotFoundException {
Class c = null;
if (name.startsWith("java.") || name.startsWith("javax.")
|| name.equals("javassist.tools.web.Viewer"))
c = findSystemClass(name);
if (c == null)
try {
byte[] b = fetchClass(name);
if (b != null)
c = defineClass(name, b, 0, b.length);
}
catch (Exception e) {
}
return c;
}
/**
* Fetches the class file of the specified class from the http
* server.
*/
protected byte[] fetchClass(String classname) throws Exception
{
byte[] b;
URL url = new URL("http", server, port,
"/" + classname.replace('.', '/') + ".class");
URLConnection con = url.openConnection();
con.connect();
int size = con.getContentLength();
InputStream s = con.getInputStream();
if (size <= 0)
b = readStream(s);
else {
b = new byte[size];
int len = 0;
do {
int n = s.read(b, len, size - len);
if (n < 0) {
s.close();
throw new IOException("the stream was closed: "
+ classname);
}
len += n;
} while (len < size);
}
s.close();
return b;
}
private byte[] readStream(InputStream fin) throws IOException {
byte[] buf = new byte[4096];
int size = 0;
int len = 0;
do {
size += len;
if (buf.length - size <= 0) {
byte[] newbuf = new byte[buf.length * 2];
System.arraycopy(buf, 0, newbuf, 0, size);
buf = newbuf;
}
len = fin.read(buf, size, buf.length - size);
} while (len >= 0);
byte[] result = new byte[size];
System.arraycopy(buf, 0, result, 0, size);
return result;
}
}