Java Servlets #### Big picture Re-examine the http server project. Your program allows browers to remotely fetch files and also to launch some java code that ran code on the server machine and sent HTML back by accessing a special URL ({/java/...}). The java code satisfied the {ServerSideJava} interface. A _servlet_ is the exact same concept--a piece of Java code that runs on the server in response to a browser request. Servlets support the request/response client/server model and are able to service HTTP GET and POST requests. They are a replacement for CGI bin and php etc... Many dynamic pages on the net are generated by Java code on a server and spit back to the browser, which pretends everything is a static file. Another way to think of a GET request is as a method call over the internet (this is the basic idea behind @(http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm, REST)) where method parameters are URL parameters. The return value is simply the (usually XML) data spit back by the server. For example, here is how to access Yahoo's search engine via their REST interface to find information on the movie Serenity: << http://api.search.yahoo.com/WebSearchService/V1/webSearch?appid=YahooDemo&query=serenity+movie >> One could view this as an implementation of this method call: << WebSearchService.webSearch("YahooDemo", "serenity+movie"); >> See @(http://developer.yahoo.net/search/rest.html, Creating a REST Request for Yahoo! Search Web Services) for more information. Again, though, REST is just a way to think about access dynamic data from the web. A POST request is almost the opposite of a GET request: it is meant to send data to the server from HTML forms (i.e., pages with {
>> Servlet responding to form (note both {doGet} and {doPost} methods): %include(../code/servlets/SimpleResponse.java, "code") Use HttpUtils.getRequestURL(request) to reconstruct URL minus query (args). Other useful methods in: getRemoteHost() http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/ServletRequest.html ### Argument URL encoding Converts meaningful or special chars to ascii code. {' '->%20} etc.... Use: {java.netURLEncoder.encode(...)} Here is an example calling the doGet method using a space in arg. @(http://localhost:8080/servlet/SimpleResponse?name=Terence%20Parr) #### Jetty and Servlets @(http://jetty.mortbay.org/jetty, Jetty) is a simple embeddable or standalone web server for static pages or dynamic pages like JSP or Servlets. For our purposes, it's probably easier to have your application embed the web server directly. ### Simple file serving Here is a simple program that starts up a web server at port 8080 and has a document root specified by args[0]: << import org.mortbay.http.*; import org.mortbay.jetty.Server; import org.mortbay.jetty.servlet.*; import org.mortbay.log.*; public class MyServer { public static void main(String[] args) throws Exception { String DOC_ROOT = args[0]; Server server = new Server(); server.addListener(":8080"); server.setRequestLog(getServerLogging()); server.addWebApplication("/", DOC_ROOT); server.start(); } } >> You will need the following additions to your {CLASSPATH}: << /home/public/cs601/jetty/javax.servlet.jar /home/public/cs601/jetty/org.mortbay.jetty.jar /home/public/cs601/jetty/log4j-1.2.12.jar /home/public/cs601/jetty/jasper-compiler.jar /home/public/cs601/jetty/jasper-runtime.jar /home/public/cs601/jetty/xercesImpl.jar /home/public/cs601/jetty/commons-logging.jar >> which are the jars used by Jetty. Start your server like this: << java MyServer ~/foo >> which will start serving files underneath {~/foo}. URLs like _host_{:8080/t.html} will get file {~/foo/t.html} from the disk. At this point, Jetty will serve files, but you are not recording requests to your server. Add the following method: << private static RequestLog getServerLogging() throws Exception { NCSARequestLog a = new NCSARequestLog("./request.log"); a.setRetainDays(90); a.setAppend(true); a.setExtended(false); a.setLogTimeZone("GMT"); a.start(); return a; } >> And add this line before the server.start(): << server.setRequestLog(getServerLogging()); >> You still get a warning from Jetty, but ignore it: << log4j:WARN No appenders could be found for logger (org.mortbay.util.Container). log4j:WARN Please initialize the log4j system properly. >> ### Serving Servlets To get Jetty to handle servlets, update your server code to look like this: << Server server = new Server(); server.addListener(":8080"); // logging server.setRequestLog(getServerLogging()); // Servlets ServletHttpContext context = (ServletHttpContext) server.getContext("/"); context.addServlet("Invoker","/servlet/*", "org.mortbay.jetty.servlet.Invoker"); // HTTP server.addWebApplication("/", "./"); server.start(); >> Servlet {HelloServlet} would be visible at _host_{:8080/servlet/HelloServlet}. #### Thread safety The safety issue for servlets with instance variables can be illustrated with the following: << class PageServlet extends HttpServer { String id; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { id = request.getParameter("ID"); ... out.println("Your id is "+id); } ... } >> One thread can set the id then switch to another thread which resets id. When that second thread finishes the first thread would start up and proceed to print out the same value as the second thread. I.e., if url s?ID=1 and s?ID=2 result in simultaneous exec of this servlet, you risk seeing the same id value printed out. #### JSP JSP: autogenerated servlets from html + java page. <<