diff --git a/framework/src/com/phonegap/CallbackServer.java b/framework/src/com/phonegap/CallbackServer.java new file mode 100755 index 00000000..722444f0 --- /dev/null +++ b/framework/src/com/phonegap/CallbackServer.java @@ -0,0 +1,224 @@ +package com.phonegap; + +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.LinkedList; + +/** + * This class provides a way for Java to run JavaScript in the web page that has loaded PhoneGap. + * The CallbackServer class implements an XHR server and a list of JavaScript statements + * that are to be executed on the web page. + * + * The process flow is: + * 1. JavaScript makes an async XHR call. + * 2. The server holds the connection open until data is available. + * 3. The server writes the data to the client and closes the connection. + * 4. The server immediately starts listening for the next XHR call. + * 5. The client receives this XHR response, processes it. + * 6. The client sends a new async XHR request. + * + */ +public class CallbackServer implements Runnable { + + /** + * The list of JavaScript statements to be sent to JavaScript. + */ + private LinkedList javascript; + + /** + * The port to listen on. + */ + private int port; + + /** + * The server thread. + */ + private Thread serverThread; + + /** + * Indicates the server is running. + */ + private boolean active; + + /** + * Indicates that the JavaScript statements list is empty + */ + private boolean empty; + + /** + * Constructor. + */ + public CallbackServer() { + //System.out.println("CallbackServer()"); + this.active = false; + this.empty = true; + this.port = 0; + this.javascript = new LinkedList(); + this.startServer(); + } + + /** + * Get the port that this server is running on. + * + * @return + */ + public int getPort() { + return this.port; + } + + /** + * Start the server on a new thread. + */ + public void startServer() { + //System.out.println("CallbackServer.startServer()"); + this.active = false; + + // Start server on new thread + this.serverThread = new Thread(this); + this.serverThread.start(); + } + + /** + * Restart the server on a new thread. + */ + public void restartServer() { + + // Stop server + this.stopServer(); + + // Start server again + this.startServer(); + } + + /** + * Start running the server. + * This is called automatically when the server thread is started. + */ + public void run() { + + // Start server + try { + this.active = true; + String request; + ServerSocket waitSocket = new ServerSocket(0); + this.port = waitSocket.getLocalPort(); + //System.out.println(" -- using port " +this.port); + + while (this.active) { + //System.out.println("CallbackServer: Waiting for data on socket"); + Socket connection = waitSocket.accept(); + BufferedReader xhrReader = new BufferedReader(new InputStreamReader(connection.getInputStream()),40); + DataOutputStream output = new DataOutputStream(connection.getOutputStream()); + request = xhrReader.readLine(); + //System.out.println("Request="+request); + if(request.contains("GET")) + { + //System.out.println(" -- Processing GET request"); + + // Wait until there is some data to send, or send empty data every 30 sec + // to prevent XHR timeout on the client + synchronized (this) { + while (this.empty) { + try { + this.wait(30000); // prevent timeout from happening + //System.out.println(">>> break <<<"); + break; + } + catch (Exception e) { } + } + } + + // If server is still running + if (this.active) { + + // If no data, then send 404 back to client before it times out + if (this.empty) { + //System.out.println(" -- sending data 0"); + output.writeBytes("HTTP/1.1 404 NO DATA\r\n\r\n"); + } + else { + //System.out.println(" -- sending item"); + output.writeBytes("HTTP/1.1 200 OK\r\n\r\n"+this.getJavascript()); + } + } + } + //System.out.println("CallbackServer: closing output"); + output.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + this.active = false; + //System.out.println("CallbackServer.startServer() - EXIT"); + } + + /** + * Stop server. + * This stops the thread that the server is running on. + */ + public void stopServer() { + //System.out.println("CallbackServer.stopServer()"); + this.active = false; + + // Break out of server wait + synchronized (this) { + this.notify(); + } + } + + /** + * Destroy + */ + public void destroy() { + this.stopServer(); + } + + /** + * Get the number of JavaScript statements. + * + * @return int + */ + public int getSize() { + int size = this.javascript.size(); + //System.out.println("getSize() = " + size); + return size; + } + + /** + * Get the next JavaScript statement and remove from list. + * + * @return String + */ + public String getJavascript() { + if (this.javascript.size() == 0) { + return null; + } + String statement = this.javascript.remove(0); + //System.out.println("CallbackServer.getJavascript() = " + statement); + if (this.javascript.size() == 0) { + synchronized (this) { + this.empty = true; + } + } + return statement; + } + + /** + * Add a JavaScript statement to the list. + * + * @param statement + */ + public void sendJavascript(String statement) { + //System.out.println("CallbackServer.sendJavascript("+statement+")"); + this.javascript.add(statement); + synchronized (this) { + this.empty = false; + this.notify(); + } + } + +}