Conversation on HttpSession

Our beer selector web application is so successful that we want to improve it to perform a better selection of beers, now we want to keep a conversation with the user to better understand his requirements. This means we have to keep a conversational state across multiple requests from the same client.

To do that we could use:

A stateful session enterprise javabean. Cool solution, but a bit too much overhead for our simple app. Besides, we would like to use Tomcat, that is not an EJB Container.

A database. We could store temporary result there, that would be fine, too. Just too time consuming, perhaps.

The HttpSession. Actually, also the other two solution would require usage of the HttpSession, to ensure the job would be done correctly.

The container generates a session ID that identify any specific conversation. The most common way to keep the conversation alive is using cookies, and the good thing is that the container does almost all the job related with cookies, from the programmer side is enough to call getSession() on the request to both sending a session cookie in the response, and getting the session ID from the request:
HttpSession session = request.getSession();
Besides, we can ask to the session object if it refers to a new session, or an already existing one:
HttpSession session = request.getSession();
    if (session.isNew())
    // ...
Alternatively we could explicitly specify if we want to work only with an existing session or with a brand new one, passing a boolean to getSession(): passing false means "give me the pre-existing session, or null if we are not in an existing conversation"; true has the same behaviour of the no-parameter getSession().

Let's write a simple conversational web application that uses three subject: a static HTML page, a servlet, a JSP page.

The HTML page has the only purpose of make available to the user a link to the servlet, as a way of starting the conversation.

The servlet checks if the session in the current request is a new one or not, and puts accordingly some information in a session attribute, then delegates to the JSP the job of creating a feedback to the user.

The JSP page shows the result to the user, and provides him a way to call again the servlet, to continue the conversation.

The HTML page could be very simple, actually, the only thing it matters to us is that it would include a link to our servlet, something like this:
<a href="./Conversation.do">Start a conversation</a><br />
We are going to put it at root level in our Web Application.

We create the servlet using the NetBeans wizard, specifying its URL as "/Conversation.do", its name "Conversation" and its class "ch06.Conversation".

We get a Conversation.java file, and we change its processRequest() method in this way:
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
    List<String> report;

    HttpSession session = request.getSession();
    if(session.isNew()) { // 1
        report = new ArrayList<String>();
        report.add("Starting conversation " + session.getId());
    }
    else { // 2
        report = (List<String>)session.getAttribute("report");
        if(report == null) {
            report = new ArrayList<String>();
            report.add("Continuing conversation " + session.getId());
        }
        report.add("Next step in the conversation "
            + session.getId() + "[" + report.size() + "]" );
    }
    session.setAttribute("report", report); // 3

    RequestDispatcher view = request.getRequestDispatcher("Conversation.jsp");
    view.forward(request, response); // 4
}
1. If the session is new, we create a new String List, where we are about to put our information for the user.
2. If the session is not new, we expect to reuse the existing session attribute - if anything goes wrong, we create a new String List.
3. Put the report in the session, to keep available for all the conversation.
4. Delegate the display job to the JSP page.

That is the body of our JSP:
<body>
<h1>Our conversation:</h1>

<%
    Object o = session.getAttribute("report");
    if (o == null || !(o instanceof List)) {
        out.print("No report of a previous conversation!<br />");
    } else {
        List report = (List) o;

        Iterator it = report.iterator();
        int line = 1;
        while (it.hasNext()) {
            out.print("" + line++ + " " + it.next() + "<br />");
        }
    }
%>
    <form method="POST" action="./Conversation.do">
        <p>Get some more information from the web app ...</p>
        <p><input type="SUBMIT"></p>
    </form>
</body>
We check the report session attribute and show its values to the user, then provide a button to call again the servlet, having so a new step in the conversation.

If the user enters this JSP the first time, bypassing the link provided by the HTML page, we'll be in the unpleasant situation of not having a "report" attribute in the session scope, and of starting here a new conversation. The check here on the attribute for null avoid bad surprises. And the check that we made in the servlet for "report" being actually null avoid us a possible crash there, in case the conversation was started in this not-orthodox way.

I have written the first draft of this post while I was reading the sixth chapter of Head First Servlet and JSP, a fun ad interesting book on Java EE.

No comments:

Post a Comment