Interview Questions

Example: The Thread Pool

Java Network Programming - Sockets for Servers


(Continued from previous question...)

Example: The Thread Pool

Each connection is handled by the run( ) method of the RequestProcessor class shown in Example. This method waits until it can get a Socket out of the pool. Once it does that, it gets input and output streams from the socket and chains them to a reader and a writer. The reader reads the first line of the client request to determine the version of HTTP that the client supports--we want to send a MIME header only if this is HTTP 1.0 or later--and what file is requested. Assuming the method is GET, the file that is requested is converted to a filename on the local filesystem. If the file requested was a directory (i.e., its name ended with a slash), we add the name of an index file. We use the canonical path to make sure that the requested file doesn't come from outside the document root directory. Otherwise, a sneaky client could walk all over the local filesystem by including .. in URLs to walk up the directory hierarchy. This is all we'll need from the client, though a more advanced web server, especially one that logged hits, would read the rest of the MIME header the client sends.

Next the requested file is opened and its contents are read into a byte array. If the HTTP version is 1.0 or later, we write the appropriate MIME headers on the output stream. To figure out the content type, we call the guessContentTypeFromName( ) method to map file extensions such as .html onto MIME types such as text/html. The byte array containing the file's contents is written onto the output stream, and the connection is closed. Exceptions may be thrown at various places if, for example, the file cannot be found or opened. If an exception occurs, we send an appropriate HTTP error message to the client instead of the file's contents

Example : The Thread Pool 
That Handles HTTP Requests

    import java.net.*;
    import java.io.*;
    import java.util.*;
        
public class RequestProcessor implements Runnable
{
        
private static List pool = new LinkedList(  );
 private File documentRootDirectory;
private String indexFileName = "index.html";
      
public RequestProcessor
(File documentRootDirectory,String indexFileName)
{
            
        if (documentRootDirectory.isFile(  )) 
        {
throw new IllegalArgumentException(
"documentRootDirectory must be a directory, 
not a file");   
        }
this.documentRootDirectory =
            documentRootDirectory;
        try {
this.documentRootDirectory 
= documentRootDirectory.getCanonicalFile(  );
        }
        catch (IOException e) {
        }
 if (indexFileName != null) 
 this.indexFileName = indexFileName;
      }
      
public static void processRequest(Socket request)
{
        
        synchronized (pool) {
          pool.add(pool.size(  ), request);
          pool.notifyAll(  );
        }
     
      }  
      
      public void run(  ) {
            
        // for security checks
String root = documentRootDirectory.getPath(  );
          
        while (true) {       
          Socket connection;
          synchronized (pool) {         
            while (pool.isEmpty(  )) {
              try {
                pool.wait(  );
              }
              catch (InterruptedException e) {
              }
            }
            connection = (Socket) pool.remove(0); 
          }
     
          try {            
            String filename;
            String contentType;   
OutputStream raw = new BufferedOutputStream(
                connection.getOutputStream(  )
                               );         
Writer out = new OutputStreamWriter(raw);
        Reader in = new InputStreamReader(
      new BufferedInputStream(
        connection.getInputStream(  )
                         ),"ASCII"
                        );
StringBuffer requestLine = new StringBuffer( );
            int c;
            while (true) {
              c = in.read(  );
              if (c == '\r' || c == '\n') break;
              requestLine.append((char) c);
            }
            
         String get = requestLine.toString(  );
            
            // log the request 
            System.out.println(get);
            
 StringTokenizer st = new StringTokenizer(get);
            String method = st.nextToken(  );
            String version = "";
            if (method.equals("GET")) {
              filename = st.nextToken(  );
if (filename.endsWith("/"))
    filename += indexFileName;
contentType=guessContentTypeFromName(filename);
              if (st.hasMoreTokens(  )) {
                version = st.nextToken(  );
              }
     
File theFile = new File(documentRootDirectory, 
filename.substring(1,filename.length(  )));
              if (theFile.canRead(  ) 
              
 // Don't let clients outside the document root
 
 && theFile.getCanonicalPath(  ).
 startsWith(root)) {
 
 DataInputStream fis = new DataInputStream(
                  new BufferedInputStream(
                 new FileInputStream(theFile)
                                       )
                                      );
byte[] theData =
        new byte[(int) theFile.length(  )];
                fis.readFully(theData);
                fis.close(  );
 if (version.startsWith("HTTP "))
  {  // send a MIME header
         out.write("HTTP 1.0 200 OK\r\n");
        Date now = new Date(  );
        out.write("Date: " + now + "\r\n");
      out.write("Server: JHTTP 1.0\r\n");
out.write("Content-length: " + 
  theData.length + "\r\n");
out.write("Content-type: " + contentType + "
   \r\n\r\n");
        out.flush(  );
                }  // end try
            
// send the file; it may be an image or
// other binary data so use the 
//underlying output stream 
// instead of the writer

                raw.write(theData);
                raw.flush(  );
              }  // end if
 else {  // can't find the file
if (version.startsWith("HTTP "))
 {  // send a MIME header
out.write("HTTP 1.0 404 File Not Found\r\n");
Date now = new Date(  );
out.write("Date: " + now + "\r\n");
out.write("Server: JHTTP 1.0\r\n");
 out.write("Content-type: text/html\r\n\r\n");
                } 
out.write("<HTML>\r\n");
out.write("<HEAD><TITLE>
File Not Found</TITLE>\r\n");
                out.write("</HEAD>\r\n");
                out.write("<BODY>");
                
out.write("<H1>HTTP Error 404: 
File Not Found</h2>\r\n");

out.write("</BODY></HTML>\r\n");
                out.flush(  );
              }
            }
else {  // method does not equal "GET"
if (version.startsWith("HTTP ")) 
{  // send a MIME header
out.write("HTTP 1.0 501 Not Implemented\r\n");
 Date now = new Date(  );
out.write("Date: " + now + "\r\n");
out.write("Server: JHTTP 1.0\r\n");
out.write("Content-type: text/html\r\n\r\n"); 
              }       
 out.write("<HTML>\r\n");
 out.write("<HEAD><TITLE>
Not Implemented</TITLE>\r\n");
  out.write("</HEAD>\r\n");
out.write("<BODY>");
 out.write("<H1>HTTP Error 501:
 Not Implemented</h2>\r\n");
out.write("</BODY></HTML>\r\n");
              out.flush(  );
            }
          }
          catch (IOException e) {
          }
          finally {
            try {
              connection.close(  );        
            }
            catch (IOException e) {} 
          }
          
        } // end while
     
      } // end run
     
public static String 
guessContentTypeFromName(String name) {
if (name.endsWith(".html") || name.endsWith(".htm"))
{
          return "text/html";
        }
 else if (name.endsWith(".txt") || 
name.endsWith(".java")) {
          return "text/plain";
        }
        else if (name.endsWith(".gif")) {
          return "image/gif";
        }
        else if (name.endsWith(".class")) {
          return "application/octet-stream";
        }
 else if (name.endsWith(".jpg") 
 || name.endsWith(".jpeg")) {
          return "image/jpeg";
        }
        else return "text/plain";
      }  
     
    } // end RequestProcessor

This server is functional but still rather austere. 
Here are a few features you might 
want to think about adding:

 * A server administration interface

* Support for CGI programs and/or the 
Java Servlet API

 * Support for other request methods, such as 
 POST, HEAD, and PUT

 * A log file in the common web log file format

 * Server-side includes and/or Java Server Pages

 * Support for multiple document roots, 
so that individual users can have their own sites

Other Interview Questions