title: establishing communication between Java desktop app and Java browser applet via sockets
body: <p>I'm new to network I/O programming, and I've run into a snag-- basically what I want to do is have a desktop app talk to the google maps javascript API.  In order to facilitate this, I have built a java applet which will act as a bridge between the desktop app and the browser javascript app.  When I run the desktop app and applet together in Eclipse they can communicate perfectly, and I am able to invoke applet functions by writing strings to a Socket bound to the same port the applet has established a ServerSocket connection with.  For testing purposes in Eclipse, I send the string "sendJSAlertTest" to the socket's outputstream, then derive a Method instance using the java.lang.reflect API from the ServerSocket inputstream, and then finally invoke the resulting method in the applet.  When the applet is running in a browser I write "sendJSAlert" to the socket instead since it leads to the actual invocation of javascript.  The result in Eclipse using the appletviewer is that the desktop application context prints the output "awesome sendJSAlert" and the applet context prints the output from the sendJSAlertTest() method, "Hello Client, I'm a Server!".  The result of passing "sendJSAlert" to the applet running in the browser is that the desktop application prints null, suggesting that for some reason the inputstream of the ServerSocket is empty, and the browser itself does nothing when it should generate a javascript alert box with the text "Hello Client, I'm a Server!".  The browser I'm using is Google Chrome, and for the moment I am simply running everything on the local machine (e.g. no remote server involved yet)</p>
  
  <p>Below is the relevant Java code and HTML:</p>
  
  <p>SocketClient.java</p>
  
  <pre><code>import java.awt.event.*;
  import java.io.*;
  import java.net.*;
  
  public class SocketClient {
  
  
  Socket socket = null;
  PrintWriter out = null;
  BufferedReader in = null;
  private InetAddress myAddress;
  private String remoteFunction;
  
  public SocketClient(){
  
  }
  
  
  public void listenSocket(int portNum){
  //Create socket connection
  
   try{
     System.out.println("@Client Trying to create socket bound to port " + portNum);
     socket = new Socket(&lt;my hostname here as a string&gt;, portNum);
     System.out.println("the attached socket port is " + socket.getLocalPort());
     System.out.flush();
     out = new PrintWriter(socket.getOutputStream(), true);
     out.println("sendJSAlertTest");
     in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
     String line = in.readLine();
     System.out.println("@CLient side Text received from server: " + line);
   } catch (UnknownHostException e) {
     System.out.println("Unknown host: &lt;my hostname here as a string&gt;.eng");
     System.exit(1);
   } catch  (IOException e) {
     System.out.println("No I/O");
     e.printStackTrace();
     System.exit(1);
   }
  }
  
  public void setRemoteFunction(String funcName){
    remoteFunction = funcName;
  }
  public String getRemoteFunction(){
   return remoteFunction;
  }
  
  }
  </code></pre>
  
  <p>SocketServer.java</p>
  
  <pre><code>import java.io.*;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.net.*;
  
  class SocketServer {
  
  
  ServerSocket server = null;
  Socket client = null;
  BufferedReader in = null;
  PrintWriter out = null;
  String line;
  private NetComm hNet;
  private Method serverMethod;
  
  SocketServer(NetComm netmain){
     hNet = netmain;
  
  
  
  }
  
  
  
  public void listenSocket(int portNum){
  
  try{
    System.out.println("@server Trying to create socket bound to port " + portNum);
    server = new ServerSocket(portNum);
    System.out.println("the attached socket port is " + server.getLocalPort());
  } catch (IOException e) {
    System.out.println("Could not listen on port " + portNum);
    System.exit(-1);
  }
  
  try{
    client = server.accept();
    System.out.println("Connection accepted!");
  } catch (IOException e) {
    System.out.println("Accept failed: " + portNum);
    System.exit(-1);
  }
  
  try{
    in = new BufferedReader(new InputStreamReader(client.getInputStream()));
    out = new PrintWriter(client.getOutputStream(), true);
  } catch (IOException e) {
    System.out.println("Accept failed: " + portNum);
    System.exit(-1);
  }
  
  while(true){
    try{
       System.out.println("trying to read from inputstream...");
      line = in.readLine();
      System.out.println(line);
      //Now that we have a method name, invoke it
      try {
          serverMethod = hNet.getClass().getDeclaredMethod(line,
    String.class);
      } catch (SecurityException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      } catch (NoSuchMethodException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      }
  
      try {
          serverMethod.invoke(hNet, "Hello Client, I'm a Server!");
      } catch (IllegalArgumentException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      } catch (IllegalAccessException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      } catch (InvocationTargetException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      }
  
      //Send data back to client
      out.println("awesome " + line);
  
  
     } catch (IOException e) {
       System.out.println("Read failed");
       System.out.flush();
       System.exit(-1);
     }
  
    }
    }
  
    protected void finalize(){
    //Clean up
    try{
      in.close();
      out.close();
      server.close();
    } catch (IOException e) {
      System.out.println("Could not close.");
      System.exit(-1);
   }
  }
  
  public int getBoundLocalPort(){
    return server.getLocalPort();
  }
  
  }
  </code></pre>
  
  <p>NetComm.java</p>
  
  <pre><code>import cresco.ai.att.ccm.core.CCMMain;
  import cresco.ai.att.ccm.gui.DataPanel;
  
  import java.io.*;
  import java.lang.reflect.Method;
  import java.net.MalformedURLException;
  import java.net.ServerSocket;
  import java.net.Socket;
  import java.net.URL;
  import java.util.ArrayList;
  
  import javax.servlet.*;
  import javax.servlet.http.*;
  
  import java.applet.*;
  
  
  public class NetComm extends JApplet{//HttpServlet{
  private CCMMain hMain;
  private DataPanel dpLocal;
  private SocketServer sockserver;
  private Method serverMethod;
  
  String testStr;
  Integer testInt;  /*integer */
  Character testChar; /*character*/
  
  //Testing this...
  ServerSocket server = null;
  Socket client = null;
  BufferedReader in = null;
  PrintWriter out = null;
  String line;
  
  
  
  
  
  
  @Override
  public void init(){
      sockserver = new SocketServer(this);
  
  
      //For offline debug (should be disabled in a release to the webapp):
              //initSocketServer is commented out in the release version and
              //invoked in the Eclipse testbed version.  In the webapp,
              //initSocketServer is invoked from javascript (see below js sockPuppet())
      //////initSocketServer(0);
  
  
  
  
      String msg = "Hello from Java (using javascript alert)";
      try {
          getAppletContext().showDocument(new URL("javascript:doAlert(\"" +
     msg +"\")"));
      }
      catch (MalformedURLException me) { }
  
  
  }
  
  public void sendJSAlertTest(String message){
      System.out.println("sendJSAlert remotely invoked, with message: " +
      message);
  
  }
  
  public void sendJSAlert(String message){
  
      try {
          getAppletContext().showDocument(new URL("javascript:doAlert(\"" +
      message +"\")"));
      }
      catch (MalformedURLException me) { }
  
  }
  
  
  
  public void initSocketServer(int portNum){
      sockserver.listenSocket(portNum);
  }
  public void finalizeSocketServer(){
      sockserver.finalize();
  }
  
  public int socket2Me(int portNum){
  
      try {
          socks.add(new ServerSocket(portNum));
          return 0; //socket opened successfully
      } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          return -1; //socket failed to open
      }
  
  
  
  
  }
  
  public int getSocketServerPort(){
      return sockserver.getBoundLocalPort();
  }
  
  
  public void showRectTest(){
      try {
          getAppletContext().showDocument(new
                      URL("javascript:overlayRect()"));
      }
      catch (MalformedURLException me) { }
  }
  
  
  
  
  public void setGUI(DataPanel d){
      dpLocal = d;
  }
  
  
   }
  </code></pre>
  
  <p>MapViz.html</p>
  
  <pre><code>&lt;html&gt;
  &lt;head&gt;
  &lt;title&gt;Welcome to Geographic Midpoint Map Vizualization!&lt;/title&gt;
  &lt;meta name="viewport"
      content="width=device-width, initial-scale=1.0, user-scalable=no"&gt;
  &lt;meta charset="UTF-8"&gt;
  &lt;link href="https://google-developers.appspot.com/maps/documentation/javascript
  /examples/default.css"
      rel="stylesheet" type="text/css"&gt;
  
  ...google maps stuff omitted...
  
  &lt;script type="text/javascript"&gt;
  &lt;script type="text/javascript"&gt;
  function overlayRect(){
      var c=document.getElementById("myCanvas");
      var ctx=c.getContext("2d");
      ctx.fillStyle="#FF0000";
      ctx.fillRect(0,0,150,75);
  }
  function doAlert(s){
      alert(s);
  }
  function testJava(){
      document.ccmApplet.showRectTest();
  }
  function sockPuppet(){
      var i = parseInt(document.getElementById("args").value,10);
      alert("parsing the input args... got " + i);
      if(i == NaN || i == null){
          i = 0;
      }
      alert("passed NaN OR null block, i is " + i);
      //i = 6672; //because $%*&amp; you, that's why!
      document.ccmApplet.initSocketServer(i);
      //document.ccmApplet.listenSocket(i);
      alert("inittializing socket server...");
      //queryPort();
      alert("querying port...");
      document.ccmApplet.finalizeSocketServer();
      //document.ccmApplet.finalize();
      alert("finalizing socket server...");
  }
  function queryPort(){
      var d = document.getElementById("debug");
      var s1 = "Last port opened was: ";
      //var s2 = document.ccmApplet.getLastBoundPort();
      var s2 = document.ccmApplet.getSocketServerPort();
      var sFinal = s1.concat(s2);
      d.value = sFinal;
  }
  &lt;/script&gt;
   &lt;/head&gt;
   &lt;body&gt;
   &lt;applet width="500" height="50" name="ccmApplet" archive="CCM.jar"
   code="cresco.ai.att.ccm.io.NetComm" MAYSCRIPT&gt;&lt;/applet&gt;
   &lt;p&gt;&lt;/p&gt;
  &lt;canvas id="myCanvas" width="200" height="100"&gt;&lt;/canvas&gt;
   &lt;div id="map_canvas"&gt;&lt;/div&gt;
  &lt;input id="args" type="textentry" value="" /&gt;
  &lt;button height="50" width="50" onClick="sockPuppet()"&gt;Test Socket
      Creation&lt;/button&gt;
  &lt;input id="debug" type="debugthingy" value="debug area..." /&gt;
  &lt;button height="50" width="50" onClick="testJava()"&gt;Test Java Callback&lt;/button&gt;
    &lt;/body&gt;
   &lt;/html&gt;
  </code></pre>
  
  <p>In the webapp, I fill in the args input with a valid port number on the local machine and press the Test Socket Connection button which invokes the sockPuppet() javascript. This should create a ServerSocket and bind it to the specified port, which I then separately connect my desktop client app to via SocketClient.listenSocket.  The result from Eclipse in the desktop app context is "awesome sendJSAlertTest" and in the appletviewer context is the output "sendJSAlert remotely invoked, with message: Hello Client, I'm a Server!".  The webapp, invoking sendJSAlert(), should call the javascript alert function on the same message, creating a popup box with the message "sendJSAlert remotely invoked, with message: Hello Client, I'm a Server!" but instead nothing happens in the browser (nor the Chrome java or javascript debug consoles), and the desktop app output is null instead of "awesome sendJSAlert" as expected</p>
  
  <p>So the question: What might be the cause of the different results?  I know the browser's security sandbox could be an issue, but I've included a permissions file which should allow communication via sockets on any localhost port:</p>
  
  <pre><code>grant {
   permission java.net.SocketPermission
      "localhost:1024-", 
   "accept, connect, listen, resolve";
  };
  </code></pre>
  
  <p>It's certainly possible though that I have not applied the permissions properly (I used the sun policytool gui); what exactly needs to be done in the applet code (if anything) to apply the permissions? Could a security problem result in the lack of response I'm seeing?  I'd expect an exception to be reported in Chrome's java debug console, but there weren't any...</p>
  
  <p>any help would be much appreciated, thanks!</p>
  
  <p>-CCJ</p>
  
  <p>UPDATE:
  Okay, some new information: I ran the applet again in Chrome with the javascript console open (could have sworn I tried this before without effect, but evidently not) and received the following console output--</p>
  
  <pre><code>"Uncaught Error: java.security.AccessControlException: access denied 
  ("java.net.SocketPermission" "&lt;myipaddress&gt;:4218" "accept,resolve") MapVizApp.html:154
   sockPuppet MapVizApp.html:154 onclick MapVizApp.html:179 Uncaught Error: Error 
   calling method on NPObject. sockPuppet onclick " 
  </code></pre>
  
  <p>So the question now is why am I tripping this security exception?  The policy file with the permissions given above is in the same working directory as the html page and the jar file containing the applet, and I added the following to my system's JRE security policy file</p>
  
  <pre><code>//Grants my NetComm applet the ability to accept, connect, and listen on unpriv. ports
  grant codeBase "file:${user.home}\Desktop\dev\invention\ATT\MapViz\CCM.jar" {
    permission java.net.SocketPermission
      "localhost:1024-", 
    "accept, connect, listen, resolve";
  };
  </code></pre>
  
  <p>I haven't yet signed the applet, but it was my understanding that if the policy files are in order an applet doesn't need to be signed... if I'm wrong on that please let me know.  Anyway, does anyone have any suggestions as to why this security exception is being thrown despite the policy files having the above granted permissions?  Is there a naming convention for policy files in working directories that the JRE looks for?  My working directory policy file for now is just named ccmPolFile, but I'm not clear on how the JRE is supposed to locate it; is there something I need to add to the applet code to point the JRE at the intended working directory policy file?  Further, shouldn't the system policy file grant that I added be enough by itself to satisfy socket permissions for my applet inside CCM.jar? </p>
  
  <p>UPDATE 2:
  I signed the applet and added the line policy.url.3=file:${user.home}\Desktop\dev\invention\ATT\MapViz\ccmPolFile.policy to my java.security file in ${java.home}/lib/security (via <a href="http://docs.oracle.com/javase/tutorial/security/tour2/step4.html#Approach2" rel="nofollow">http://docs.oracle.com/javase/tutorial/security/tour2/step4.html#Approach2</a> this is apparently how the JRE locates policy files to load)... sadly, the result is exactly the same security exception.  The only thing left that I know of is </p>
  
  <pre><code>AccessController.doPrivileged(new PrivilegedAction() {
      public Object run() {
          // perform the security-sensitive operation here
          return null;
      }
  });
  </code></pre>
  
  <p>which should let me do almost anything since the applet is now signed.  I wanted to keep signing out of the equation, but policy files aren't working for some reason.  I'll be back shortly with how that works out</p>