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.rmi;
17
18import java.io.*;
19
20import javassist.tools.web.*;
21import javassist.CannotCompileException;
22import javassist.NotFoundException;
23import javassist.ClassPool;
24import java.lang.reflect.Method;
25import java.util.Hashtable;
26import java.util.Vector;
27
28/**
29 * An AppletServer object is a web server that an ObjectImporter
30 * communicates with.  It makes the objects specified by
31 * <code>exportObject()</code> remotely accessible from applets.
32 * If the classes of the exported objects are requested by the client-side
33 * JVM, this web server sends proxy classes for the requested classes.
34 *
35 * @see javassist.tools.rmi.ObjectImporter
36 */
37public class AppletServer extends Webserver {
38    private StubGenerator stubGen;
39    private Hashtable exportedNames;
40    private Vector exportedObjects;
41
42    private static final byte[] okHeader
43                                = "HTTP/1.0 200 OK\r\n\r\n".getBytes();
44
45    /**
46     * Constructs a web server.
47     *
48     * @param port      port number
49     */
50    public AppletServer(String port)
51        throws IOException, NotFoundException, CannotCompileException
52    {
53        this(Integer.parseInt(port));
54    }
55
56    /**
57     * Constructs a web server.
58     *
59     * @param port      port number
60     */
61    public AppletServer(int port)
62        throws IOException, NotFoundException, CannotCompileException
63    {
64        this(ClassPool.getDefault(), new StubGenerator(), port);
65    }
66
67    /**
68     * Constructs a web server.
69     *
70     * @param port      port number
71     * @param src       the source of classs files.
72     */
73    public AppletServer(int port, ClassPool src)
74        throws IOException, NotFoundException, CannotCompileException
75    {
76        this(new ClassPool(src), new StubGenerator(), port);
77    }
78
79    private AppletServer(ClassPool loader, StubGenerator gen, int port)
80        throws IOException, NotFoundException, CannotCompileException
81    {
82        super(port);
83        exportedNames = new Hashtable();
84        exportedObjects = new Vector();
85        stubGen = gen;
86        addTranslator(loader, gen);
87    }
88
89    /**
90     * Begins the HTTP service.
91     */
92    public void run() {
93        super.run();
94    }
95
96    /**
97     * Exports an object.
98     * This method produces the bytecode of the proxy class used
99     * to access the exported object.  A remote applet can load
100     * the proxy class and call a method on the exported object.
101     *
102     * @param name      the name used for looking the object up.
103     * @param obj       the exported object.
104     * @return          the object identifier
105     *
106     * @see javassist.tools.rmi.ObjectImporter#lookupObject(String)
107     */
108    public synchronized int exportObject(String name, Object obj)
109        throws CannotCompileException
110    {
111        Class clazz = obj.getClass();
112        ExportedObject eo = new ExportedObject();
113        eo.object = obj;
114        eo.methods = clazz.getMethods();
115        exportedObjects.addElement(eo);
116        eo.identifier = exportedObjects.size() - 1;
117        if (name != null)
118            exportedNames.put(name, eo);
119
120        try {
121            stubGen.makeProxyClass(clazz);
122        }
123        catch (NotFoundException e) {
124            throw new CannotCompileException(e);
125        }
126
127        return eo.identifier;
128    }
129
130    /**
131     * Processes a request from a web browser (an ObjectImporter).
132     */
133    public void doReply(InputStream in, OutputStream out, String cmd)
134        throws IOException, BadHttpRequest
135    {
136        if (cmd.startsWith("POST /rmi "))
137            processRMI(in, out);
138        else if (cmd.startsWith("POST /lookup "))
139            lookupName(cmd, in, out);
140        else
141            super.doReply(in, out, cmd);
142    }
143
144    private void processRMI(InputStream ins, OutputStream outs)
145        throws IOException
146    {
147        ObjectInputStream in = new ObjectInputStream(ins);
148
149        int objectId = in.readInt();
150        int methodId = in.readInt();
151        Exception err = null;
152        Object rvalue = null;
153        try {
154            ExportedObject eo
155                = (ExportedObject)exportedObjects.elementAt(objectId);
156            Object[] args = readParameters(in);
157            rvalue = convertRvalue(eo.methods[methodId].invoke(eo.object,
158                                                               args));
159        }
160        catch(Exception e) {
161            err = e;
162            logging2(e.toString());
163        }
164
165        outs.write(okHeader);
166        ObjectOutputStream out = new ObjectOutputStream(outs);
167        if (err != null) {
168            out.writeBoolean(false);
169            out.writeUTF(err.toString());
170        }
171        else
172            try {
173                out.writeBoolean(true);
174                out.writeObject(rvalue);
175            }
176            catch (NotSerializableException e) {
177                logging2(e.toString());
178            }
179            catch (InvalidClassException e) {
180                logging2(e.toString());
181            }
182
183        out.flush();
184        out.close();
185        in.close();
186    }
187
188    private Object[] readParameters(ObjectInputStream in)
189        throws IOException, ClassNotFoundException
190    {
191        int n = in.readInt();
192        Object[] args = new Object[n];
193        for (int i = 0; i < n; ++i) {
194            Object a = in.readObject();
195            if (a instanceof RemoteRef) {
196                RemoteRef ref = (RemoteRef)a;
197                ExportedObject eo
198                    = (ExportedObject)exportedObjects.elementAt(ref.oid);
199                a = eo.object;
200            }
201
202            args[i] = a;
203        }
204
205        return args;
206    }
207
208    private Object convertRvalue(Object rvalue)
209        throws CannotCompileException
210    {
211        if (rvalue == null)
212            return null;        // the return type is void.
213
214        String classname = rvalue.getClass().getName();
215        if (stubGen.isProxyClass(classname))
216            return new RemoteRef(exportObject(null, rvalue), classname);
217        else
218            return rvalue;
219    }
220
221    private void lookupName(String cmd, InputStream ins, OutputStream outs)
222        throws IOException
223    {
224        ObjectInputStream in = new ObjectInputStream(ins);
225        String name = DataInputStream.readUTF(in);
226        ExportedObject found = (ExportedObject)exportedNames.get(name);
227        outs.write(okHeader);
228        ObjectOutputStream out = new ObjectOutputStream(outs);
229        if (found == null) {
230            logging2(name + "not found.");
231            out.writeInt(-1);           // error code
232            out.writeUTF("error");
233        }
234        else {
235            logging2(name);
236            out.writeInt(found.identifier);
237            out.writeUTF(found.object.getClass().getName());
238        }
239
240        out.flush();
241        out.close();
242        in.close();
243    }
244}
245
246class ExportedObject {
247    public int identifier;
248    public Object object;
249    public Method[] methods;
250}
251