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:
- Counter Example Servlet
- Multithreaded servlet that generates HTML
- Run the servlet
- 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 //************************************ HandleServerclass 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 jbsHandleServerRun 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 //************************************ jbsAppletConnectServerclass 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