Computer Science 15-100, Fall 2008
Notes:  Bonus Lecture on Writing Servlets


See the general notes on bonus lectures.


Note:  In this bonus lecture, in order to place our servlets online, we created free accounts from "eatj".  An eatj account is a real account from a real vendor on the real web.  Meaning:  while you can and should experiment with it, be prudent with it.  Using that account to spam the world or for denial-of-service attacks, for instance, would qualify as (very) imprudent.  But testing your own distributed, multiplayer Tetris game (whatever that would look like…) is a GREAT way to use the site!


Steps to create and use an "eatj" account:

1) Create eatj account

2) eatj Getting Started
http://s41.eatj.com/index.jsp?url=getting_started.html

3) Modify HelloServlet
a) edit WEB-INF\src\util\HelloServlet.java
b) download Apache Tomcat (http://tomcat.apache.org/download-55.cgi)
c) copy apache-tomcat-5.5.27/common/lib/servlet-api.jar to WEB-INF\src\util
d) add that jar to your classpath and/or your IDE's libraries
e) slightly modify and compile HelloServlet.java
f) be sure your class file lives in WEB-INF\classes\util\HelloServlet.class
g) rebuild and reupload ROOT.war and restart Tomcat

4) Create MyTestServlet
a) Copy WEB-INF\src\util\HelloServlet.java to MyTestServlet.java
b) slightly modify and compile MyTestServlet.java
c) be sure your class file lives in WEB-INF\classes\util\MyTestServlet.class
d) edit WEB-INF\web.xml
1) Add servlet and servlet-mapping entries for MYTestServlet
that are copied-and-edited from HelloServlet entries
e) rebuild and reupload ROOT.war and restart Tomcat
f) Run your servlet as such: (replace "dkosbie.s155" as appropriate)
http://dkosbie.s155.eatj.com/servlet/util.MyTestServlet

5) Add parameters to MyTestServlet
a) Add this code to MyTestServlet.java's processRequest:
out.println("<hr>");
out.println("Here are the parameters:");
out.println("<br>");
java.util.Enumeration parameters = request.getParameterNames();
while (parameters.hasMoreElements()) {
  String parameter = (String) parameters.nextElement();
  String value = request.getParameter(parameter);
  out.println(parameter + " : " + value);
  out.println("<br>");
}
out.println("<hr>");

b) rebuild and reupload ROOT.war and restart Tomcat
c) Run your servlet as such: (replace "dkosbie.s155" as appropriate)
http://dkosbie.s155.eatj.com/servlet/util.MyTestServlet?foo=1&bar=wahoo


Here is the code we wrote together.  It has the usual disclaimers for code written by a group during a bonus lecture, with design-by-committee flaws and surely bugs here and there, and with a near contempt for good style.  But here it is, and it works more or less.

// MyTestServlet.java

package util;

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

public class MyTestServlet extends HttpServlet { 
  protected void processRequest(HttpServletRequest request, HttpServletResponse response)
                 throws ServletException, IOException {
      response.setContentType("text/html");
      String msg = "err: unknown error";
      try {
        String cmd = request.getParameter("cmd");
        String userId = request.getParameter("userId");
        msg = (new MySimpleGameServer(cmd, userId)).getResponse();
      }
      catch (Exception e) {
        msg = "err: " + e.getMessage();
      }
      PrintWriter out = response.getWriter();
      out.println(msg);
      out.close();
  }
    
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException { processRequest(request, response); }

  protected void doPost(HttpServletRequest request, HttpServletResponse response)
  throws ServletException, IOException { processRequest(request, response); }

  public String getServletInfo() { return "MyTestServlet"; }    
  
  public static void main(String[] args) {
    MyGameTester.playOnline();
  }
}

class MyGameTester {
  public static void playStandalone() {
    Scanner scanner = new Scanner(System.in);
    while (true) {
      System.out.print("Enter userId: ");
      String userId = scanner.nextLine();
      System.out.print("Enter cmd: ");
      String cmd = scanner.nextLine();
      String msg;
      msg = (new MySimpleGameServer(cmd, userId)).getResponse();
      System.out.println(msg);
    }
  }
  
  public static String login() {
    return getResponseFromServer("login","");
  }

  public static void playOnline() {
    Scanner scanner = new Scanner(System.in);
    String userId = login();
    System.out.println("userId = " + userId);
    while (true) {
      System.out.print("Enter cmd (or 'quit'): ");
      String cmd = scanner.nextLine();
      if (cmd.equals("quit")) break;
      String msg = getResponseFromServer(cmd, userId);
      System.out.println(msg);
    }
    String cmd = "logout";
    String msg = getResponseFromServer(cmd, userId);
    System.out.println(msg);
  }
  
  public static String getResponseFromServer(String cmd, String userId) {
    String url = "http://dkosbie.s155.eatj.com/servlet/util.MyTestServlet";
    url += "?cmd=" + cmd + "&userId=" + userId;
    Scanner scanner = getUrlTextScanner(url);
    return scanner.nextLine();
  }

  // Convenient helper method for reading from a web page (url) as plain text.
  // Returns null if the page cannot be opened (or for any other error).
  // On some pages, especially if they contain XML, the spaces may be elided
  // (sothetextislikethis) -- in that case, try setting the second parameter
  // to " " or "\n", so spaces or newlines are added after each parsed element.
  // You are responsible for using this method, but not
  // for writing it (neither on homeworks or tests)!

  public static Scanner getUrlTextScanner(String url) { return getUrlTextScanner(url, null); }

  public static Scanner getUrlTextScanner(String url, final String dataDelimiter) {
    Scanner scanner = null;
    try {
      final StringBuffer sb = new StringBuffer();
      java.io.InputStreamReader reader = new java.io.InputStreamReader(
                                      new java.net.URL(url).openStream());
      javax.swing.text.html.HTMLEditorKit.ParserCallback parser =
        new javax.swing.text.html.HTMLEditorKit.ParserCallback() {
          public void handleText(char[] data, int pos) {
            if (data != null) {
              sb.append(data);
              if (dataDelimiter != null) sb.append(dataDelimiter);
            }
          }
          public void handleSimpleTag(javax.swing.text.html.HTML.Tag tag,
                            javax.swing.text.MutableAttributeSet a,
                            int pos) {
           if (tag.breaksFlow()) sb.append("\n");
          }
        };
      new javax.swing.text.html.parser.ParserDelegator().parse(reader, parser, true);
      scanner = new Scanner(sb.toString());
    }
    catch (Exception e) {
      System.out.println("Error opening text reader for url: " + url);
      return null;
    }
    return scanner;
  }
}

class MyGameServer {
  public String doCommand(String cmd, String userId) { return "ok"; }
  public void deserializeData(ArrayList<String> data) { }
  public void serializeData(ArrayList<String> data) { }
  public void reset() { }
  
  protected void deserializeSystemData(ArrayList<String> data) {
    if (data == null) {
    }
    else {
      // get the startIndex from the end
      int index = Integer.parseInt(data.get(data.size()-1));
      // now get the userIds
      int userIdCount = Integer.parseInt(data.get(index++));
      for (int i=0; i<userIdCount; i++)
        userIds.add(data.get(index++));
    }
  }
  
  protected ArrayList<String> userIds = new ArrayList<String>();
  
  protected void serializeSystemData(ArrayList<String> data) {
    int startIndex = data.size();
    // serialize the user id's
    data.add("" + userIds.size());
    for (String userId : userIds)
      data.add(userId);
    // store the startIndex at the end
    data.add("" + startIndex);
  }

  protected String response;
  
  public MyGameServer(String cmd, String userId) {
    response = "err: Unknown error";
    try {
      // load the data from the file
      String stateFileName = getClass().getName() + ".txt";
      Scanner scanner = getFileScanner(stateFileName);
      ArrayList<String> serializedData = new ArrayList<String>();
      if (scanner == null)
        serializedData = null;
      else {
        while (scanner.hasNext())
          serializedData.add(scanner.nextLine());
      }
      deserializeData(serializedData);
      deserializeSystemData(serializedData);
      // do the command
      if (cmd == null)
        throw new RuntimeException("No cmd specified");
      if ((!cmd.equals("login")) && (userId == null))
        throw new RuntimeException("No userId specified");
      if (cmd.equals("login"))
        response = doLogin();
      else if (!userIds.contains(userId))
        throw new RuntimeException("No such userId: " + userId);
      else if (cmd.equals("logout"))
        response = doLogout(userId);
      else if (cmd.equals("reset")) {
        reset();
        response = "ok";
      }
      else
        response = doCommand(cmd, userId);
      // store the data back to the file
      serializedData = new ArrayList<String>();
      serializeData(serializedData);
      serializeSystemData(serializedData);
      PrintStream out = getFilePrintStream(stateFileName);
      for (String line : serializedData)
        out.println(line);
      out.close();    
    }
    catch (Exception e) {
      response = "err: " + e.getMessage();
    }
  }
  
  public String getResponse() { return response; }

  protected static final Random random = new Random();

  public String doLogin() {
    Integer id = 0;
    do
      id = 1000 + random.nextInt(9000);
    while (userIds.contains(id));
    userIds.add("" + id);
    return "" + id;
  }
  
  public String doLogout(String userId) {
    userIds.remove(userId);
    return "ok";
  }
  
  // Convenient helper method for reading from a file.
  // Returns null if the file is not found (or for any other error).
  // You are responsible for using this method, but not
  // for writing it (neither on homeworks or tests)!
  public static Scanner getFileScanner(String filename) {
    Scanner scanner = null;
    try { scanner = new Scanner(new java.io.File(filename)); }
    catch (Exception e) {
      // System.out.println("File not found");
      return null;
    }
    return scanner;
  }

  // Convenient helper method for writing to a file.
  // Returns null if the file cannot be opened (or for any other error).
  // You are responsible for using this method, but not
  // for writing it (neither on homeworks or tests)!
  public static PrintStream getFilePrintStream(String filename) {
    PrintStream out = null;
    try { out = new PrintStream(new java.io.File(filename)); }
    catch (Exception e) {
      System.out.println("Error opening file " + filename);
      return null;
    }
    return out;
  }
}

// plays "Get20"
class MySimpleGameServer extends MyGameServer {

  public MySimpleGameServer(String cmd, String userId) { super(cmd, userId); }
  
  private int counter;
  private int winValue;

  public void reset() {
    counter = 0;
    winValue = 15+random.nextInt(5);
  }

  public void deserializeData(ArrayList<String> data) {
    if (data == null)
      reset();
    else {
      counter = Integer.parseInt(data.get(0));
      winValue = Integer.parseInt(data.get(1));
    }
  }
  
  public void serializeData(ArrayList<String> data) {
    data.add("" + counter);
    data.add("" + winValue);
  }

  public String doCommand(String cmd, String userId) {
    if (cmd.equals("add")) {
      counter++;
      if (counter < winValue)
        return "ok (counter = "  + counter + ")";
      else if (counter == winValue)
        return "YOU WIN!!!";
      else
        return "YOU LOSE (counter = "  + counter + ")";
    }
    else throw new RuntimeException("I can only add, not this: " + cmd);
  }
}

carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem   -   carpe diem