Threaded Self Loading Server Servlet

In this discussion, two new functions will be added to the server/servlet. first, the servlet will include in the HTML it generates an applet tag that, in turn, will enable the browser to load the client applet directly that connects to the servlet/server. Second, the server will be built using multiple threads so that it can handle multiple requests from multiple clients.

The discussion includes four main parts:

  1. Counter Example Servlet
  2. Multithreaded servlet that generates HTML
  3. Run the servlet
  4. Applet referenced by the generated HTML

 

Counter Example Servlet

In the example, below, code is added to generate the applet tag. It looks straight forward, but it does not work. See if you can see why it does not work.

import java.net.*;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class jbsServerTwo extends  HttpServlet {

//**************** single-threaded server, implemented as servlet,
//**************** returns applet tag for applet to connect to servlet
//**************** default runs on wwwj.cs.unc.edu:8901

  public void doGet (HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException
	{

    ServerSocket listenSocket;
    Socket connection;

    InputStream inStream;
    DataInputStream inDataStream;
    OutputStream outStream;
    DataOutputStream outDataStream;

    String message;

    ServletOutputStream out;

    String DEFAULT_HOST = "wwwj.cs.unc.edu";
    int DEFAULT_PORT = 8901;
    String host;
    int port = DEFAULT_PORT;

    try  {

	    resp.setContentType("text/html");
	    out = resp.getOutputStream();

	    out.println("<html>");
	    out.println("<head><title>jbsServerBasic</title></head>");
	    out.println("<body>");
	    out.println("<center>");
	    out.println("<h3>Servlet Server Two Started</h1>");
	    out.println("<h3>Applet Tag Returned</h1>");
	    out.println("</center>");
	    out.println("<applet code=Client_Applet codebase=http://jbspc.cs.unc.edu:8080/Courses/wwwp-s98/docs/lessons/java/java_servlets/programs/jbsClientBasic>");
	    out.println("</applet>");
	    out.println("</body>");
	    out.println("</html>");
	    out.close();

        listenSocket = new ServerSocket ( port );
        connection = listenSocket.accept ();

        outStream = connection.getOutputStream ();
        outDataStream = new DataOutputStream ( outStream );
        inStream = connection.getInputStream ();
        inDataStream = new DataInputStream ( inStream );

        try {
            message = inDataStream.readUTF ();
            outDataStream.writeUTF ( message );
            connection.close ();
        }  // end try for input
        catch ( EOFException except ) {
        }  // end catch
        catch ( IOException except ) {
        }  // end catch
    }  // end try
    catch ( IOException except)  {
    }  // end catch

  }  // end doGet

}  // end jbsServerTwo



Multithreaded Servlet that Generates Applet Tag

The reason the code, above, does not work is that although the output stream is closed, the Java Server retains its socket connection with the client until the doGet method returns. Since the server function is implemented within this method, the socket remains open. Thus, when the browser tries to load the classes for the client applet, it hangs.

The solution is to run the server in another method and let the doGet method return. This requires the second incremental change -- making the server multithreaded. The example below does this.

The first thread returns the generated HTML and starts the server listen thread, if it is not already running. The second thread listens for a client connection and, when it gets one, starts a thread to handle that connection. The third thread provides services to a single client connection. there may be multiple instances of this thread class for multiple connections

 

Example Servlet

import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class jbsServerThreaded extends HttpServlet {


//*****  Mulit-threaded server, accepts multiple messages
//       default runs on jbs.cs.unc.edu:8902

public boolean firsttime = true;
String host;
int port;
protected String DEFAULT_HOST = "jbspc.cs.unc.edu";
protected int DEFAULT_PORT = 8902;
jbsListenServer listen;

    public void doGet (HttpServletRequest req, HttpServletResponse resp)
	throws ServletException, IOException
	{

        if ( firsttime )  {
            firsttime = false;
            setHostPort ( req );
            returnHTML ( resp );
            listen = new jbsListenServer ( port );
            listen.start ();
        }  // end firsttime
        else  {
            returnHTML ( resp );
        }  // end else

    }  // end doGet


    public void setHostPort ( HttpServletRequest res )  {

        try {
            InetAddress here = InetAddress.getLocalHost ();
            host = here.getHostName ();
        }  // end try
        catch (UnknownHostException e)
        { ;}

        port = DEFAULT_PORT;
        String qs = res.getQueryString ();
        if ( qs == null ) return;
        Hashtable ht = HttpUtils.parseQueryString ( qs );
        String portString[] = (String[])ht.get( "port" );
        if ( portString[0] != null ) {
            port = Integer.parseInt( portString[0] );
        }

    }  // end setHostPort

    public void returnHTML ( HttpServletResponse resp )  {

    ServletOutputStream out;

        try  {

	        resp.setContentType("text/html");
	        out = resp.getOutputStream();

	        out.println("<html>");
	        out.println("<head><title>jbsServerThreaded</title></head>");
	        out.println("<body>");
	        out.println("<center><font color=AA0000>");
	        out.println("<h3>jbsServerThreaded Running on Host " + host + ", port " + Integer.toString (port) + "</h3>");
	        out.println("<h3>Applet Tag for jbsClientApplet Returned</h3>");
	        out.println("</font></center>");
	        out.println("<applet code=jbsClientApplet.class codebase=http://" + host + ":8080/Courses/wwwp-s98/applets/jbsClientApplet width=50 height=50>");
	        out.println("<param name=host value=" + host +">");
	        out.println("<param name=port value=" + Integer.toString(port) +">");
	        out.println("</applet>");
	        out.println("</body>");
	        out.println("</html>");
	        out.flush();
	        out.close();
	    }  // end try
        catch ( IOException except)  {
        }  // end catch

    }  // end returnHTML


}  // end ServerThreaded

//************************************  ListenServer

class jbsListenServer extends Thread  {

jbsServerThreaded source;
ServerSocket listenSocket;
int port;
Socket connection;
jbsHandleServer handle;
boolean again = true;


// **************  jbsListenServer

    jbsListenServer ( int p)  {
        super ();
        port = p;
    }  // end constructor


// **************  run

    public void run  ()  {

        try  {
            listenSocket = new ServerSocket ( port );
            while ( true )  {
                Socket connection = listenSocket.accept();
                jbsHandleServer handleServer = new jbsHandleServer ( connection );
                handleServer.start ();
            }  // end while

    }  catch ( IOException e )  {
    }  // end catch


}  // end run


}  // end jbsListenServer


//************************************  HandleServer
class jbsHandleServer extends Thread  {

Socket connection;

InputStream inStream;
DataInputStream inDataStream;
OutputStream outStream;
DataOutputStream outDataStream;

String message;


// **************  jbsHandleServer

    jbsHandleServer ( Socket socket )  {
        super ();
        connection =  socket;
    }  // end constructor


// **************  run

    public void run  ()  {

        String stringIn, stringOut;

        try  {

            outStream = connection.getOutputStream ();
            outDataStream = new DataOutputStream ( outStream );
            inStream = connection.getInputStream ();
            inDataStream = new DataInputStream ( inStream );

            while ( true )  {
                message = inDataStream.readUTF ();
                outDataStream.writeUTF ( message );
            }  // end while

        }  // end try

        catch ( EOFException except ) {
            try  {
                connection.close ();
                return;
            }
            catch ( IOException e )  {
                return;
            }  // end IOException

         }  // end catch EOFException
         catch ( IOException e )  {
            return;
         }  // end catch IOException


    }  // end run


}  // end jbsHandleServer

Run the Servlet (wwwj: 8902)

 

Run the Servlet (jbspc: 8902)

 


 

Applet

The applet that is referenced in the HTML generated by the servlet is shown below. Notice how it obtains the host address and port number generated by the servlet. This enables the servlet to insure that the applet obtained by the client will be able to connect back to it.

import java.applet.*;
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
import java.util.*;

public class jbsClientApplet extends Applet implements ActionListener, WindowListener {

//*****  Mulit-threaded client, sends and receives multiple messages
//       expected to connect to server on jbs.cs.unc.edu:8902


protected String DEFAULT_HOST = "jbspc.cs.unc.edu";
protected int DEFAULT_PORT = 8902;
String host;
int port;

jbsAppletConnectServer connection;

Frame outerBox;
TextField hostDisplay, portDisplay;
TextArea logDisplay, sendDisplay, recvDisplay;
Panel topPanel;
Panel middlePanel;
Panel buttonPanel;
Button connectButton, sendButton, cancelButton, quitButton;


// ************** init

    public void init ()  {

        buildUI ();

    }  // end init



//***********  Interface Methods   ***********


//****  ActionListener methods

  public void actionPerformed ( ActionEvent e )  {

    Object s = e.getSource();

    // *** process Button actions

    if ( s instanceof Button )  {

        if ( s == connectButton )  {
            connection = new jbsAppletConnectServer ( this );
            connection.start ();
        }  // end connectButton

        if ( s == sendButton )  {
            connection.sendReceive ();
        }  // end sendButton

        if ( s == cancelButton )  {
            sendDisplay.setText ( "" );
        }  // end cancelButton

        if ( s == quitButton )  {
            logDisplay.appendText ( "Closing connection and quitting\n" );
            connection.closeConnection ();
            try {Thread.sleep ( 2000 );} catch ( InterruptedException except) {;}

            outerBox.hide ();
            outerBox.dispose ();
            System.exit ( 0 );

            }  // end quitButton

    }  // end process Button actions

  }  // end actionPerformed


//****  WindowListener methods

  public void windowActivated ( WindowEvent e )  {
  }

  public void windowDeactivated ( WindowEvent e )  {
  }

  public void windowOpened ( WindowEvent e )  {
  }

   public void windowClosed ( WindowEvent e )  {
  }

  public void windowClosing ( WindowEvent e )  {
    outerBox.hide ();
    outerBox.dispose ();
    System.exit(0);
  }

  public void windowIconified ( WindowEvent e )  {
  }

  public void windowDeiconified ( WindowEvent e )  {
  }


//***********  Utility Methods   ***********

// **************  buildUI

  private void buildUI ()  {

    String hostString, portString;

    hostString = getParameter ( "host" );
    portString = getParameter ( "port" );

    if ( hostString == null ) {
        hostDisplay = new TextField ( DEFAULT_HOST  , 30 );
    }  else  {
        hostDisplay = new TextField ( hostString, 30 );
    }
    if ( portString == null ) {
        portDisplay = new TextField ( Integer.toString ( DEFAULT_PORT ), 30 );
    }  else  {
        portDisplay = new TextField ( portString, 4 );
    }

    topPanel = new Panel ();
    topPanel.setLayout ( new GridLayout ( 2, 1 ) );
    topPanel.add ( hostDisplay );
    topPanel.add ( portDisplay );

    logDisplay = new TextArea ( 40, 10 );

    sendDisplay = new TextArea ( 40, 5 );
    recvDisplay = new TextArea ( 40, 5 );
    sendDisplay.setText ("Default message.");
    recvDisplay.setText ("Messages received will be displayed here.");
    middlePanel = new Panel ();
    middlePanel.setLayout ( new GridLayout ( 3, 1 ) );
    middlePanel.add ( logDisplay );
    middlePanel.add ( sendDisplay );
    middlePanel.add ( recvDisplay );

    connectButton = new Button ( "Connect" );
    sendButton = new Button ( "Send" );
    cancelButton = new Button ( "Cancel" );
    quitButton = new Button ( "Quit" );
    connectButton.addActionListener ( this );
    sendButton.addActionListener ( this );
    cancelButton.addActionListener ( this );
    quitButton.addActionListener ( this );
    buttonPanel = new Panel ( );
    buttonPanel.add ( connectButton );
    buttonPanel.add ( sendButton );
    buttonPanel.add ( cancelButton );
    buttonPanel.add ( quitButton );

    outerBox = new Frame ("Client_Applet");
    outerBox.addWindowListener ( this );
    outerBox.add ( "North", topPanel );
    outerBox.add ( "Center", middlePanel );
    outerBox.add ( "South", buttonPanel );

    outerBox.resize ( 400, 450 );
    outerBox.show ();

    }  // end buildUI


}  // end jbsClientApplet


//************************************  jbsAppletConnectServer
class jbsAppletConnectServer extends Thread  {

jbsClientApplet source;

String host;
int port;
Socket connection;

InputStream inStream;
DataInputStream inDataStream;
OutputStream outStream;
DataOutputStream outDataStream;

String message;

// **************  jbsAppletConnectServer

    jbsAppletConnectServer ( jbsClientApplet c)  {

        super ();

        source = (jbsClientApplet ) c;

        connectServer ();

    }  // end constructor


// **************  run

    public void run  ()  {

    }  // end run


// **************  connectServer

  public void connectServer (  )  {

    host = source.hostDisplay.getText ();
    if ( host.equals ("" ) ) host = source.DEFAULT_HOST;
    if ( ! ( source.portDisplay.getText () ).equals ( "" ) ) port = Integer.parseInt ( source.portDisplay.getText () );
    else port = source.DEFAULT_PORT;
    source.logDisplay.setText ( "Server specified: "+host+":"+port+"\n\n" );


    try  {
      connection = new Socket ( host, port );

      outStream = connection.getOutputStream ();
      outDataStream = new DataOutputStream ( outStream );
      inStream = connection.getInputStream ();
      inDataStream = new DataInputStream ( inStream );

      source.logDisplay.appendText ( "Socket created: \n  connecting to server "+host+":"+port+"\n\n" );

    }  // end try

    catch ( IOException except)  {
      source.logDisplay.setText ( "Error connecting to server\n" );
      except.printStackTrace ();
      System.exit ( 1 );
    }  // end catch

  }  // end connectServer



  public void sendReceive ()  {

    try {

        message = source.sendDisplay.getText ();
        outDataStream.writeUTF ( message );
        source.logDisplay.appendText ( "Message, below, sent to Server\n" );

        source.recvDisplay.setText ( "" );
        source.recvDisplay.setForeground ( Color.red );

        message = inDataStream.readUTF ();
        source.recvDisplay.appendText ( message );
        source.logDisplay.appendText ( "  Message returned from server\n\n" );

    }  // end try for input

    catch ( EOFException except ) {
        source.logDisplay.appendText ( "EOF received\n" );
        closeConnection ();
    }  // end catch IOException
    catch ( IOException e )  {
        source.logDisplay.appendText ( "IOException\n" );
        e.printStackTrace ();
        return;
    }  // end catch IOException

  }  // end sendReceive

// **************

  public void closeConnection () {

    try {
      connection.close ();
     }  catch ( IOException except )  {
        source.logDisplay.appendText ( "  Error clossing conncetion\n" );
       except.printStackTrace ();
     }

  }  // end closeConnection


}  // end jbsAppletConnectServer