internet technologies 6 - servlets i - unibzricci/it/slides/6-servlets-1.pdf · 2011. 4. 7. ·...
TRANSCRIPT
Internet Technologies 6 - Servlets I
F. Ricci
2010/2011
Content
Basic Servlets
Tomcat
Servlets lifecycle
Servlets and forms
Reading parameters
Filtering text from HTML-specific characters
Reading headers
Sending compressed content
Differentiating among browsers
Referer
Most of the slides were made available by www. coreservlets.com
Servlet Roles
Read the explicit data sent by the client
Read the implicit HTTP request data sent by the browser
Generate the results
Send the explicit data (i.e., the document) to the client
Send the implicit HTTP response data
HelloWorld
import java.io.*; import javax.servlet.*; import javax.servlet.http.*;
/** Very simplistic servlet that generates plain text. * <P> * Taken from More Servlets and JavaServer Pages * from Prentice Hall and Sun Microsystems Press, * http://www.moreservlets.com/. * © 2002 Marty Hall; may be freely used or adapted. */
public class HelloWorld extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); out.println("Hello World"); } } code
Servlet Container
Servlet Architecture
Client Web
Java Virtual Machine (JVM)
Web Server
Servlet 1
HTTP Request
HTTP Response
Servlet 2
Servlet n
Installing and Running Tomcat
Tomcat is distributed as a ZIP archive http://tomcat.apache.org
unzip the download file, for instance into a root-level directory: C:\apache-tomcat-6.0.16
To run Tomcat you'll need to tell it where to find your J2SE SDK installation
Set the JAVA_HOME environment variable to C:\Program Files\Java\jdk1.6.0_04
To run Tomcat:
open a command window
change directory to Tomcat's bin directory
Type startup
Tomcat Directory Structure
Tomcat binaries: startup, shutdown
All jar libraries: e.g. servlet-api.jar
Configuration files: e.g. when you build your application in Netbeans a new file is added to indicate where is deployed (e.g., C:\apache-tomcat-6.0.16\conf\Catalina\localhost\coresjsp.xml)
Directories of the web applications deployed here, possibly generated by .war files deployed here (with a web.xml file)
Catalina Base
Go to netbeans: tools>servers to look at these details
web.xml
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application
2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <servlet> <servlet-name>bob</servlet-name> <servlet-class>HelloWorld</servlet-class> </servlet>
<servlet-mapping> <servlet-name>bob</servlet-name> <url-pattern>/HelloWorld</url-pattern> </servlet-mapping> </web-app>
Compiling and deploying
Create a directory in \webapps called hello
Create a directory in \webapps\hello called WEB-INF and then a subdirectory classes where to put the sources and the compiled files
Set the classpath
C:\>set CLASSPATH=\apache-tomcat-6.0.16\common\lib\servlet-api.jar
Compile
C:\>javac HelloWorld.java
Deploy the web.xml file in C:\apache-tomcat-6.0.16\webapps\hello\WEB-INF
Or use an IDE (Netbeans) – separating sources (web directory) and deployment (build directory).
Starting Tomcat
/bin/startup.bat or startup.sh
Point Browers to http://localhost:8080 should see default page
All the Docs are there on the default page!
Check out the examples pages, good tutorials
Basic Servlet Structure
Here's the outline of a basic servlet that handles GET and POST requests in the same way:
import java.io.*;
import javax.servlet.*; import javax.servlet.http.*;
public class SomeServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { PrintWriter out = response.getWriter(); // Use "out" to send content to browser } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); }
}
HelloWorld HTML
import java.io.*; import javax.servlet.*; import javax.servlet.http.*;
public class HelloServlet2 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); String docType = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " + "Transitional//EN\">\n"; out.println(docType + "<HTML>\n" + "<HEAD><TITLE>Hello (2)</TITLE></HEAD>\n" + "<BODY BGCOLOR=\"#FDF5E6\">\n" + "<H1>Hello</H1>\n" + "</BODY></HTML>"); } }
code
Some Simple HTML-Building Utilities
public class ServletUtilities { public static final String DOCTYPE = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " + "Transitional//EN\">";
public static String headWithTitle(String title) { return(DOCTYPE + "\n" + "<HTML>\n" + "<HEAD><TITLE>" + title + "</TITLE></HEAD>\n"); } ... }
Don’t go overboard Complete HTML generation packages
usually work poorly The JSP framework is a better solution
code
HelloServlet3: Packages and Utilities
package coreservlets;
import java.io.*; import javax.servlet.*; import javax.servlet.http.*;
public class HelloServlet3 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title = "Hello (3)"; out.println(ServletUtilities.headWithTitle(title)+ "<BODY BGCOLOR=\"#FDF5E6\">\n" + "<H1>" + title + "</H1>\n" + "</BODY></HTML>"); } }
code
The Servlet Life Cycle (Servlet Interface)
init
Executed once when the servlet is first loaded Not called for each request
service
Called in a new thread by server for each request
Dispatches to doGet, doPost, etc. Do not override this method!
doGet, doPost, doXxx
Handles GET, POST, etc. requests
Override these to provide desired behavior
destroy
Called when server deletes servlet instance Not called after each request.
javadoc
Example of usage of init
If a servlet get a request for a url with the http header
if-Modified-Since: Mon, 12 Nov 2010 18:00:00 GMT
It can check if something has really changed the output after that date – if not the servlet sends back a reply:
HTTP/1.1 304 Not Modified
And the browser shows the cashed url
The servlet must only implement the following method to know when it has been modified the last time:
public long getLastModified(HttpServletRequest request)
The app server will call it when a client requests the servlet
And will send back a result only if the page was modified (according to the value returned by the method) after Mon, 12 Nov 2010 18:00:00 GMT
Code
The init method set the time the page was modified
public void init() throws ServletException {
// Round to nearest second (i.e, 1000 milliseconds)
modTime = System.currentTimeMillis()/1000*1000;
for(int i=0; i<numbers.length; i++) {
numbers[i] = randomNum();
}
}
An then overwrite the method that tells when the page was modified
public long getLastModified(HttpServletRequest request) {
return(modTime); }
LotteryNumbers code
Calls to the servlet
The page was modified after 8:00:00 GMT
The page was not modified after 9:00:00 GMT
Calls to the servlet
You can do the same experiments with a telnet connection (e.g., using putty)
HTML Form
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML> <HEAD><TITLE>A Sample Form Using GET</TITLE></HEAD> <BODY> <H2 ALIGN="CENTER">A Sample Form Using GET</H2> <FORM ACTION="http://localhost:8080/coresjp/ServletForm"> <CENTER>
First name: <INPUT TYPE="TEXT" NAME="FirstName" VALUE=""><BR/> Last name: <INPUT TYPE="TEXT" NAME="LastName" VALUE=""><P> <INPUT TYPE="SUBMIT"> </CENTER> </FORM> </BODY> </HTML>
form
Reading form data in servlets
request.getParameter(“FirstName")
Returns URL-decoded value of first occurrence of FirstName parameter in query string
Works identically for GET and POST requests
Returns null if no such parameter is in query data
request.getParameterValues(“FirstName")
Returns an array of the URL-decoded values of all occurrences of FirstName parameter in query string
Returns a one-element array if param is not repeated
Returns null if no such parameter is in the query
request.getParameterNames() or request.getParameterMap()
Returns Enumeration or Map of request parameters
Usually reserved for debugging.
Reading parameters
import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import java.util.*;
public class ServletForm extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html"); PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">" + "<HTML>\n" + "<HEAD><TITLE>ServletForm</TITLE></HEAD>\n" + "<BODY BGCOLOR=\"#FDF5E6\">\n" + "<H1 ALIGN=\"CENTER\">ServletForm</H1>\n" + "<UL>\n" + "<LI><B>FirstName</B>: "+ request.getParameter("FirstName")+"\n"+ "<LI><B>LastName</B>: "+ request.getParameter("LastName")+"\n"+ "</UL>\n" + "</BODY></HTML>"); }
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
ServletForm code
Example: reading all parameters
getform postform
Reading All Parameters
public class ShowParameters extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String docType =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transitional//EN\">\n";
String title = "Reading All Request Parameters";
out.println(docType +
"<HTML>\n" +
"<HEAD><TITLE>"+title + "</TITLE></HEAD>\n"+
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=CENTER>" + title + "</H1>\n" +
"<TABLE BORDER=1 ALIGN=CENTER>\n" +
"<TR BGCOLOR=\"#FFAD00\">\n" +
"<TH>Parameter Name<TH>Parameter Value(s)");
code
Reading All Parameters
Enumeration paramNames = request.getParameterNames(); while(paramNames.hasMoreElements()) { String paramName = (String)paramNames.nextElement(); out.print("<TR><TD>" + paramName + "\n<TD>"); String[] paramValues = request.getParameterValues(paramName); if (paramValues.length == 1) { String paramValue = paramValues[0]; if (paramValue.length() == 0) out.println("<I>No Value</I>"); else out.println(paramValue); } else { out.println("<UL>"); for(int i=0; i<paramValues.length; i++) { out.println("<LI>" + paramValues[i]); } out.println("</UL>"); } } out.println("</TABLE>\n</BODY></HTML>"); }
Reading All Parameters
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
Filtering Strings for HTML-Specific Chars
You cannot safely insert arbitrary strings into servlet output
< and > can cause problems anywhere & and " can cause problems inside of HTML
attributes You sometimes cannot manually translate
The string is derived from a program excerpt or another source where it is already in some standard format
The string is derived from HTML form data Failing to filter special characters from form data
makes you vulnerable to cross-site scripting attack.
Filtering Strings for HTML-Specific Chars
public class ServletUtilities { public static String filter(String input) { if (!hasSpecialChars(input)) { return(input); } StringBuffer filtered = new StringBuffer(input.length()); char c; for(int i=0; i<input.length(); i++) { c = input.charAt(i); switch(c) { case '<': filtered.append("<"); break; case '>': filtered.append(">"); break; case '"': filtered.append("""); break; case '&': filtered.append("&"); break; default: filtered.append(c); } } return(filtered.toString()); } …
code
No Filtering
public class BadCodeServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { … out.println(docType + "<HTML>\n" + "<HEAD><TITLE>"+title+"</TITLE></HEAD>\n" + "<BODY BGCOLOR=\"#FDF5E6\">\n" + "<H1 ALIGN=\"CENTER\">" + title + "</H1>\n"+ "<PRE>\n" + getCode(request) + "</PRE>\n" + "Now, wasn't that an interesting sample\n" + "of code?\n" + "</BODY></HTML>"); }
protected String getCode(HttpServletRequest request) { return(request.getParameter("code")); } }
code
Spaces and line breaks are preserved
Special Chars: no filtering
form calling the badservlet
Servlet that does Filtering
public class GoodCodeServlet extends BadCodeServlet {
protected String getCode(HttpServletRequest request) {
return
(ServletUtilities.filter(super.getCode(request)));
//get the code as in the super class and then do the filter
}
}
code
Filtering
goodservlet
A Typical HTTP Request
GET /servlet/Search?keywords=servlets+jsp HTTP/1.1 Accept: image/gif, image/jpg, */* Accept-Encoding: gzip Connection: Keep-Alive Cookie: userID=id456578 Host: www.somebookstore.com Referer: http://www.somebookstore.com/findbooks.html User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)
You need to understand HTTP to be effective with servlets and JSP
Reading Request Headers
General methods of the HttpServletRequest
getHeader(String name) the first one with that name
getHeaders(String name) all occurrences of the header
getHeaderNames
Specialized methods of the HttpServletRequest
getCookies
getAuthType and getRemoteUser
getContentLength
getContentType
getDateHeader
getIntHeader
Related info
getMethod, getRequestURI , getQueryString, getProtocol
Checking For Missing Headers
HTTP 1.0 All request headers are optional
HTTP 1.1 Only Host is required
Conclusion Always check that request.getHeader is
non-null before trying to use it
String val = request.getHeader("Some-Name");
if (val != null) { … }
All Request Headers (Firefox)
Call servlet
Request Headers (Internet Explorer)
Making a Table of Request Headers
public class ShowRequestHeaders extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
out.println
(docType +
"<HTML>\n" +
"<HEAD><TITLE>"+title+"</TITLE></HEAD>\n"+
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + title + "</H1>\n" +
"<B>Request Method: </B>" +
request.getMethod() + "<BR>\n" +
"<B>Request URI: </B>" +
request.getRequestURI() + "<BR>\n" +
"<B>Request Protocol: </B>" +
request.getProtocol() + "<BR><BR>\n" +
code
Request Headers (Continued)
"<TABLE BORDER=1 ALIGN=\"CENTER\">\n" + "<TR BGCOLOR=\"#FFAD00\">\n" + "<TH>Header Name<TH>Header Value"); Enumeration headerNames = request.getHeaderNames(); while(headerNames.hasMoreElements()) { String headerName = (String)headerNames.nextElement(); out.println("<TR><TD>" + headerName); out.println(" <TD>"+request.getHeader(headerName)); } out.println("</TABLE>\n</BODY></HTML>"); }
/** Since this servlet is for debugging, have it * handle GET and POST identically. */
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
Common HTTP 1.1 Request Headers
Accept
Indicates MIME types browser can handle
Can send different content to different clients. For example, PNG files have good compression characteristics but are not widely supported in browsers. A servlet could check to see if PNG is supported, sending <IMG SRC="picture.png" ...> if it is supported, and <IMG SRC="picture.gif" ...> if not.
Accept-Encoding
Indicates encodings (e.g., gzip or compress) browser can handle.
See following example
Common HTTP 1.1 Request Headers
Authorization User identification for password-protected
pages Instead of HTTP authorization, use HTML forms
to send username/password and store info in session object - this approach is usually preferable because standard HTTP authorization results in a small, terse dialog box that is unfamiliar to many users
Servers have high-level way to set up password-protected pages without explicit programming in the servlets.
Common HTTP 1.1 Request Headers
Connection
In HTTP 1.0, keep-alive means browser can handle persistent connection - in HTTP 1.1, persistent connection is default
Persistent connections mean that the web server can reuse the same socket over again for requests very close together from the same client (e.g., the images associated with a page, or cells within a framed page).
Servlets can't do this unilaterally; the best they can do is to give the web server enough info to permit persistent connections - they should set Content-Length response header.
Common HTTP 1.1 Request Headers
Cookie Gives cookies previously sent to client Use getCookies, not getHeader (we shall do
that in a next lecture)
Host Indicates host given in original URL This is a required header in HTTP 1.1 This fact is important to know if you write a
custom HTTP client (e.g., WebClient we used before) or telnet to a server and use the HTTP/1.1 version.
Common HTTP 1.1 Request Headers
If-Modified-Since Indicates client wants page only if it has been changed
after specified date Don’t handle this situation directly; implement getLastModified instead
See lottery-number example we have already shown Referer
URL of referring Web page
Useful for tracking traffic; logged by many servers
Can also be used to let users set preferences and then return to the page they came from
Can be easily spoofed; don't let this header be sole means of deciding how much to pay sites that show your banner ads
Some browsers (Opera), ad filters (Web Washer), and personal firewalls (Norton) screen out this header.
Common HTTP 1.1 Request Headers
User-Agent Best used for identifying category of client
Web browser vs. I-mode cell phone, etc. For Web applications, use other parameters if
possible Again, can be easily spoofed We shall touch this later
Sending Compressed Web Pages
Dilbert used with permission of United Syndicates Inc.
Sending Compressed Pages: GzipUtilities.java
public class GzipUtilities { public static boolean isGzipSupported (HttpServletRequest request) { String encodings = request.getHeader("Accept-Encoding"); return((encodings != null) && (encodings.indexOf("gzip") != -1)); }
public static boolean isGzipDisabled (HttpServletRequest request) { String flag = request.getParameter("disableGzip"); return((flag != null)&& // if the parameter is not null (!flag.equalsIgnoreCase("false"))); // and not "false" }
public static PrintWriter getGzipWriter (HttpServletResponse response) throws IOException { return(new PrintWriter (new GZIPOutputStream (response.getOutputStream()))); } }
code
Sending Compressed Pages: LongServlet.java
public class LongServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
// Change the definition of "out" depending on
// whether or not gzip is supported.
PrintWriter out;
if (GzipUtilities.isGzipSupported(request) &&
!GzipUtilities.isGzipDisabled(request)) {
out = GzipUtilities.getGzipWriter(response);
response.setHeader("Content-Encoding", "gzip");
} else {
out = response.getWriter();
}
code
Sending Compressed Pages: LongServlet.java
…
out.println
(docType +
"<HTML>\n" +
"<HEAD><TITLE>" + title + "</TITLE></HEAD>\n" +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + title + "</H1>\n");
String line = "Blah, blah, blah, blah, blah. " +
"Yadda, yadda, yadda, yadda.";
for(int i=0; i<10000; i++) {
out.println(line);
}
out.println("</BODY></HTML>");
out.close();
}
}
Sending Compressed Pages: Results
Uncompressed (28.8K modem), Firefox, Netscape and Internet Explorer: > 50 seconds
Compressed (28.8K modem), Firefox, Netscape and Internet Explorer: < 5 seconds
Caution: be careful about generalizing benchmarks
call
call
Differentiating Among Different Browser Types
Use User-Agent only when necessary Otherwise, you will have difficult-to-maintain code that
consists of tables of browser versions and associated capabilities
Check for null The header is not required by the HTTP 1.1 specification,
some browsers let you disable it (e.g., Opera), and custom clients (e.g., Web spiders or link verifiers) might not use the header at all
To differentiate among Firefox, Netscape, and Internet Explorer, check for “MSIE,” not “Mozilla” Both Firefox and Internet Explorer say “Mozilla” at the
beginning of the header Note that the header can be faked
If a client fakes this header, the servlet cannot tell the difference.
Differentiating Among Different Browser Types
public class BrowserInsult extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); String title, message; // Assume for simplicity that Firefox and IE are // the only two browsers. String userAgent = request.getHeader("User-Agent"); if ((userAgent != null) && (userAgent.indexOf("MSIE") != -1)) { title = "Microsoft Minion"; message = "Welcome, O spineless slave to the " + "mighty empire."; } else { title = "Hopeless Firefox Rebel"; message = "Enjoy it while you can. " + "You <I>will</I> be assimilated!"; }
code
Differentiating Among Browser Types (Result)
call
Referer
The Referer header designates the location of the page users were on when they clicked on a link
No Referer is set if the user typed the address of the page
You can customize a page depending on how the user reached it Customize the layout of a page according to
the site that link to Change the content if the user come from the
same domain or another domain Supply a link to go back Track the effectiveness of a banner or a link.