/*
 * @(#)Counter.java	1.43 97/07/17
 * 
 * Copyright (c) 1996, 2004, Oracle. 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/>
 * <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.
 *
 * @author David Brownell
 * @author Pavani Diwanji
 * @version 1.43
 */
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";
    }
}
