Coffea Works

Web Frameworks

Using "Just" Model 2

by Neal Ford

This article is the first in a series that looks at one of the most fertile areas in open source Java code -- Web frameworks for Java. The Jakarta site alone features three -- Struts, Turbine, and Tapestry, as well as some "meta-frameworks". Each month, we'll feature a different framework (including the upcoming Java Server Faces), look at its important architectural details, and how it differs from the other frameworks available today.

One of the toughest jobs in evaluating frameworks is doing a fair comparison between them, and even more so because most frameworks' tutorial and documentation tend to stress on the features that make it unique and gloss over its weak areas. To make our work easier, we'll build the same application using each framework, and then do an 'apples to apples' comparison.

Note: The material in this series of articles is adapted from my book, Art of Java Web Development, from Manning Publications (ISBN 1932394060).

Frameworks Defined
Building reusable code is all the rage today - after all, if we can build it once and reuse it over and over, we can build less and less over time. The whole "short" history of software development is an exploration of the layers of abstractions to make building applications easier. Once you have a collection of pre-built, generic parts, you have the beginnings of a framework. A framework is a set of related classes and other supporting elements that make application development easier by supplying pre-built parts. They provide the infrastructure for application development.It's good to build applications with frameworks, since we can leverage the code that others have already written, debugged, and tested. In fact, in some sense, all code is written to a framework. Nobody writes code that parses the contents of HTTP headers and twiddles the bits to generate responses. At an even deeper level, the TCP/IP stack that supports the HTTP is a framework for making networking easier and more reliable. Remember the time when there was contention between what flavors of network protocol to use? We could choose between IPX/SPX (from Novell), NetBUI (from Microsoft), or TCP/IP (from the UNIX world). And, today, we have more choices than ever before. So how do you decide on which framework to use?To go back a bit, frameworks became pervasive because they solved common problems, without seriously compromising the intent of the application that they were meant to support. As development became more complex, we came to rely on frameworks to ease the complexity, which enabled us to write at a higher level of abstraction. To reap the full benefit from a framework, you must evaluate it in the context of the problem you are trying to solve (that is, the application you are currently writing). If the framework makes your job easier without forcing you to compromise, it is a good match. If you constantly have to code around the framework and "kludge" together solutions to problems caused by it, you should discard it.

Model 2 as Your Framework
On a fundamental level, Web applications are just collections of related request/response pairs. When you look at the pieces of the Java Web API, JSP is the clear choice for the response because it is already a type of output document. Servlets are the clear choice for request because the servlet API already handles much of the detail of HTTP. Plain old Java classes are optimally suited for executing code that performs work, such as database access, and business logic. Model 2 Web applications reflect the suitability of each building block by using the simple rule "use what is most naturally suited to solving the design problem of Web applications".Model 2's separation of responsibilities allows the different aspects of the application to be built concurrently. Thus, the Java developers can concentrate on building the models and controllers, and the user interface designers can build user interfaces. When working on an application concurrently, it is important that both the model designer and the user interface designer agree on information flow before significant work begins. Since JSPs rely on information pulled from model JavaBeans, it is critical that the bean authors and user interface designers agree on what information will be required. This avoids headaches later in the process when the user interface cannot effectively pull information from the model without an inordinate amount of work. It is sufficient to agree on the accessors and mutator method names of the beans and what other methods are required. This tells the user interface designers what information is available (through the methods they may call) and the model writers about what information to provide (through the methods they must write). You might also consider extracting all the methods needed by the user interface designers into an interface implemented by the models. This is a firmer contract between the two teams. The user interface designers write to the interface and the model writers adhere to it.If you are the sole author, you have a choice as to whether you want to write the models or the user interface first. If the project is based on information from a database, it makes sense to create the models first. The flow of the application will depend on the structure of the database. The JSPs you create will rely on the model beans for dynamic information. Even if you plan to create the models first, you might want to create a user interface prototype to ensure you are creating models for the correct information you need to display. Just as in the concurrent development case, it is important to think about this early in the process. Essentially, the roles of the model writer and the user interface designer will collapse into one person (you). You will find it just as unrewarding yelling at yourself about a poor design as yelling at someone else. The prototype can be the beginnings of the full-fledged JSP, with static data in lieu of the "real" data. For the sample application in this article, the models are built first because the user interface is simple.

The Model 2 Schedule Application
Our first implementation of this model is a Web application that tracks someone's schedule. It will consist of two pages. The first page is a list of the currently scheduled events, and the second page allows the user to add more events. The first page is shown in Figure 1. The source code for this application appears in the samples archive under the name art_sched_mvc.


Figure 1: The Model 2 Schedule application's first page shows the events scheduled for a busy person

As you can see, the user interface is very sparse. This has been done on purpose, so that the design and architecture of the application become the center of attention. The information flow in Model 2 applications can sometimes be hard to see from the code. Because each artifact that the user sees is the culmination of several classes and JSPs, it is useful to see a collaboration diagram of the interaction before the code. Figure 2 shows the interaction between the classes and JSPs discussed.


Figure 2: The controller servlets create and manipulate the model beans, eventually forwarding the ones with displayable characteristics to a view JSP

In this diagram, the solid lines represent control flow and the dotted lines represent a using relationship. The user invokes the ViewSchedule controller, which creates the appropriate model objects and forwards to ScheduleView. The view allows the user to invoke the ScheduleEntry controller, which also uses the model. It forwards to the ScheduleEntryView, which posts to the SaveEntry controller. If there are validation errors, this controller returns the entry view. Otherwise, it forwards back to the main view to show the results of the addition. The code to make all this happen appears in the following sections of the article.

The Model
The model for this application consists of two classes: a boundary class that handles database access using JDBC, and an entity class that encapsulates information from an single row.
  • The Boundary Class -- The ScheduleBean class is the main model of the application. It's responsible for pulling schedule information from the database. The database structure for this application is very simple (see Figure 3).


Figure 3: The database schema diagram for the Schedule application shows that it is a very simple database structure

The ScheduleBean model class is simple, straight forward JDBC data access, and isn't shown here for space considerations. It is available with the source archive included with this article.
  • The Entity Class - Applications that access rows from SQL tables need an atomic unit of work.That is, you'll need a class that encapsulates a single entity that forms a unit of work that cannot be subdivided. This unit of work is usually implemented as a value object. A value object is a simple class that encapsulates a collection of member variables, corresponding to the columns of a SQL table, and accessor and mutator pairs for each member of the class. Methods in model classes, such as the model bean discussed previously, can use the value object to operate on table rows. If the value object contains methods other than accessors and mutators, they are methods that interact with the internal values. Range checking and other validations are good examples of helper methods in a value object.
The Schedule application uses a value object to encapsulate the event table. It is a simple class that includes properties for each of the fields in the table. The only non-property code in the class is the validate() method, shown here:

public List validate() {
List validationMessages = new ArrayList(0);
if (duration < 0 || duration > 31)
validationMessages.add("Invalid duration");
if (text == null || text.length() < 1)
validationMessages.add("Event must have description");
return validationMessages;
}

Building the Main Controller
In Model 2 applications, the controller servlet is the first point of contact with the user. It is the resource the user invokes in the Web application. The controller servlet is responsible for creating the models, making them work, and then forward them to an appropriate view. In the schedule application, the first controller is the welcome page of the application (see Listing 6).

Listing 6: The ViewSchedule controller

package com.nealford.art.mvcsched;

import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.nealford.art.mvcsched.boundary.ScheduleBean;

public class ViewSchedule extends HttpServlet {

public class ViewSchedule extends HttpServlet {

public void doGet(HttpServletRequest request,
HttpServletResponse response) throws
ServletException, IOException {
ScheduleBean scheduleBean = new ScheduleBean();
try {
scheduleBean.populate();
} catch (Exception x) {
getServletContext().log(
"Error: ScheduleBean.populate()", x);
}
request.setAttribute("scheduleItems",
scheduleBean.getList());
RequestDispatcher rd = request.getRequestDispatcher(
"/ScheduleView.jsp");
rd.forward(request, response);
}
}

Controllers in Model 2 applications tend to be small and this one is no exception. This servlet starts the application by creating a new ScheduleBean, populating it, and then adding it to the request attribute. A RequestDispatcher is created that points to the appropriate view and the request is forwarded to that view. The model bean is already constructed and populated when it passes to the view. Notice that it would be a mistake to defer creating the model bean and populating it in the view. This view consists of the user interface code and nothing else. The relationship between the controller, model class, and view appear in Figure 4.


Figure 4: The controller servlet creates and populates the model class, then forwards it to the view via a request attribute. The view extracts the viewable information and generates the response for the user.

Building the Main View
To complete this request, the view JSP named ScheduleView accepts the forwarded ScheduleBean and displays the results. This JSP is shown in Listing 7.

Listing 7: The Introductory View Page -- ScheduleView.jsp

<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<jsp:useBean id="scheduleItems" scope="request"
type="java.util.List" />

<html>
<head>
<title>
Schedule Items
</title>
</head>
<body>

<p><h2>Schedule List</h2></p>
<table border="2">

<tr bgcolor="yellow">
<c:forEach var="column" items="Start,Duration,Text,Event">
<th><c:out value="${column}"/></th>
</c:forEach>
</tr>
<tr>
<c:forEach var="item" items="${scheduleItems}">
<tr>
<td><c:out value="${item.start}" /></td>
<td><c:out value="${item.duration}" /></td>
<td><c:out value="${item.text}" /></td>
<td><c:out value="${item.eventType}" /></td>
</tr>
</c:forEach>
</table>
<p>

<a href="scheduleentry">Add New Schedule Item</a>

</body>
</html>

This JSP uses the list supplied by the ScheduleBean model from the controller (from Listing 6) via the request collection.Depending on how often the user needs to see updated schedule information, this list of schedule items could have been added to the user's session instead. The advantages of this approach is that it would result in fewer database accesses for this user upon repeated viewing of this page. The controller could check for the presence of the list and pull it from the session on subsequent invocations. The disadvantage of adding it to the user session is threefold.First, because the list object exists for the lifetime of the user's session, it will occupy more server memory.Second, if changes are made and the populate() method isn't called to refresh the list, the user will see stale data. When building an application, you must consider tradeoffs between scalability for speed (adding model lists to the session) or speed for scalability (adding model lists to the request). The topics of performance and scalability reappear in Chapters 14 and 15 of 'Art of Java Web Development'. Third, in a clustered system, either you need a router to redirect calls to the same server or you have to have a way of sharing session data across all instances of the application on all machines. If you don't handle caching via one of these two mechanisms, you end up with one cached copy per server.The significant code in this view (shown in Listing 7) appears in the body of the table. The goal for any view page is to make it as clear as possible to a non-Java developer (a user interface designer, that is). To that end, it is preferable to use the XML syntax when accessing bean properties rather than the expression syntax. The XML syntax:

<jsp:getProperty name="item" property="start"/>

is easier to for non-developers to read, as compared to the expression syntax:

<%= item.getStart() %>

Even though they are functionally equivalent, the more readable is preferred. To enable the use of the XML syntax, the page must have access to the ScheduleItem value object handed back to us from the model bean. The model returns a collection (specifically, a java.util.List) of ScheduleItem objects. In the code earlier, the list is iterated over and each item is pulled from the collection, type cast, and added to the pageContext collection. This allows the use of the XML syntax to retrieve the values in the table.When using Model 2 design methodology, the primary goal is to place as little scriptlet/expression code as possible in the JSP. In the view JSP in Listing 7, the only scriptlet code present deals with iterating over the collection so that the items can be displayed. As a rule of thumb, each occurrence of a scriptlet tag doubles the potential maintenance headaches. This completes the first page of the application. The user invokes the controller, which creates the model and forwards to the view.

Building the Entry Controller
The main page of the application has a link at the bottom allowing the user to add new schedule items. This link leads the user to the entry portion of the application, as shown in Figure 5.


Figure 5: The entry part of the application allows the user to add new schedule entries

The entry controller, ScheduleEntry, is shown in Listing 8.

Listing 8: The Entry Controller

package com.nealford.art.mvcsched;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;

public class ScheduleEntry extends HttpServlet {

public void doGet(HttpServletRequest request,
HttpServletResponse response) throws
ServletException, IOException {
request.getRequestDispatcher("/ScheduleEntryView.jsp").
forward(request, response);
}
}

The ScheduleEntry controller is extremely simple. In fact, this controller is technically not even necessary - the link on the previous page could simply point directly to the JSP dispatched by this controller. However, it is still a good idea to have a controller. First, because it makes the application consistent --you always link or post to a controller servlet, not a JSP. Second, chances are excellent that sometime in the future that code will become necessary in this controller. If the controller is already present, you don't have to modify any of the code surrounding it, you can simply add the required functionality.

Building the Entry View
The view page forwarded by ScheduleEntry (shown in Listing 8) is much more complex than the previous JSP. This page is responsible for two tasks: allowing the user to enter a new record and handling validation errors that have returned for an unsuccessful entry. The first portion of this JSP is shown in Listing 9.

Listing 9: The Header of the Entry View

<%@ page import="java.util.*" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>

<jsp:useBean id="scheduleItem" scope="request"
class="com.nealford.art.mvcsched.ScheduleItem" />
<jsp:useBean id="errors" scope="request"
class="java.util.ArrayList" type="java.util.List" />
<jsp:useBean id="scheduleBean" scope="page"
class="com.nealford.art.mvcsched.ScheduleBean" />
<jsp:setProperty name="scheduleItem" property="*" />

<HTML>
<HEAD>
<TITLE>
Add Schedule Item
</title>
</head>
<BODY>
<H3>
Add New Schedule Entry
</h3>

At the top of the page, there is a declaration for a scheduleItem reference. This declaration is scoped to the request, indicating that this object may have been passed to this JSP. The controller servlet in Listing 8 passes nothing to this page. We'll see that the validation controller may pass an invalid record back, via this variable.An errors bean is declared next. Referring back to the ScheduleItem.validate() method in Listing 5, a failed validation generates a List object, which is returned to this page so that the list of errors may appear. It is possible to pass generic versions of concrete objects via the use of the "type" attribute of the useBean tag. The type attribute is designed for exactly this situation. Even though the class identifies it as an ArrayList, it can be passed as the more generic List class. However, notice that both the class and type attributes are included, which is unusual. This is necessary because, in the initial case, no error list is passed to this JSP. If just the type attribute appears, an error will be generated when no list is passed to this JSP since it cannot automatically instantiate the bean. In this case, I include both, which allows the page to create an empty ArrayList when no list is passed and use the List when it is.Next, ScheduleBean is declared, with the page scope, on this page. It is only required to get the list of event types, so it can be instantiated locally. The last part of the prelude is a call to populate the scheduleItem instance with any parameter values passed in the request, which is also used in the validation case.The next portion of the page, shown in Listing 10, handles validation errors.

Listing 10: The Validation Display Section of ScheduleEntryView.jsp

<%
if (! errors.isEmpty()) {
%>
<hr/>
<b><u>Validation Errors</u><//b3><br>
<font color="red">

<c:forEach var="error" items="${errors}">
<c:out value="${error}" /><br>
</c:forEach>
</font>
<hr/>
<%
}
%>

This section of the JSP checks to see if any errors have passed back by checking the errors collection for records. If the JSP was called in response to a validation error, the error list will not be empty. The JSP runtime ensures that all beans have been instantiated, either because of being passed to the page or via automatic construction. Thus, this errors object will never be null. If errors are present, the list is iterated over (using JSTL tags), printing out each error in turn before showing the rest of the page. Figure 6 shows this result when a user has entered invalid data.


Figure 6: When the user enters invalid data, the application redirects them back to the entry page and displays a list of errors for them to repair

The last section of the page handles the data entry chores (see Listing 11).

Listing 11: The Data Entry Section of ScheduleEntryView.jsp

<!-- Data entry form -->
<form action="saveentry" method="post">
<table border="0" width="30%" align="left">
<tr>
<th align="right">
Duration
</th>
<td align="left">
<input name="duration" size="16"
value="<jsp:getProperty name="scheduleItem"
property="duration"/>">
</td>
</tr>
<tr>
<th align="right">
Event Type
</th>
<td align="left">
<select name="eventTypeKey">
<%
//-- get the list of allowable event types from bean
int currentValue = scheduleItem.getEventTypeKey();
Map eventMap = scheduleBean.getEventTypes();
Set keySet = eventMap.keySet();
Iterator eti = keySet.iterator();
while (eti.hasNext()) {
int key = ((Integer) eti.next()).intValue();
%>
<option value='<%= key %>'<%= (currentValue == key ?
"selected" : "") + ">" +
eventMap.get(new Integer(key)) %>
<%
}
%>
</select>
</td>
</tr>
<tr>
<th align="right">
Start
</th>
<td align="left">
<input name="start" size="16" value="<jsp:getProperty
name="scheduleItem" property="start"/>"/>
</td>
</tr>
<tr>
<th align="right">
Text
</th>
<td align="left">
<input name="text" size="16" value="<jsp:getProperty
name="scheduleItem" property="text"/>"/>
</td>
</tr>

<tr>
<td align="right">
<input type="submit" name="Submit" value="Submit">
</td>
<td align="right">
<input type="reset" value="Reset">
</td>
</tr>
</table>
</form>

</body>
</html>

The entry portion of the ScheduleEntryView JSP has the requisite HTML elements for entry, including both inputs and a select tag. Notice that in each of the inputs the value appears as a call to the scheduleItem bean. This results in no value when the page is initially called but allows the values of the input form to be re-populated when a validation error occurs. Using this property tag syntax means that the user doesn't have to re-enter the valid values.The code for the HTML select tag is more convoluted. The select tag encapsulates a set of option tags, one of which may be flagged as selected. The list of items should never be hard coded into the JSP. This information must come from the model because it is a business rule for this application. It is a serious mistake to sprinkle hard-coded values throughout the view portion of the application because it breaks Model 2's clean separation of responsibilities. It also becomes a maintenance nightmare when (not if) those values change. Even when using Model 2 for separation of concerns, complexity still manages to creep in because of the necessary interface between display and logic.

Building the Save Controller
The last file in the Model 2 schedule application is the SaveEntry controller that handles validation and update (see Listing 12).

Listing 12: The SaveEntry Controller, which handles Validation and Updates

package com.nealford.art.mvcsched.controller;

import java.io.IOException;
import java.util.List;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.nealford.art.mvcsched.boundary.ScheduleDb;
import com.nealford.art.mvcsched.entity.ScheduleItem;
import com.nealford.art.mvcsched.util.ScheduleAddException;

public class SaveEntry extends HttpServlet {

public void doPost(HttpServletRequest request
HttpServletResponse response) throws
ServletException, IOException {
ScheduleItem newItem = populateNewItemFromRequest(request);
List validationErrors = newItem.validate();
if (!validationErrors.isEmpty())
returnToInput(request, response, newItem,
validationErrors);
else {
addNewItem(newItem);
forwardToSchedule(request, response);
}
}

private void addNewItem(ScheduleItem newItem) throws
ServletException, IOException {
try {
new ScheduleDb().addRecord(newItem);
} catch (ScheduleAddException sax) {
getServletContext().log("Add error", sax);
}
}

private void forwardToSchedule(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
RequestDispatcher dispatcher =
request.getRequestDispatcher("/viewschedule");
dispatcher.forward(request, response);
}

private void returnToInput(HttpServletRequest request,
HttpServletResponse response,
ScheduleItem newItem,
List validationErrors) throws
ServletException, IOException {
RequestDispatcher dispatcher =
request.getRequestDispatcher(
"/ScheduleEntryView.jsp");
request.setAttribute("scheduleItem", newItem);
request.setAttribute("errors", validationErrors);
dispatcher.forward(request, response);
return;
}

private ScheduleItem populateNewItemFromRequest(
HttpServletRequest
request) {
ScheduleItem newItem = new ScheduleItem();
populateDuration(request, newItem);
populdateEventTypeKey(request, newItem);
populateStart(request, newItem);
populateText(request, newItem);
return newItem;
}

private void populateText(HttpServletRequest request,
ScheduleItem newItem) {
String text = request.getParameter("text");
if (text != null)
newItem.setText(text);
}

private void populateStart(HttpServletRequest request,
ScheduleItem newItem) {
String start = request.getParameter("start");
if (start != null)
newItem.setStart(start);
}

private void populdateEventTypeKey(HttpServletRequest request,
ScheduleItem newItem) {
String typeKey = request.getParameter("eventTypeKey");
try {
if (typeKey != null)
newItem.setEventTypeKey(Integer.parseInt(typeKey));
} catch (NumberFormatException nfx) {
getServletContext().log("Conversion error:eventTypeKey",
nfx);
}
}

private void populateDuration(HttpServletRequest
request, ScheduleItem newItem) {
String duration = request.getParameter("duration");
try {
if (duration != null)
newItem.setDuration(Integer.parseInt(duration));
} catch (NumberFormatException nfx) {
getServletContext().log("Conversion error:duration",
nfx);
}
}
}

The top-level public method performs the step-by-step behavior of the controller servlet. It populates a new item from request parameters, performs a validation on the item, and dispatches it to either the input page (if errors exist) or adds the item and forwards to the main view. The addNewItem() method adds a new item to the database via the boundary object. The forwardToSchedule() method performs a request dispatcher and forwards back to the main page of the application. The returnToInput() method bundles the error list and newly created item into the request collection and forwards back to the input page. Because the errors collection is populated, the errors will appear at the top of the form and the values present in newItem will appear in the form fields. The populateNewItemFromRequest() method takes care of populating a new ScheduleItem object with the values passed in the request parameters. This method performs its work by calling additional helper methods to handle the validation and assignment of the individual fields. The populateText() method is representative of the other helper methods that validate and assign values from request parameters to the new item.

Options in Model 2
The Model 2 schedule application demonstrates the servlet-centric approach to Model 2 applications. Note that we could have used JSPs throughout, replacing the controller servlets with JSP pages. In particular, much of the code that appears in Listing 12 could be replaced with a single line of code to populate the bean from request parameters.

<jsp:setProperty name=scheduleItem property=*/>

However, this contradicts the idea that JSPs should be used only for display and servlets only for code. In general, placing non-UI code in a JSP is a mistake, no matter how convenient it may be. That convenience comes at a price. First, this practice dilutes the consistency of your architecture. If you follow Model 2 to the letter, you can be assured that every JSP is a user interface element, and every servlet executes code with no user interface. Not every servlet is a controller but no servlet contains user interface code.Second, pitfalls exist in some of JSP's automatic behavior. The automatic population of the properties referenced previously can cause problems for fields of the bean that you don't want to be overwritten. For example, the user can pass a parameter on the URL and inadvertently replace a value by automatically calling the mutator method. Like many scripting languages, JSP is powerful but that power breeds danger. Even though the servlet code is more verbose, you shouldn't relinquish control for the sake of expediency. You might prefer to create a utility class that automatically populates the fields from the request parameters. Some of the Web frameworks that we'll look at in the forthcoming articles use this approach.

Disadvantages of Model 2
The advantages in Model 2 have been spelled out in the Model 2 schedule application but there are disadvantages as well. First, more source files are generated. Generally, you have at least 3 files for every unit of work in the Web application. However, these files are usually small and (more importantly) highly cohesive. Each file is responsible for one task and never blurs its responsibility into other facets of the application where it has no business. Many small, single purpose files are better than fewer, highly coupled files.Second, you must be diligent when using Model 2 not to violate the architecture. If you start allowing model code into the view, you end up with the worst of all worlds - more source files, each of which is a tangle of spaghetti-like coupled code. Instead of searching through one poorly designed file, you must search through a set of them. A perfect example of this kind of diligence appears in the entry view of the sample application, and particularly in Listing 11. It would be easy (and would need less code) to place the event types directly into the HTML select tag on the JSP. This embodies the kind of design that must be avoided. When (not if) the event types change, the model changes and propagates through the application. Model 2 requires close attention to architecture and design throughout the project. Frequently, especially for teams who are new to this practice, code reviews should be conducted early, and often enough, to ensure that no undesirable code is slipping into the wrong place.Third, Model 2 appears more complex than ad hoc Web applications. However, once the development team understands the architecture, it makes development (and maintenance) orders of high magnitude easier. Sometimes it is hard to get developers to buy into Model 2. However, they will quickly see the improved maintainability and lead happier lives!

Coming Up Next
Now that you've seen the non-framework version of the application, we'll look into using Struts to build the same application in the second part of this article. We'll cover the architecture and implementation of the Schedule application in Struts. As you will see, using Struts will greatly simplify some of the code from this application and make it easier to write Model 2 applications.

About the Author
Neal is the Chief Technology Officer at The DSW Group, Ltd. He is also the designer and developer of applications, instructional materials, magazine articles, courseware, video presentations, and author of the books Developing with Delphi: Object-Oriented Techniques, from Prentice Hall Ptr (ISBN 0133781186); JBuilder 3 Unleashed, from SAMS (ISBN 0672315483) , and Art of Java Web Development, from Manning Publications (ISBN 1932394060). His primary consulting focus is the building of large scale enterprise applications. He is also an internationally acclaimed speaker, having spoken at numerous developers' conferences worldwide.


back

top

print

recommend