CS396N - Web Programming |
Spring 2002 |
Chapter 19 - Server-Side Java Servlets |
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
/** Servlet that processes a request to generate n
* prime numbers, each with at least m digits.
* It performs the calculations in a low-priority
background
* thread, returning only the results it has found
so far.
* If these results are not complete, it sends a
Refresh
* header instructing the browser to ask for new
results a
* little while later. It also maintains a list of
a
* small number of previously calculated prime lists
* to return immediately to anyone who supplies the
* same n and m as a recent completed computation.
*/
public class PrimeNumbers extends HttpServlet {
private Vector primeListVector = new Vector();
private int maxPrimeLists = 30;
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
int numPrimes =
ServletUtilities.getIntParameter(request,
"numPrimes", 50);
int numDigits =
ServletUtilities.getIntParameter(request,
"numDigits", 120);
PrimeList primeList =
findPrimeList(primeListVector,
numPrimes, numDigits);
if (primeList == null) {
primeList = new PrimeList(numPrimes,
numDigits, true);
// Multiple servlet request threads
share the instance
// variables (fields) of PrimeNumbers.
So
// synchronize all access to
servlet fields.
synchronized(primeListVector)
{
if (primeListVector.size()
>= maxPrimeLists)
primeListVector.removeElementAt(0);
primeListVector.addElement(primeList);
}
}
Vector currentPrimes = primeList.getPrimes();
int numCurrentPrimes = currentPrimes.size();
int numPrimesRemaining = (numPrimes - numCurrentPrimes);
boolean isLastResult = (numPrimesRemaining
== 0);
if (!isLastResult)
{
response.setHeader("Refresh",
"5");
}
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Some " + numDigits + "-Digit
Prime Numbers";
out.println(ServletUtilities.headWithTitle(title)
+
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2 ALIGN=CENTER>" + title + "</H2>\n" +
"<H3>Primes found with " + numDigits +
" or more digits: " + numCurrentPrimes +
".</H3>");
if (isLastResult)
out.println("<B>Done searching.</B>");
else
out.println("<B>Still looking
for " + numPrimesRemaining +
" more<BLINK>...</BLINK></B>");
out.println("<OL>");
for(int i=0; i<numCurrentPrimes; i++)
{
out.println(" <LI>"
+ currentPrimes.elementAt(i));
}
out.println("</OL>");
out.println("</BODY></HTML>");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
doGet(request, response);
}
// See if there is an existing ongoing or completed
// calculation with the same number of primes and number
// of digits per prime. If so, return those results instead
// of starting a new background thread. Keep this list
// small so that the Web server doesn't use too much
memory.
// Synchronize access to the list since there may be
// multiple simultaneous requests.
private PrimeList findPrimeList(Vector primeListVector,
int numPrimes,
int numDigits) {
synchronized(primeListVector) {
for(int i=0; i<primeListVector.size();
i++) {
PrimeList primes
=
(PrimeList)primeListVector.elementAt(i);
if ((numPrimes ==
primes.numPrimes()) &&
(numDigits == primes.numDigits()))
return(primes);
}
return(null);
}
}
}
HTML Code
<!DOCTYPE HTML PUBLIC "-//W3C//DTD
HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Finding Large Prime Numbers</TITLE>
</HEAD>
<BODY BGCOLOR="#FDF5E6">
<H2 ALIGN="CENTER">Finding Large Prime
Numbers</H2>
<BR><BR>
<CENTER>
<FORM ACTION="/servlet/cwp.PrimeNumbers">
<B>Number of primes to calculate:</B>
<INPUT TYPE="TEXT" NAME="numPrimes"
VALUE=25 SIZE=4><BR>
<B>Number of digits:</B>
<INPUT TYPE="TEXT" NAME="numDigits"
VALUE=150 SIZE=3><BR>
<INPUT TYPE="SUBMIT" VALUE="Start
Calculating">
</FORM>
</CENTER>
</BODY>
</HTML>
Supporting Files
PrimeList.java
package cwp;
import java.util.*;
import java.math.BigInteger;
/** Creates a Vector of large prime numbers,
usually in
* a low-priority background
thread. Provides a few small
* thread-safe access methods.
* <P>
* Taken from Core Web Programming
Java 2 Edition
* from Prentice Hall and Sun
Microsystems Press,
* http://www.corewebprogramming.com/.
* May be freely used or adapted.
*/
public class PrimeList
implements Runnable {
private Vector primesFound;
private int numPrimes, numDigits;
/** Finds numPrimes prime numbers,
each of which are
* numDigits long or
longer. You can set it to only
* return when done,
or have it return immediately,
* and you can later
poll it to see how far it
* has gotten.
*/
public PrimeList(int numPrimes,
int numDigits,
boolean runInBackground) {
// Using Vector instead
of ArrayList
// to support JDK 1.1
servlet engines
primesFound = new Vector(numPrimes);
this.numPrimes = numPrimes;
this.numDigits = numDigits;
if (runInBackground)
{
Thread
t = new Thread(this);
// Use
low priority so you don't slow down server.
t.setPriority(Thread.MIN_PRIORITY);
t.start();
} else {
run();
}
}
public void run() {
BigInteger start =
Primes.random(numDigits);
for(int i=0; i<numPrimes;
i++) {
start =
Primes.nextPrime(start);
synchronized(this)
{
primesFound.addElement(start);
}
}
}
public synchronized boolean isDone()
{
return(primesFound.size()
== numPrimes);
}
public synchronized Vector getPrimes()
{
if (isDone())
return(primesFound);
else
return((Vector)primesFound.clone());
}
public int numDigits() {
return(numDigits);
}
public int numPrimes() {
return(numPrimes);
}
public synchronized int numCalculatedPrimes()
{
return(primesFound.size());
}
}
Primes.java
package cwp;
import java.math.BigInteger;
/** A few utilities to generate a large random BigInteger,
* and find the next prime number above a given BigInteger.
* <P>
* Taken from Core Web Programming Java 2 Edition
* from Prentice Hall and Sun Microsystems Press,
* http://www.corewebprogramming.com/.
* May be freely used or adapted.
*/
public class Primes {
// Note that BigInteger.ZERO was new in JDK 1.2, and
1.1
// code is being used to support the most servlet engines.
private static final BigInteger ZERO = new BigInteger("0");
private static final BigInteger ONE = new BigInteger("1");
private static final BigInteger TWO = new BigInteger("2");
// Likelihood of false prime is less than 1/2^ERR_VAL
// Assumedly BigInteger uses the Miller-Rabin test or
// equivalent, and thus is NOT fooled by Carmichael numbers.
// See section 33.8 of Cormen et al's Introduction to
// Algorithms for details.
private static final int ERR_VAL = 100;
public static BigInteger nextPrime(BigInteger start)
{
if (isEven(start))
start = start.add(ONE);
else
start = start.add(TWO);
if (start.isProbablePrime(ERR_VAL))
return(start);
else
return(nextPrime(start));
}
private static boolean isEven(BigInteger n) {
return(n.mod(TWO).equals(ZERO));
}
private static StringBuffer[] digits =
{ new StringBuffer("0"), new StringBuffer("1"),
new StringBuffer("2"), new StringBuffer("3"),
new StringBuffer("4"), new StringBuffer("5"),
new StringBuffer("6"), new StringBuffer("7"),
new StringBuffer("8"), new StringBuffer("9")
};
private static StringBuffer randomDigit() {
int index = (int)Math.floor(Math.random()
* 10);
return(digits[index]);
}
public static BigInteger random(int numDigits) {
StringBuffer s = new StringBuffer("");
for(int i=0; i<numDigits; i++) {
s.append(randomDigit());
}
return(new BigInteger(s.toString()));
}
/** Simple command-line program to test. Enter number
* of digits, and it picks a random number
of that
* length and then prints the first 50 prime
numbers
* above that.
*/
public static void main(String[] args) {
int numDigits;
if (args.length > 0)
numDigits = Integer.parseInt(args[0]);
else
numDigits = 150;
BigInteger start = random(numDigits);
for(int i=0; i<50; i++) {
start = nextPrime(start);
System.out.println("Prime " +
i + " = " + start);
}
}
}
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** Sets six cookies: three that apply
only to the current
* session (regardless of how
long that session lasts)
* and three that persist for
an hour (regardless of
* whether the browser is restarted).
* <P>
* Taken from Core Web Programming
Java 2 Edition
* from Prentice Hall and Sun
Microsystems Press,
* http://www.corewebprogramming.com/.
* May be freely used or adapted.
*/
public class SetCookies extends HttpServlet
{
public void doGet(HttpServletRequest
request,
HttpServletResponse response)
throws
ServletException, IOException {
for(int i=0; i<3;
i++) {
// Default
maxAge is -1, indicating cookie
// applies
only to current browsing session.
Cookie
cookie = new Cookie("Session-Cookie-" + i,
"Cookie-Value-S" + i);
response.addCookie(cookie);
cookie
= new Cookie("Persistent-Cookie-" + i,
"Cookie-Value-P" + i);
// Cookie
is valid for an hour, regardless of whether
// user
quits browser, reboots computer, or whatever.
cookie.setMaxAge(3600);
response.addCookie(cookie);
}
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Setting
Cookies";
out.println
(ServletUtilities.headWithTitle(title)
+
"<BODY
BGCOLOR=\"#FDF5E6\">\n" +
"<H1
ALIGN=\"CENTER\">" + title + "</H1>\n" +
"There
are six cookies associated with this page.\n" +
"To
see them, visit the\n" +
"<A
HREF=\"/servlet/cwp.ShowCookies\">\n" +
"<CODE>ShowCookies</CODE>
servlet</A>.\n" +
"<P>\n"
+
"Three
of the cookies are associated only with the\n" +
"current
session, while three are persistent.\n" +
"Quit
the browser, restart, and return to the\n" +
"<CODE>ShowCookies</CODE>
servlet to verify that\n" +
"the
three long-lived ones persist across sessions.\n" +
"</BODY></HTML>");
}
}
Example : ShowCookies.java
package cwp;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** Creates a table of the cookies associated
with
* the current page.
* <P>
* Taken from Core Web Programming
Java 2 Edition
* from Prentice Hall and Sun
Microsystems Press,
* http://www.corewebprogramming.com/.
* May be freely used or adapted.
*/
public class ShowCookies extends HttpServlet
{
public void doGet(HttpServletRequest
request,
HttpServletResponse response)
throws
ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Active
Cookies";
out.println(ServletUtilities.headWithTitle(title)
+
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + title + "</H1>\n" +
"<TABLE BORDER=1 ALIGN=\"CENTER\">\n" +
"<TR BGCOLOR=\"#FFAD00\">\n" +
" <TH>Cookie Name\n" +
" <TH>Cookie Value");
Cookie[]
cookies = request.getCookies();
if (cookies != null)
{
Cookie
cookie;
for(int
i=0; i<cookies.length; i++) {
cookie = cookies[i];
out.println("<TR>\n" +
" <TD>" + cookie.getName() + "\n" +
" <TD>" + cookie.getValue());
}
}
out.println("</TABLE></BODY></HTML>");
}
}
import javax.servlet.*;
import javax.servlet.http.*;
/** Some simple time savers. Note that most are static methods.
* <P>
* Taken from Core Web Programming Java 2 Edition
* from Prentice Hall and Sun Microsystems Press,
* http://www.corewebprogramming.com/.
* May be freely used or adapted.
*/
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");
}
/** Read a parameter with the specified name, convert
it
* to an int, and return it. Return the designated
default
* value if the parameter doesn't exist or
if it is an
* illegal integer format.
*/
public static int getIntParameter(HttpServletRequest
request,
String paramName,
int defaultValue) {
String paramString = request.getParameter(paramName);
int paramValue;
try {
paramValue = Integer.parseInt(paramString);
} catch(NumberFormatException nfe) { // null
or bad format
paramValue = defaultValue;
}
return(paramValue);
}
/** Given an array of Cookies, a name, and a default value,
* this method tries to find the value of
the cookie with
* the given name. If there is no cookie matching
the name
* in the array, then the default value is
returned instead.
*/
public static String getCookieValue(Cookie[]
cookies,
String cookieName,
String defaultValue) {
if (cookies != null)
{
for(int
i=0; i<cookies.length; i++) {
Cookie cookie = cookies[i];
if (cookieName.equals(cookie.getName()))
return(cookie.getValue());
}
}
return(defaultValue);
}
/** Given an array of cookies and a name, this method
tries
* to find and return the cookie from the
array that has
* the given name. If there is no cookie matching
the name
* in the array, null is returned.
*/
public static Cookie getCookie(Cookie[]
cookies,
String cookieName) {
if (cookies != null)
{
for(int
i=0; i<cookies.length; i++) {
Cookie cookie = cookies[i];
if (cookieName.equals(cookie.getName()))
return(cookie);
}
}
return(null);
}
/** Given a string, this method replaces all occurrences
of
* '<' with '<', all occurrences
of '>' with
* '>', and (to handle cases that occur
inside attribute
* values), all occurrences of double quotes
with
* '"' and all occurrences of '&'
with '&'.
* Without such filtering, an arbitrary string
* could not safely be inserted in a Web page.
*/
public static String filter(String input) {
StringBuffer filtered = new StringBuffer(input.length());
char c;
for(int i=0; i<input.length(); i++) {
c = input.charAt(i);
if (c == '<') {
filtered.append("<");
} else if (c == '>') {
filtered.append(">");
} else if (c == '"') {
filtered.append(""");
} else if (c == '&') {
filtered.append("&");
} else {
filtered.append(c);
}
}
return(filtered.toString());
}
}
Example: ShowSession.java
package cwp;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.net.*;
import java.util.*;
/** Simple example of session tracking.
* <P>
* Taken from Core Web Programming
Java 2 Edition
* from Prentice Hall and Sun
Microsystems Press,
* http://www.corewebprogramming.com/.
* May be freely used or adapted.
*/
public class ShowSession extends HttpServlet
{
public void doGet(HttpServletRequest
request,
HttpServletResponse response)
throws
ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Session
Tracking Example";
HttpSession
session = request.getSession(true);
String heading;
Integer accessCount
=
(Integer)session.getAttribute("accessCount");
if (accessCount ==
null) {
accessCount
= new Integer(0);
heading
= "Welcome, Newcomer";
} else {
heading
= "Welcome Back";
accessCount
= new Integer(accessCount.intValue() + 1);
}
// Use setAttribute
instead of putValue in version 2.2.
session.setAttribute("accessCount",
accessCount);
out.println(ServletUtilities.headWithTitle(title)
+
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + heading + "</H1>\n" +
"<H2>Information on Your Session:</H2>\n" +
"<TABLE BORDER=1 ALIGN=\"CENTER\">\n" +
"<TR BGCOLOR=\"#FFAD00\">\n" +
" <TH>Info Type<TH>Value\n" +
"<TR>\n" +
" <TD>ID\n" +
" <TD>" + session.getId()
+ "\n" +
"<TR>\n" +
" <TD>Creation Time\n" +
" <TD>" +
new Date(session.getCreationTime())
+ "\n" +
"<TR>\n" +
" <TD>Time of Last Access\n" +
" <TD>" +
new Date(session.getLastAccessedTime())
+ "\n" +
"<TR>\n" +
" <TD>Number of Previous Accesses\n" +
" <TD>" + accessCount +
"\n" +
"</TABLE>\n" +
"</BODY></HTML>");
}
/** Handle GET and POST requests
identically. */
public void doPost(HttpServletRequest
request,
HttpServletResponse response)
throws
ServletException, IOException {
doGet(request, response);
}
}