/*
 * @(#)Counter.java	1.43 97/07/17
 * 
 * Copyright (c) 1996-1997 Sun Microsystems, Inc. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of Sun
 * Microsystems, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with Sun.
 * 
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
 * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
 * THIS SOFTWARE OR ITS DERIVATIVES.
 * 
 * CopyrightVersion 1.0
 */
import java.io.*;
import java.util.*;

import javax.servlet.*;
import javax.servlet.http.*;

//import sun.servlet.http.Cookie;


/**
 * Simple servlet to demonstrate the "Cookie" API.  It uses a cookie to
 * serve as a counter that increments each time this web page is visited
 * during a user session.  That counter, along with some other data, is
 * shown on an HTML page dynamically generated by this servlet.
 *
 * <P> Of course, other kinds of data can be stored using cookies.  You
 * should look at the privacy and security guidelines in RFC 2109 before
 * you start to use cookies, and be clear to your customers what value
 * your cookies are providing to them.
 *
 * @version 1.43
 * @author  David Brownell
 * @author  Pavani Diwanji
 */
public
class Counter extends HttpServlet {

    //
    // Name of the main cookie saved by this servlet.
    //
    private static final String	counterName = "counter";

    //
    // Default initial value of session cookies maintained by counter
    // servlets.  May be overriden by an instance's init parameter.
    //
    static final int		defaultInitialValue = 10;

    //
    // Actual initial value used by this servlet instance.  There could
    // be several such instances, for different parts of the web site's
    // URL namespace, with different initial values.
    //
    // User agents maintain "live" counter values in cookies which are
    // presented with requests, and the servlet increments such values
    // in its responses.
    //
    private int			initialValue;



    /**
     * Initializes the servlet.  Session counters normally start at ten,
     * but that may be overridden by providing an initialization parameter
     * named "initial" with a value which is a decimal number.  This lets
     * different "counter" servlets have different initial values, as well
     * as letting different user sessions have different actual values.
     */
    public void init(ServletConfig conf) throws ServletException {
	String		s;

	super.init(conf);

	if ((s = getInitParameter ("initial")) == null)
	    initialValue = defaultInitialValue;
	else {
	    try {
		initialValue = Integer.parseInt (s);
	    } catch (NumberFormatException e) {
		initialValue = defaultInitialValue;
		log ("** Non-numeric format for 'initial' parameter:  " + s);
	    }
	}
    }


    /**
     * Handles a request.  It does this by updating a per-request "counter"
     * cookie from the request, and storing it via the response.  Output of 
     * the servlet is a simple web page showing the original value of the
     * session's counter, and some other data.
     */
    protected void doGet (HttpServletRequest req, HttpServletResponse res)
	throws ServletException, IOException
    {
	int			counter = initialValue;
	Cookie			cookies [], c = null;
	boolean			hadCookies = false, hadCounter = false;

	//
	// We do all the cookie work before we start writing output,
	// since once we start writing data the headers (with or
	// without cookies) can get flushed at any time.
	//
	if ((cookies = req.getCookies ()) != null) {
	    hadCookies = true;

	    for (int i = 0; i < cookies.length; i++) {
		if (cookies [i].getName ().equals (counterName)) {
		    try {

			// clone this cookie to keep using the browser's
			// version of the cookie protocol

			c = (Cookie) cookies [i].clone ();
			counter = Integer.parseInt (c.getValue ());
			c.setValue (Integer.toString (counter + 1));
			hadCounter = true;
			
		    } catch (NumberFormatException e) {
			// should never happen ...
			c = null;
		    }
		    break;
		}
	    }
	}

	//
	// Always save a "counter" cookie, taking care to set the
	// attributes that weren't settable by cloning the original.
	//
	if (c == null)
	    c = new Cookie (counterName, Integer.toString (counter));
	c.setComment ("Supports Cookie Counter Demo Servlet");
	// c.setMaxAge (2 * 24 * 60 * 60);		// 2 days
	// c.setPath ("/");
	res.addCookie (c);

	if (false) {
	    // add a new cookie, discarded on browser exit, to see
	    // how multiple cookies are dealt with
	    c = new Cookie (
		"gensym-" + (System.currentTimeMillis () & 0x0ff),
		new Date().toString ());
	    c.setComment ("Show multi-cookie support");
	    res.addCookie(c) ;
	}



	//
	// TRY THIS:
	//
	// (A) Design a simple form for letting people create new cookies.
	// Generate that form in the HTML below.  Use the POST action, and
	// create the new cookie before generating output below.
	//
	// (B) Do something similar for letting users delete one or more
	// of the cookies reported by the user agent ... including the
	// counter itself, as one way to reinitialize a session!
	//
	// (C) Offer form controls over cookies' paths and maximum ages.
	//
	// (D) Experiment to see how different web browsers handle version
	// zero cookies.  Which features are handled inconsistently?
	//
	// (E) See how this page reacts to HTTP's "HEAD" methods.
	//
	// (F) Normally, pages used to set cookies will not be cached.
	// This will probably be true of most dynamically generated web
	// content.  Are there other pages in your website which should
	// not be widely cached by proxies and browsers?  Why?
	//



	//
	// Generate the response message ... an HTML page that shows all
	// cookies, their values, and any attributes, plus some random
	// data about the session that may be interesting.  We buffer the
	// whole response so that HTTP keepalive can be used.
	//
	ByteArrayOutputStream	bytes;
	PrintStream		out;

	bytes = new ByteArrayOutputStream (4096);
	out = new PrintStream (bytes);

	out.println ("<HTML><HEAD>");
	out.println ("<TITLE>Cookie Counter</TITLE>");
	out.println ("</HEAD><BODY BGCOLOR=#eeeeff>");
	out.println ("<CENTER><H1>Cookie Counter</H1></CENTER>");

	if (hadCounter) {
	    out.println ("<CENTER>");
	    out.println ("<P> <em><b>Your session's counter was ");
	    out.println (counter);
	    out.println (" before you visited this page.</b></em>");
	    out.println ("<P> The counter has been incremented.");
	    out.println ("</CENTER>");
	} else {
	    out.println ("<P> You presented no session cookie.  A new");
	    out.println ("cookie was created, with an initial counter");
	    out.println ("holding the value " + counter + ".");
	}


	if (hadCookies) {
	    out.println ("<P> You presented these cookies: <OL>");
	    for (int i = 0; i < cookies.length; i++) {
		String	temp;

		out.println ("<LI> Name = ");
		out.println (cookies [i].getName ());
		out.println (", Value = ");
		out.println (cookies [i].getValue ());

		//
		// IETF standard cookies expose these attributes, but
		// the original (and still most common) style cookies
		// hide this data on the client side.
		//
		if ((temp = cookies [i].getDomain ()) != null) {
		    out.println (", Domain = ");
		    out.println (cookies [i].getDomain ());
		}
		if ((temp = cookies [i].getPath ()) != null) {
		    out.println (", Path = ");
		    out.println (cookies [i].getPath ());
		}
	    }
	    out.println ("</OL>");
	}


	out.println ("<P> Watch the value of the counter change as you");
	out.println ("reload this page!  The counter is updated by the");
	out.println ("servlet which dynamically generates this web page.");

	out.println ("<P> Try comparing how different browsers work with");
	out.println ("cookies set using these two URLs on this server:  ");
	out.println ("<a href=/counter.html>/counter.html</a>, and");
	out.println ("<a href=/servlet/Counter>/servlet/Counter</a>.");
	out.println ("These URLs refer to different 'counter' servlets,");
	out.println ("which initialize their counts to different values.");


	String temp = req.getHeader ("User-Agent");
	out.println ("<P> Your browser is <em>"
	    + ((temp != null) ? temp : "not known!") + "</em>.");

	// XXX
	// This time printing crashes IIS!!!!!

	/*
	out.println ("<P>The cookie server's time is now <em>"
	    + new Date () + ".</em>");
	    */

	out.println ("</BODY></HTML>");
	out.flush ();


	//
	// Now that we've buffered up the entire message:  write all our
	// header fields, then the body.  We buffered it up so we can set
	// content length ... ensuring we can use connection keep-alive,
	// for the best networking performance.
	//
	// With HTTP/1.1 clients guaranteed, we could set the headers
	// and just write to the output stream (using chunked encoding)
	// with no worries about preventing keep-alive.
	//

	res.setContentType("text/html");	// ;charset=us-ascii
	res.setContentLength (bytes.size ());
	// ... Last-Modified:  right now!

	bytes.writeTo (res.getOutputStream ());
    }

    /**
     * Describes what this servlet does.
     */
    public String getServletInfo() {
	return "Demonstrates the 'Cookie' API for user sessions";
    }
}
