1/*
2 * Javassist, a Java-bytecode translator toolkit.
3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License.  Alternatively, the contents of this file may be used under
8 * the terms of the GNU Lesser General Public License Version 2.1 or later.
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 */
15
16package javassist.tools.web;
17
18import java.io.*;
19import java.net.*;
20
21/**
22 * A sample applet viewer.
23 *
24 * <p>This is a sort of applet viewer that can run any program even if
25 * the main class is not a subclass of <code>Applet</code>.
26 * This viewwer first calls <code>main()</code> in the main class.
27 *
28 * <p>To run, you should type:
29 *
30 * <ul><code>% java javassist.tools.web.Viewer <i>host port</i> Main arg1, ...</code></ul>
31 *
32 * <p>This command calls <code>Main.main()</code> with <code>arg1,...</code>
33 * All classes including <code>Main</code> are fetched from
34 * a server http://<i>host</i>:<i>port</i>.
35 * Only the class file for <code>Viewer</code> must exist
36 * on a local file system at the client side; even other
37 * <code>javassist.*</code> classes are not needed at the client side.
38 * <code>Viewer</code> uses only Java core API classes.
39 *
40 * <p>Note: since a <code>Viewer</code> object is a class loader,
41 * a program loaded by this object can call a method in <code>Viewer</code>.
42 * For example, you can write something like this:
43 *
44 * <ul><pre>
45 * Viewer v = (Viewer)this.getClass().getClassLoader();
46 * String port = v.getPort();
47 * </pre></ul>
48 *
49 */
50public class Viewer extends ClassLoader {
51    private String server;
52    private int port;
53
54    /**
55     * Starts a program.
56     */
57    public static void main(String[] args) throws Throwable {
58        if (args.length >= 3) {
59            Viewer cl = new Viewer(args[0], Integer.parseInt(args[1]));
60            String[] args2 = new String[args.length - 3];
61            System.arraycopy(args, 3, args2, 0, args.length - 3);
62            cl.run(args[2], args2);
63        }
64        else
65            System.err.println(
66        "Usage: java javassist.tools.web.Viewer <host> <port> class [args ...]");
67    }
68
69    /**
70     * Constructs a viewer.
71     *
72     * @param host              server name
73     * @param p                 port number
74     */
75    public Viewer(String host, int p) {
76        server = host;
77        port = p;
78    }
79
80    /**
81     * Returns the server name.
82     */
83    public String getServer() { return server; }
84
85    /**
86     * Returns the port number.
87     */
88    public int getPort() { return port; }
89
90    /**
91     * Invokes main() in the class specified by <code>classname</code>.
92     *
93     * @param classname         executed class
94     * @param args              the arguments passed to <code>main()</code>.
95     */
96    public void run(String classname, String[] args)
97        throws Throwable
98    {
99        Class c = loadClass(classname);
100        try {
101            c.getDeclaredMethod("main", new Class[] { String[].class })
102                .invoke(null, new Object[] { args });
103        }
104        catch (java.lang.reflect.InvocationTargetException e) {
105            throw e.getTargetException();
106        }
107    }
108
109    /**
110     * Requests the class loader to load a class.
111     */
112    protected synchronized Class loadClass(String name, boolean resolve)
113        throws ClassNotFoundException
114    {
115        Class c = findLoadedClass(name);
116        if (c == null)
117            c = findClass(name);
118
119        if (c == null)
120            throw new ClassNotFoundException(name);
121
122        if (resolve)
123            resolveClass(c);
124
125        return c;
126    }
127
128    /**
129     * Finds the specified class.  The implementation in this class
130     * fetches the class from the http server.  If the class is
131     * either <code>java.*</code>, <code>javax.*</code>, or
132     * <code>Viewer</code>, then it is loaded by the parent class
133     * loader.
134     *
135     * <p>This method can be overridden by a subclass of
136     * <code>Viewer</code>.
137     */
138    protected Class findClass(String name) throws ClassNotFoundException {
139        Class c = null;
140        if (name.startsWith("java.") || name.startsWith("javax.")
141            || name.equals("javassist.tools.web.Viewer"))
142            c = findSystemClass(name);
143
144        if (c == null)
145            try {
146                byte[] b = fetchClass(name);
147                if (b != null)
148                    c = defineClass(name, b, 0, b.length);
149            }
150        catch (Exception e) {
151        }
152
153        return c;
154    }
155
156    /**
157     * Fetches the class file of the specified class from the http
158     * server.
159     */
160    protected byte[] fetchClass(String classname) throws Exception
161    {
162        byte[] b;
163        URL url = new URL("http", server, port,
164                          "/" + classname.replace('.', '/') + ".class");
165        URLConnection con = url.openConnection();
166        con.connect();
167        int size = con.getContentLength();
168        InputStream s = con.getInputStream();
169        if (size <= 0)
170            b = readStream(s);
171        else {
172            b = new byte[size];
173            int len = 0;
174            do {
175                int n = s.read(b, len, size - len);
176                if (n < 0) {
177                    s.close();
178                    throw new IOException("the stream was closed: "
179                                          + classname);
180                }
181                len += n;
182            } while (len < size);
183        }
184
185        s.close();
186        return b;
187    }
188
189    private byte[] readStream(InputStream fin) throws IOException {
190        byte[] buf = new byte[4096];
191        int size = 0;
192        int len = 0;
193        do {
194            size += len;
195            if (buf.length - size <= 0) {
196                byte[] newbuf = new byte[buf.length * 2];
197                System.arraycopy(buf, 0, newbuf, 0, size);
198                buf = newbuf;
199            }
200
201            len = fin.read(buf, size, buf.length - size);
202        } while (len >= 0);
203
204        byte[] result = new byte[size];
205        System.arraycopy(buf, 0, result, 0, size);
206        return result;
207    }
208}
209