SummaryBy Dario Laverde
In part one of Back to Basics we reviewed the basics of web development with Java using Tomcat as the web server / servlet-JSP engine and Eclipse as the development and debugging environment as well as how to use a database within the Eclipse environment. In this article we will revisit and expand on the previous article, refactoring and implementing a design pattern as well as introducing some basic tenets to proper development: unit testing and automating builds with Ant. (March, 2004)
![]()
o you're ready to move on from
the now
cliché basic login example to a nice web based e-commerce
example
that you can actually use to sell you products or services online.
Well, not so
fast. In this article we will step back a little to redo the login
example the "right" way. Why didn't I do so initially? Well in addition
to covering
too much material in one article, the first article addressed a minimal
approach using Java code to address the relevant basic Java
technologies required and not necessarily the proper design and
development process that now all programmers have to become familiar
with.
What programmers up until now have always left for last (well second to last if they do documentation) is writing tests It's no longer just an eXtreme Programming way of doing things but it's become an essential part to the development process. Like it or not, writing unit tests is now a prerequisite for many projects. A number of books and articles (e.g. see Kent Beck's Test Driven Development) drive this point home and development tools and IDEs have also begun to support it (JUnit testing is available with Eclipse).
Here's what we'll cover:
A Unit Test Example.
Let's develop a Java class that performs a specific business logic
task. For example let's say we need to
perform the following financial calculation:

So
you roll up your sleeves and begin thinking aloud: so what
parameters do I need for this method? Looks like I'll need a for-loop
for this calculation and...
Ok so you've started to design a solution and possibly code it in your
head. But as we design the class in our mind let's actual put the
design down in the form of a unit test. It takes practice but start
thinking about writing tests as part of the design process:
In
the following example we'll create a JUnit test to simply test the
object in question by adding sample input parameters and checking the
calculate method with a predetermined test result.
Example 1. Using JUnit
package org.nycjava;
import junit.framework.TestCase;
public class CashFlowPVDiscreteTest extends TestCase {
public void testFormulaCalculation() {
// class to test
CashFlowPVDiscrete pvd= new CashFlowPVDiscrete();
//set up example parameters
Vector cf_times = new Vector();
cf_times.add(new Double(5));
Vector cf_amounts = new Vector();
cf_times.add(new Double(5));
Double rate = new Double(1);
//test calculation
Double result=pvd.calculate(cf_times,cf_amounts,rate);
// expected result given example parameters
Double expected=new Double(2.5);
// equality test:
// assertEquals(expectedResult, testResult);
// custom check:
assertTrue( (result.doubleValue() > expected.doubleValue()-.05
&&
result.doubleValue() < expected.doubleValue()+.05) ? true
:
false);
}
}
Note:
From Eclipse you can add a new JUnit test by selecting
New->Other:
Now
to run a test
you can either use the command line based method (good for scripting
tests) or use the GUI method. Only after a test fails do you acually
begin coding the target code that you are testing. From the command
line you can launch the test runner as follows:
java
junit.textui.TestRunner CashFlowPVDiscreteTest
The
output will
indicate the execution time and the number of tests that passed or
failed. From Eclipse you can run the test runner from the Run menu as
shown:
As
an exercise,
since now you have a test that fails, complete the method. You'll know
you're done when your test passes. Of course there are additional tests
that you can write to test parameter validation and boundary conditions.
package
org.nycjava;
import java.io.*;
import java.net.*;
import junit.framework.TestCase;
import com.gargoylesoftware.htmlunit.*;
import com.gargoylesoftware.htmlunit.html.*;
public class LoginTest extends TestCase {
public void testCorrectLogin()
{
WebClient webClient = new WebClient();
URL url=null;
try {
url=new URL("http://localhost/login");
HtmlPage loginPage = (HtmlPage)webClient.getPage(url);
HtmlForm form = loginPage.getFormByName("login");
HtmlTextInput userField =
(HtmlTextInput)form.getInputByName("username");
HtmlPasswordInput pwdField =
(HtmlPasswordInput)form.getInputByName("password");
HtmlSubmitInput button = (HtmlSubmitInput)form.getInputByValue("log
in");
userField.setValueAttribute("admin");
pwdField.setValueAttribute("password");
HtmlPage respPage = (HtmlPage)button.click();
assertEquals("Success!",respPage.asText());
} catch(MalformedURLException e)
{
fail(e.getMessage());
} catch(IOException e) {
fail(e.getMessage());
}
}
}
<project
name="login" default="dist" basedir=".">
<description>
login example application
</description>
<property name="src" location="src"/>
<property name="build"
location="WEB-INF/classes"/>
<target name="compile">
<javac srcdir="${src}"
destdir="${build}">
<classpath>
<pathelement
path="${classpath}"/>
<fileset
dir=".">
<include
name="*.jar"/>
</fileset>
<fileset
dir="c:/Tomcat5.0.19/common/lib">
<include name="servlet-api.jar"/>
</fileset>
</classpath>
</javac>
</target>
<target name="dist" depends="compile">
<jar jarfile="login.war">
<fileset
dir=".">
<include name="*.jsp"/>
<include name="WEB-INF/web.xml"/>
<include
name="WEB-INF/**/*.class"/>
<exclude
name="WEB-INF/**/*.java"/>
<exclude
name="WEB-INF/**/*Test*"/>
<include name="META-INF/context.xml"/>
</fileset>
</jar>
</target>
</project>
Note:
the servlet-api.jar
location is hard coded in this example but literals can be placed in
properties
files that the build.xml can include. Environment variables can also be
read so
that the build.xml files can be kept platform independent.
The MVC design pattern
and the Controller Servlet.
The MVC pattern basically is as shown:
+------ View
<-----+
v
v
Model <--------->Controller
The idea is to separate the business logic from the
presentation layer. In
the case of our login servlet, we will have the servlet handle the
logic (e.g.
choosing what JSP page to display) And keep the presentation layer in
the JSP
pages. The model or state (whether logged in or not) is kept in the
database,
In order to keep logic from creeping into the presentation layer (JSP
pages).
Each action (login, logout, register) should be handled in the
controller
servlet. In simple applications (such as our login example). We can
simply put a
switch in the doPost/soGet methods of our servlet to decide (control)
what the
response will be for a given action. With large applications this
servlet (and
switch statement) grows quite large and unmanageable, because you have
to
recompile every time you want to add, remove or change an action. Using
XML would
be the easier way to maintain the mappings betweens actions and
responses.
Struts and JSF (Java
Server Faces)
As you can imagine the controller servlet
can become quite unwieldy
hence the need for web frameworks. In the Java world two web frameworks
are
Jakarta Struts which has become very popular and JSF (Java Server
Faces) which
has recently been released by Sun (JSR-127). Note that both can be used
for non
web based applications and are extensible.
Since they are very similar (with regard to basic MVC architecture. We'll look at the JSF.
The basic architecture is as follows:
JSP
----event-----> Faces ----->
Event
Login <---response--
Servlet <---- Listener
page
|
v
1) creates FacesContext
2) passes control to the Life Cycle processor
The Life Cycle processor then processes the context
as follows
1) reconstructs component tree (web components)
2) apply request values
3) process validations
4) update model values
5) invoke application
6) render response
Events are processed after each phase, handled by event listener(s). These can either forward to other request handlers or continue the lifecycle.
The Controller Servlet is the FacesServlet with the FacesContext serving a similar function to the Servlet context. As with Struts, all configuration is handled by xml files, in this case the faces-config.xml file.
What you need is to specify the FacesServlet in your web.xml
and include the
configuration file, faces-config.xml and required jar files in your
WEB-INF
folder.
JSF brings improvements over other Java web frameworks such as Struts
including:
-automatic markup generation
-declarative integration of user interface with business objects (both
read and write)
-stateful user interface component model
-server side handling of user interface events
For a thorough introduction on JSF see Kito Mann's JSF presentation, presented at a NYPC Users Group meeting, see resources at bottom.
Summary
Hopefully testing will become the first part of
your development process starting today. Also using Ant as a universal
"make" system will allow you to develop not only in any IDE but any
platform as well. And lastly, the importance of design patterns, in
this case MVC and its use in web development frameworks such as Struts
and JSF will serve as a reminder to not re-invent the wheel and make
use of available tools and frameworks.
Recommended Exercises: