EQUIP2 WebApp Tutorial Application Website Structure

Chris Greenhalgh, 2007-02-02; last updated 2007-02-05

Introduction

The tutorial includes an implementation of a simple active web site/application. This is minimally specified in EQUIP2_WebApp_Tutorial_Application_Specification.html. This document defines/describes the structure of the Website for the game and the basic HTTP interactions it supports.

Note that this tutorial (and the application itself) is incomplete, but it already has quite a lot of useful examples. E.g. player registration, login, and custom form-based adding and editing of dataspace objects.

General structure

The web pages are generated from JSPs in WEB-INF/jsp/publicweb/.

Simple HTML Cascading StyleSheet (CSS) for some page formatting; the style file is site/publicweb.css (i.e. outside WEB-INF/ and therefore served directly by Tomcat as a static file).

Web-related Java classes (form-backing beans and controllers) are in Java package equip2.webapptutorial.publicweb.

The database classes are as already described in EQUIP2_WebApp_Defining_the_Dataspace.html.

The web functionality is organised with the path .../publicweb/... and is handled by the Spring Dispatcher servlet 'publicweb', which is configured by WEB-INF/publicweb-servlet.xml.

Messages from controllers (typically form rejection messages) are specified in WEB-INF/classes/publicweb.properties (as configured in the Servlet's message resolver).

Main elements

The application relies on the identification of players as they use the site. It further distinguishes between three main player types: admin, trusted and untrusted.

The main areas of the site/application are:
These are described below.

New player registration

The first element of the site/application is a new player registration area. In the initial application a simple single-stage registration is supported via the the URL http://.../publicweb/register.html. This is generated from the JSP register_form.jsp (the configured formView of the controller, below), which in turn uses jsp:include actions to generate the individual form elements from the JSP register_form_input.jsp. Form submission is a HTTP POST to the same URL.

The form is handled by the form controller equip2.webapptutorial.publicweb.RegisterFormController, and the form is backed by an instance of class equip2.webapptutorial.publicweb.RegisterFormBean. This has properties name, emailAddress, password and password2 (for password confirmation). Form validation is currently performed in the Controller's onSubmit method rather than a separate validator and checks that:

If successful and new Player database ID is allocated using equip2.spring.db.IDAllocator (part of the equip2webapp equip2webdb.jar), a new Player object is created (instance of generated class equip2.webapptutorial.db.Player as defined in etc/equip2.webapptutorial.db.Player.xml), initialised, and added the dataspace (note that the controller is provided with reference to the dataspace via the Spring configuration of its dataspace property):

// begin dataspace session
ISession session = dataspace.getSession();
session.begin(ISession.READ_WRITE);
// check if email address is in use - query for Player with emailAddress = ...
QueryTemplate playerQuery = new QueryTemplate(Player.class);
playerQuery.addConstraintEq("emailAddress", registerBean.getEmailAddress());
// don't actually need the values (from match()), just if there are any
int count = session.count(playerQuery);
if (count>0)
errors.rejectValue("emailAddress", "in-use", "Registration emailAddress already in use (default message");
...
// create new Player
Player player = new Player();
// allocate ID of form 'P123', ensuring it is not already in use for a Player...
player.setID(IDAllocator.getNewID(session, Player.class, "P", null));
// other fields
player.setName(registerBean.getName());
// add to dataspace
session.add(player);
// dataspace stuff all done
session.end();

The registration controller also marks the new player as logged in within the current session by placing an instance of equip2.webapptutorial.publicweb.LoginSessionBean into the session as attribute LoginSessionBean.SESSION_ATTRIBUTE_NAME ('loginSessionBean').

The success view after registration is JSP register_ok.jsp.

Note the use of the JSP get_current_player.jsp, which uses the Player ID in the LoginSessionBean in the HTTP Session to fetch information about the current player.

Player Log on

Player log in/out is handled in the same way as documented in EQUIP2_WebApp_Login_and_Interceptors.html.

The log in form is login_form.jsp, access via the URL http://.../publicweb/login.html.

This is modified compared to the login example to use the stylesheet (site/publicweb.css) and register_form_input.jsp for the individual inputs. It is backed by an instance of equip2.webapptutorial.publicweb.LoginFormBean (with properties emailAddress and password). The form controller is equip2.webapptutorial.publicweb.LoginFormController. This is based on the LoginFormController from EQUIP2_WebApp_Login_and_Interceptors.html but:

 // open dataspace session - for read only (no changes here)
ISession session = dataspace.getSession();
session.begin(ISession.READ_ONLY);
// check if email address is in use - query for Player with emailAddress = ...
QueryTemplate playerQuery = new QueryTemplate(Player.class);
playerQuery.addConstraintEq("emailAddress", loginBean.getEmailAddress());
Object matches[] = session.match(playerQuery);

boolean authenticated = false;
Player player = null;
// there should only be zero or one!
if (matches.length>0) {
// coerce is safe from Query type
player = (Player)matches[0];
if (loginBean.getPassword().equals(player.getPassword()))
// matches!
authenticated = true;
}
// end of dataspace stuff
session.end();

The login interceptor, equip2.webapptutorial.publicweb.CheckLoginInterceptor, is an unchanged copy of the one from EQUIP2_WebApp_Login_and_Interceptors.html, configured to check for 'loginSessionBean' in the session, or redirect on failure to the login page.

Log out is again directly based on the example, and uses the controller equip2.webapptutorial.publicweb.LogoutController and the configuration view logged_out.jsp accessed via the URL http://.../publicweb/logout.html.

Editing player details

Player editing of their own information is handled via the request URL http://.../publicweb/player_edit.html. This has form view player_edit_form.jsp, is backed by class equip2.webapptutorial.publicweb.RegisterFormBean as 'player' (which includes property 'oldPassword' specifically for this purpose) and is handled by controller equip2.webapptutorial.publicweb.PlayerEditFormController.

Note that formBackingObject queries the dataspace while making the initial form-backing object pulls information about the player from the dataspace to populate it:

    protected Object formBackingObject(HttpServletRequest request) throws Exception
{
RegisterFormBean formBean = new RegisterFormBean();

if (!this.isFormSubmission(request))
{
// on first access (not submission), copy information to form-backing bean
// get current player information from Http session
HttpSession httpSession = request.getSession();
LoginSessionBean lsb = (LoginSessionBean)httpSession.getAttribute(LoginSessionBean.SESSION_ATTRIBUTE_NAME);

// open dataspace session - for read
ISession session = dataspace.getSession();
session.begin(ISession.READ_WRITE);

// get current player details
Player player = (Player)session.get(Player.class, lsb.getUserId());

// done with dataspace
session.end();

formBean.setEmailAddress(player.getEmailAddress());
formBean.setName(player.getName());
}
return formBean;
}

On successful submission the form value(s) are pushed back to the dataspace. In this situation be careful to consider possible concurrent changes to these values (e.g. by other forms/user or threads within the application).

Creating an ADMIN player

For now, use the database interface directly to edit the player's role to ROLE_ADMIN, probably starting from http://.../db/view_list.htm?class=equip2.webapptutorial.db.Player.

Creating a new topic

A Topic is represented by an instance of class equip2.webapptutorial.db.Topic, as generated from etc/equip2.webapptutorial.db.Topic.xml. The generic DB forms can be used to create Topics, but typically only by player with Tomcat operator access. In most applications a more tailored form, integrated with the rest of the site will be required.

This is handled by the form with view add_topic_form.jsp, backed by a equip2.webapptutorial.db.Topic object directly, and with controller equip2.webapptutorial.publicweb.AddTopicFormController. The controller:

Note that the success view, add_topic_ok.jsp, simply returns a HTTP temporary redirect to the client, to cause the browser to move on to (in this case) the index page for the newly added topic:

<%-- redirect to topic_index.html?topic=ID (ID from request 'topic') 
--%><% response.sendRedirect("topic_index.html?ID="+
((equip2.webapptutorial.db.Topic)(request.getAttribute("topic"))).getID()); %>

This is often a useful strategy to push from edit actions to re-usable views, and gives the user immediate access to re-usable URLs that can be copied or book-marked.

Topic views are provided by (both reasonable examples of EQUIP2 Taglib use) :

Editing a topic

This is similar to editing a Player (above), but the topic to be edited is identified by a request parameter (ID, the database ID), rather than implicitly from the session, e.g. URL http://.../publicweb/topic_edit.html?ID=T502. The topic editing form, topic_edit_form.jsp, therefore also includes a (hidden) input to preserve this ID (called 'ID', of course).

The form backing object is a equip2.webapptutorial.db.Topic object. In this case the controller, ,equip2.webapptutorial.publicweb.TopicEditFormController gets it directly from the dataspace in formBackingObject:

	...
if (!this.isFormSubmission(request)) {
 String topicId = request.getParameter("ID");
// open dataspace session - for read
ISession session = dataspace.getSession();
session.begin(ISession.READ_WRITE);
// get current topic details
topic = (Topic)session.get(Topic.class, topicId);
// done with dataspace
session.end();
}
return topic;

Next...

[work in progress :-)]

Changes

2007-02-02
2007-02-05