Link to home
Start Free TrialLog in
Avatar of edmargayao
edmargayao

asked on

Applet throws ThreadDeath when using a Socket connection

I have an applet below that a browser (Using Sun JDK1.4.1 plugin) uses to open a socket connection to a server.  It acts as a thread to ensure that it will connect to the server only once.  It behaves by waiting for the server to send it a message.  Once it receives the message, it displays the message on the browser using javascript by invoking a method of JSObject.  The Applet is used across multiple pages (e.g. Login and Home using the sequence: Login->Home).  

The Applet Code:

public class webClientAppletThread extends Applet implements Runnable {

  private static Socket socket;
  private static String serverIP = null;
  private static Thread connector = null;
  private static String location = null;

  /** String representing the default action to be taken upon signalling */
  private static String refreshAction = "JavaScript: document.location.reload()";

  /** server socket has to be static because the listener is a global thread */
  private static int port = 9090;

  /** A generic message for warning the browser user that it cannot accept messages */
  public static final String NOT_LISTENING_MSG =
    "JavaScript: alert('WARNING: A browser has already been opened for webClientFiles."
    + "\\nThis browser will not be able to receive update messages correctly."
    + "\\n\\nPlease close this browser.'); window.close()";

  /**
   * A generic message for warning the browser that
   * it will no longer receive messages
   */
  public static final String CONNECTION_LOST =
    "JavaScript: alert('WARNING: The connection to the server has been lost."
    + "\\n\\n\\nPlease close this browser.'); window.close()";

  private static Applet globalApplet;

  private static final String PORT_PARAM = "Port";
  private static final String REFRESH_PARAM = "Refresh";
  private static final String SERVER_IP = "ServerIP";

    public void init() {

    System.out.println("init()");
    System.out.println("Refresh: " + getParameter(REFRESH_PARAM));
    System.out.println("Port: " + getParameter(PORT_PARAM));
    System.out.println("ServerIP: " + getParameter(SERVER_IP));

    refreshAction = getParameter(REFRESH_PARAM);
    if ((refreshAction == null) || (refreshAction.equals(""))) {
      refreshAction = "JavaScript: document.location.reload()";
    }

    try {
      port = Integer.parseInt(getParameter(PORT_PARAM));
    } catch (NullPointerException e) {
    } catch (NumberFormatException e) {
    }

    System.out.println("default refresh action = \"" + refreshAction + "\""
      + "; port=" + port);

    globalApplet = this;
    serverIP = getParameter(SERVER_IP);
    try{
    if (connector==null) {
                  System.out.println("creating new connector thread");

                  connector = new Thread(new webClientAppletThread());
                  connector.setDaemon(false);
                  this.port = port;
                  connector.start();
            } else {
                  System.out.println("using old connector thread");
            }
    }catch(SecurityException e){
      e.printStackTrace();
    }
    // Initialize global listener will attempt listen:
    // start up single global port listener
  }

  public void start() {
    System.out.println("start()...");
  }

  public void stop() {
    System.out.println("stop()...");
  }

  /**
   * The run() method does the actual ServerSocket waiting
   * and wakes up all threads that need notification when
   * when a message is received from the middleware server.
   *
   * The run() method does the actual Socket connection
   * to the server.
   */
  public void run() {
  // start up the client socket!!!
    System.out.println("Inside Run.. "+connector.getThreadGroup().getName());

    try {

    // give microsoft some slack!!!
      try {
        if (Class.forName("java.security.AccessController") != null) {
          //PolicyEngine.assertPermission(PermissionID.SYSTEM);
          java.security.AccessController.checkPermission(new
              SocketPermission(serverIP,"connect, accept"));
          //java.security.AccessController.checkPermission(new
              //java.lang.RuntimePermission("stopThread"));
          java.security.AccessController.checkPermission(new
              java.security.AllPermission());
          //System.out.println("GL: Asserted Permissions...");
        } else {
          System.out.println("GL: AccessController class not found.");
        }
      } catch (AccessControlException ace){
        System.out.println("GL: Access Control Exception: " + ace.getMessage());
      } catch (SecurityException se) {
        System.out.println("GL: Throwed Security Exception: " + se.getMessage());
      } catch (Throwable e){
        System.out.println("GL: Throwed After Permission: " + e.getMessage());
      }
      System.out.println("Before synch..");
      //////////////////////////////////////////////////////////
      // Listen on port for client connections if no thread
      // is currently listening...
      //
      // In a way, this makes sure that the eMediclApplet class
      // thread is a singleton...
     synchronized (webClientAppletThread.class) {
        if (socket != null) {
          // quit!  if clientSocket has already been initialized...
           return;
        }
        System.out.println("GL: Connect attempt on port: " + port);
        try{
          socket = new Socket(serverIP,port);
          socket.setSoTimeout(0);
          //socket.setKeepAlive(true);
          BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        bos.write(InetAddress.getLocalHost().getHostAddress().toString().getBytes());
        bos.flush();
        }catch(UnknownHostException uhex){
          System.out.println("EA: " + uhex.getMessage());
        }catch (SecurityException se) {
          System.out.println("GL: Sync Throwed Security Exception: " + se.getMessage());
        }catch(IOException ioe){
          System.out.println("GL: Sync Throwed IO Exception: " + ioe.getMessage());
        }
      }
      System.out.println("After synch..");
      //////////////////////////////////////////////////////////
      // loop for server connections
      Thread thisThread = Thread.currentThread();
      while (socket.isConnected() && connector == thisThread) {

      //////////////////////////////////////////////////////////
      // try to read data from the socket...
      String mes = null;
        try {
          System.out.println("Before getInputStream.."+socket.isBound()+":"+socket.isClosed()
              +":"+socket.isConnected()+":"+socket.isInputShutdown()+":"+socket.isOutputShutdown());
          ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());

          System.out.println("After getInputStream.."+Thread.activeCount());
          mes = (String)ois.readObject();
          System.out.println("After readUTF.."+ mes);
        } catch (IOException e) {
        // end of client stream ?
        // just ignore the so far received message ;)
          System.out.println("IOException: " + e.getMessage());
          mes = null;
        } catch (SecurityException se) {
          System.out.println("GL: While Throwed Security Exception: " + se.getMessage());
        }

      System.out.println("GL: TCP/IP message received: \"" + mes + "\"");
      String appletMessage = mes;

      // close client socket...

      //////////////////////////////////////////////////////////
      // Invoke javascript...
      jsInvoke((appletMessage.equals("")?refreshAction:appletMessage));
      }

    }catch (Throwable t1) {
      t1.printStackTrace();
      System.out.println("GL: Throwable" + t1+" : "+ connector.isAlive() + Thread.activeCount());
      System.out.println("GL: Throwable :" + socket.isConnected()+":" + this.isActive());
      System.out.println("Socket State"+socket.isBound()+":"+socket.isClosed()
          +":"+socket.isConnected()+":"+socket.isInputShutdown()+":"+socket.isOutputShutdown());

      // failure of any kind...
      jsInvoke(CONNECTION_LOST);

      // try to close socket...
      // notify all instances that our socket is no longer valid
      // by setting global server socket variable to null...
      // set server socket to null so that a browser refresh could recover....
      try { socket.close(); } catch (Throwable t2) {}
         socket = null;
      }

  }

  private static void jsInvoke(String message) {
    try {
      System.out.println(">>>>>>> Invoking: " + message);
      JSObject.getWindow(globalApplet).eval(message);
    } catch (Throwable t) {
      // ignore :(
    }
  }
}



The problem is that we are getting the following error when the applet receives a message on the succeeding page (e.g. Error occurs at Home in the sequence: Login > Home) :

java.lang.ThreadDeath
      at java.lang.Thread.stop(Thread.java:635)
      at java.lang.ThreadGroup.stopOrSuspend(ThreadGroup.java:633)
      at java.lang.ThreadGroup.stop(ThreadGroup.java:547)
      at sun.awt.AppContext.dispose(AppContext.java:382)
      at sun.applet.AppletClassLoader.release(AppletClassLoader.java:662)
      at sun.plugin.security.PluginClassLoader.release(PluginClassLoader.java:415)
      at sun.applet.AppletPanel.release(AppletPanel.java:163)
      at sun.applet.AppletPanel.sendEvent(AppletPanel.java:260)
      at sun.plugin.AppletViewer.onPrivateClose(AppletViewer.java:764)
      at sun.plugin.AppletViewer$1.run(AppletViewer.java:726)
      at java.lang.Thread.run(Thread.java:536)

The error above is thrown when the following line of code comes out of a block state "ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());"


I also tried separating the Socket code and Thread into a singleton class to ensure that the Thread and socket persists across different pages but it did not solve the error above.
ASKER CERTIFIED SOLUTION
Avatar of Mick Barry
Mick Barry
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ie. you cannot persist a thread across pages.
Probable reason is that people would abuse this and leave a background thread running after you have visited a page containing their unfriendly applet.
Avatar of edmargayao
edmargayao

ASKER

Whether we separated the thread from the applet and made that thread a singleton or used the code above, our thread never got killed.  We even made sure that the thread did not die by logging (look at throwing clause) it's state.
Not sure I understand what you're saying. The ThreadDeath exception is being thrown when your thread is stopped.
Whether it's a singleton or not has nothing to do with it, all threads in the applets ThreadGroup are stopped when the applet is stopped.
True.  ThreadDeath is thrown when your thread is stopped.  We even separated the ThreadGroup of the Applet and the Thread Singleton, and found out that the Thread Singleton would be alive.  

>Probable reason is that people would abuse
>this and leave a background thread running
>after you have visited a page containing their
>unfriendly applet.

We intentionally need a background thread running all the time when the web application is being used which is why we made sure the Thread Singleton is kept alive at all times.

Thing is, when the line:

ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());

comes out from the blocked state, the applet throws a ThreadDeath exception and not the Thread Singleton.  We are suspecting that the I/O stream in conjunction with the Applet's start() method has something to do with this, and not the Thread Singleton because our debugging already showed proof of the Thread Singleton's life through the isActive() method.

> and found out that the Thread Singleton would be alive.  

If your thread is still alive then what exactly is the problem?

> comes out from the blocked state, the applet throws a ThreadDeath exception

probably cause thats the first chance it gets to shut things down.

> If your thread is still alive then what exactly is the problem?

The applet dies by throwing a ThreadDeath exception for which we cannot trace any further, and we are sure this is not attributed to the Thread Singleton.
> and we are sure this is not attributed to the Thread Singleton.

If you remove the thread (or stop it when the applet is stopped) does the problem still occur?
> If you remove the thread (or stop it when the applet is stopped) does the problem still occur?

We can't stop the Thread Singleton because the thread listens to a socket, which is why we made the Thread a Singleton in the other solution i mentioned above.  However, we can try removing the thread, but that would mean that the Applet will just function as any normal applet... init start and stop... etc..
> We can't stop the Thread Singleton because the thread listens to a socket

Why does that stop you stopping the thread.

> However, we can try removing the thread, but that would mean that the
> Applet will just function as any normal applet... init start and stop... etc..

Doesn't that imply the thread is the cause of the problem.
I'm just trying to isolate the cause of the problem.

If you can post a simple applet that keeps a thread running when you leave the page then I can try it here.