http://nycjava.net articles

Back to Basics (part 2)

Using Java Servlets, JSPs and JDBC with Tomcat, Eclipse, Unit Tests and Ant

Summary
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)
By Dario Laverde


So 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:


                                       Figure 1. Present value with discrete compounding  *

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:


Figure 2.

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:


Figure 3.

There is a difference between "error" and "failure". If you don't actually have a class to test (one with the expected method but that does nothing) you will get compiler "errors". Hence you need at least the following for a "failure" result with JUnit.

package org.nycjava;

import java.util.Vector;

public class CashFlowPVDiscrete {
   
    Double calculate(Vector v1, Vector v2, Double rate) {
       
        Double result= new Double(Double.NaN);

        //TODO: write the code only after unit test fails
       
        return result;  
    }

}

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.

Using HtmlUnit to test our Login servlet.

Unit tests that test components like the example above fall under the white box testing category. Black box tests the entire web application as a whole usually from the perspective of an external client, in our case we want to mimic the user interactions in the web browser. For this type of testing we can use one of several available test tools that work within the JUnit frameworks. HttpUnit and HtmlUnit both test web applications (both are sourceforge open source projects). While HttpUnit focuses on the http protocol itself, HtmlUnit focuses on the web client perspective. There are also more advanced projects such as Cactus (an Apache project) which are useful for both Black and White box testing especially with server side EJB testing.

Here is one of the tests for our Login servlet. The other two (incorrect username and incorrect password) are left as an exercise for the reader. As a result of these tests you no longer have to open a browser and test every use case manually.

Note you need to add htmlunit's jar files (from the lib folder) to your project's classpath (see resources for where to download).

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());
        }
    }
}

And now you can also test yourself by deleting the servlet, running these tests and re-coding the servlet (without looking at the first article of course).


Using Ant to build and deploy applications.


Ant is a cross platform "make" system that is ideal to automate builds and deployment. It is an extensible framework based in XML with extensive tag library. 

Here's an example build.xml file for the login project. This compiles the source and creates the war file. To run it simply add Ant's bin folder to your runtime path and run "ant" in the folder where your build.xml resides. You can also pass in arguments to tell it what targets (in the build.xml file) to run (the default is run when there are no arguments passed in)

<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:


Copyright © 2004 Dario Laverde