A single state is represented by Model-View-Controller components, using
the following naming convention: <state>.jsp, <state>Servlet,
and <state>Model.
Goals:
The main consequence of using the MVC system is that there will be more
files than if we didn't use MVC. For every state, there are at least
three files that are involved. However, each file should be much
simpler and easier to understand than if these things were in fewer files,
so it seems to be an acceptable tradeoff.
/**
* Performs simple conditional logic to determine which state to
* transition to.
**/
public void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException {
if (wasCancelChosen(request)) {
discardClientQualInfoModel(request);
enterState("index.jsp", request, response);
} else { // next was chosen
updateClientQualInfoModel(request);
if (getClientQualInfoModel(request).isInvalid()) {
enterState("ClientQualInfo.jsp", request, response);
} else {
createClientQualConfirmModel(request);
enterState("ClientQualConfirm.jsp", request, response);
}
}
}
private void discardClientQualInfoModel(HttpServletRequest request) throws ServletException {
//
// Remove the model from the session to allow the garbage collector
// to reclaim memory.
//
HttpSession theSession = request.getSession();
theSession.removeAttribute("clientQualInfoModel");
}
/**
* Return the ClientQualInfoModel corresponding to the session belonging to
* the given request.
**/
private ClientQualInfoModel getClientQualInfoModel(HttpServletRequest request) throws ServletException {
HttpSession theSession = request.getSession();
return (ClientQualInfoModel) theSession.getAttribute("clientQualInfoModel");
}
private void createClientQualConfirmModel(HttpServletRequest request) throws ServletException {
ClientQualConfirmModel theModel = new ClientQualConfirmModel();
//
// Set model properties (not shown here), then add the model to the session
// so that it is available to the next view.
//
HttpSession theSession = request.getSession();
theSession.setAttribute("clientQualConfirmModel", theModel);
}
/**
* Set the model properties using information gleaned from the request.
* Typically this will be pulling info out of forms and setting properties
* on the model.
**/
private void updateClientQualInfoModel(HttpServletRequest request) throws ServletException {
ClientQualInfoModel theModel = getClientQualInfoModel(request);
theModel.getUser().setFirstName(request.getParameter("firstName"));
theModel.getUser().setLastName(request.getParameter("lastName"));
//
// etc.
//
}
/**
* Forwards the request and response to the given stateView, which
* is typically a jsp. Prior to calling this method, please ensure
* that all business objects that the stateView depends on are available
* in the request (either put them in the session or make them available
* as page scope beans).
**/
private void enterState(String stateView,
HttpServletRequest request,
HttpServletResponse response) throws ServletException {
RequestDispatcher theDispatcher = ServletContext.getRequestDispatcher(stateView);
theDispatcher.forward(request, response);
}
<%!
//
// Returns <FONT COLOR="red"> if the given fieldName in the given ClientQualInfoModel
// is invalid, otherwise returns <FONT COLOR="black">. Use it like this:
//
// <%= fontColorTagFor("firstName", clientQualInfoModel) %>
// <B> First Name </B>
// </FONT>
//
public String fontColorTagFor(String fieldName, ClientQualInfoModel model) {
if (model.isFieldValid(fieldName)) {
return "<FONT COLOR=\"black\">";
} else {
return "<FONT COLOR=\"red\">";
}
}
%>
<jsp:usebean id="clientQualInfoModel" class="ClientQualInfoModel" scope="page" />
<%
if (clientQualInfoModel.isInvalid()) {
//
// print out the fields that need to be changed
//
%>
<FONT COLOR="red">
Please review the indicated fields:<BR>
<jsp:getProperty name="clientQualInfoModel" property="validationErrorList" />
</FONT>
<%
}
%>
<FORM ACTION="ClientQualInfoServlet" METHOD="POST">
<%= fontColorTagFor("firstName", clientQualInfoModel) %>
<B>First Name</B>
</FONT>
<INPUT TYPE="TEXT" NAME="firstName" VALUE="<%= clientQualInfoModel.getFirstName() %>" SIZE="20" MAXLENGTH="40">
<%= fontColorTagFor("lastName", clientQualInfoModel) %>
<B>Last Name</B>
</FONT>
<INPUT TYPE="TEXT" NAME="lastName" VALUE="<%= clientQualInfoModel.getLastName() %>" SIZE="20" MAXLENGTH="40">
<!-- etc. -->
public class ClientQualInfoModel extends FormModel {
//
// FormModel would be an abstract class that provides
// functionality used in all form models, such as
// collecting validation problems.
//
private User user;
private Vector validationProblems = new Vector();
//
// Constructors
//
public ClientQualInfoModel(User user) {
this.user = user;
}
//
// Accessing
//
public String getFirstName() {
return this.user.getFirstName();
}
public String getLastName() {
return this.user.getLastName();
}
// etc.
public boolean isFieldValid(String fieldIdentifier) {
//
// This method probably belongs on a superclass.
// It would use reflection to look for a method
// called validate<fieldIdentifier>() which returns
// a boolean. If no such method exists, assume
// no validation is necessary and return true.
// This would probably be a useful method on
// PersistentObject as well--the validate() method
// could just use reflection to call all validateXXX()
// methods on the object.
//
???
}
public boolean isInvalid() {
//
// The following is the easy case, where the validation
// of the state is equivalent to making sure that a bunch
// of business objects are valid. In some cases (e.g.
// multipage forms), business objects may be in a partially
// valid state but page needs to be validated to continue to
// the next page. In these cases one cannot simply validate
// the business objects, so it is necessary to write some
// code to do the validation.
//
try {
this.user.validate();
return false;
} catch (ValidationException e) {
collectValidationProblems(e);
return true;
}
}
}
if (! request.isRequestedSessionIdValid()) {
enterState("login.jsp", request, response);
}