Using Jakarta Struts to Rewrite the University Admission Application Kristin Kinzler Deal -...

45
Jakarta Struts to Rewrite the University Admission Application Using Jakarta Struts to Using Jakarta Struts to Rewrite the University Rewrite the University Admission Application Admission Application Kristin Kinzler Deal - [email protected] Tim Stevens - [email protected] University of Minnesota
  • date post

    20-Dec-2015
  • Category

    Documents

  • view

    217
  • download

    0

Transcript of Using Jakarta Struts to Rewrite the University Admission Application Kristin Kinzler Deal -...

Using Jakarta Struts to Rewrite the University Admission Application

Using Jakarta Struts to Rewrite the Using Jakarta Struts to Rewrite the University Admission ApplicationUniversity Admission Application

Kristin Kinzler Deal - [email protected] Stevens - [email protected]

University of Minnesota

Using Jakarta Struts to Rewrite the University Admission Application

IntroductionIntroduction

Demo old Application for Admission MVC and Struts The Flow Conversion from EJB to Strut Coding Examples Demo the new Application for Admission Struts Affordances Environments Lessons learned: the good, the bad and the ugly

Using Jakarta Struts to Rewrite the University Admission Application

Requirements : Old vs. NewRequirements : Old vs. New

Using Jakarta Struts to Rewrite the University Admission Application

Old Application for AdmissionOld Application for Admission

EJB/JSP architecture Linear in flow Could save at any point and return Applications were of singleton nature

No data sharing between applications or campuses Took a looong time to reach the end Had to go through all sections (linearly) to submit

the application Could not access the application after it was

submitted

Using Jakarta Struts to Rewrite the University Admission Application

Old RequirementsOld Requirements

Ability to save the application and return to it later PeopleSoft compliance

Front-end validation for PeopleSoft Limited to the PeopleSoft field lengths, customized for each campus

Access to application through each campus’ Admissions website No bookmarking allowed Determined the campus to which the user was applying

Applications were “standalone” Derived flow, invisible to the user

Freshman vs. Transfer; Domestic vs. International Fees determined by campus and domestic vs.

International User able to review information before submission Submit payment online using credit card

Using Jakarta Struts to Rewrite the University Admission Application

Database

DBAccessors

DBAccessors

DBAccessors

EJB

BusinessLogic

Data Gettersand Setters

Old Application ArchitectureOld Application Architecture

Using Jakarta Struts to Rewrite the University Admission Application

New Application for AdmissionNew Application for Admission

MVC (Struts)/JSP architecture Small linear chunks of related questions grouped

into sections Users can jump between sections Application saves automatically at the end of each

section Selective required sections reduces complexity and

application length MyAccount established as umbrella over the whole

admission process Applications no longer singleton

Can copy an application within certain rules Can access the application for printing/viewing

after submission

Using Jakarta Struts to Rewrite the University Admission Application

Users will be able to save their application and return to it later

PeopleSoft compliance Access to application through each

campus website Derived flow, invisible to the user Fees determined by campus and

domestic vs. international User able to review information before

submission Applications were “stand alone” Linear flow within the application Submit payment online using credit card

Old vs. New RequirementsOld vs. New Requirements Users will be able to save their

application and return to it later PeopleSoft compliance Access to application through each

campus website Derived flow, invisible to the user Fees determined by campus and

domestic vs. international User able to review information before

submission Applications were “stand alone” Linear flow within the application Submit payment online using credit card

Ability to copy application Section “chunking” – required vs. optional Submit payment using credit card or e-

check Ability to jump around within the application Ability to access an application, for

review/print, after submission MyAccount used as an entry-point to Apply

for Admission

OLD

REQ

UIR

EMEN

TS

NEW

REQ

UIR

EMEN

TS

Using Jakarta Struts to Rewrite the University Admission Application

MVC and StrutsMVC and Struts

Using Jakarta Struts to Rewrite the University Admission Application

Model-View-Controller (MVC)Model-View-Controller (MVC)

1. User requests a business logic action through a VIEW.

2. The VIEW forwards the request to the CONTROLLER.

3. The CONTROLLER maps the request for action to the appropriate MODEL, then forwards the request onto the appropriate business logic action.

4. The MODEL may request additional actions from the CONTROLLER, OR:

5. The MODEL selects the appropriate VIEW and prepares it.

6. The MODEL presents the prepared VIEW to the user.

CONTROLLER

VIEW

MODEL

Controls the requests forMODELS including model run-

time parameters and whichVIEW to use.

Presents the data preparedby the MODEL to the user.Has no logic other than the

ability to request a newaction from the controller.

Represents the data and thebusiness logic actions that act onthe data. It is also responsible forpreparing and returning the VIEW.

1

2

3

4

56

Using Jakarta Struts to Rewrite the University Admission Application

StrutsStruts

Open-source implementation of MVC Framework provides

Central controller – ActionServlet• All paths go through controller• Configurable through XML

Action classes• Provide business logic

ActionForms• Hold data for each JSP• State functions handled “behind the scenes”

– Handles form lifecycle

Using Jakarta Struts to Rewrite the University Admission Application

Struts – continuedStruts – continued

Framework provides… XML configuration for application flow

• Actions, Forms, Validate=true Tag libraries

• Avoid scriptlets Built In Internationalization Built In Mechanism for Validation

Advantages Architecture and design Reuse, modularity, extensibility etc..

• Allowed for “cookie-cutter” code reuse Provides base architecture (starting point) for

application development Established framework Open source

Using Jakarta Struts to Rewrite the University Admission Application

The Flow :The Flow :User, Architecture and PageUser, Architecture and Page

Using Jakarta Struts to Rewrite the University Admission Application

User FlowUser Flow

AdmissionsOffice

CampusWebsite

CentralAuthentication

Login(Create Guest

Account)

My Account

Request forDetailed

Information

Apply forAdmission

Apply forHousing

CheckApplication

Status(reads fromPeopleSoft)

WebDatabase

Save

Save

SavePre-Student

Account

Browser

HTTPResponse/Request

PeopleSoftSQR Batch

Using Jakarta Struts to Rewrite the University Admission Application

Architecture FlowArchitecture Flow

END Server

Apache

Tomcat

OracleDatabase

PeopleSoftApache

Tomcat

Apache

Tomcat

Box 1

Box 2

Box n

Browser

HTTPResponse/Request

SQR Batch

Using Jakarta Struts to Rewrite the University Admission Application

Database

DBAccessors

DBAccessors

DBAccessors

Database

DBAccessors

DB Pooler

DBAccessors

DBAccessors

Database

DBAccessors

DBAccessors

DBAccessors

EJB

BusinessLogic

Data Gettersand Setters

Data Gettersand Setters

BusinessLogic

EJB EJB MVC MVC

EntityBean

ActionClass

ActionClass

Using Jakarta Struts to Rewrite the University Admission Application

Page FlowPage Flow

Go ToNext Section

BeginSection

Form 1

JSP 1

JSP 1Exit

Action

Form 1(performsvalidation)

ValidationFails

ExitSection

Save

Entity Bean

The flow throughone page

Using Jakarta Struts to Rewrite the University Admission Application

Coding ExamplesCoding Examples

Using Jakarta Struts to Rewrite the University Admission Application

<action path="/enterAppPerInfoSection" scope="session“ type="edu.umn.web.apply.actions.EnterAppPerInfoSectionAction">

<forward name="next" path="/apply/EditAppPerInfoInstr.jsp" redirect="true"/></action><action path="/enterAppPerInfoNames" scope="session" name="appPerInfoName” type="edu.umn.web.apply.actions.EnterAppPerInfoNamesAction" unknown="false" validate="false">

<forward name="next" path="/apply/EditAppPerInfoNames.jsp" redirect="true"/></action><action path="/exitAppPerInfoNames" scope="session" name="appPerInfoNamesForm" type="edu.umn.web.apply.actions.ExitAppPerInfoNamesAction" unknown="false" input="/apply/EditAppPerInfoNames.jsp" validate="true">

<forward name="back" path="/apply/EditAppPerInfoInstr.jsp" redirect="true"/> <forward name="next" path="/apply/do/enterAppPerInfoHomeAddr" redirect="true"/>

<forward name="cancel" path="/myAccount/do/enterAdmissionApplication" redirect="true"/></action><action path="/saveAppPerInfoSection" scope="session"

type="edu.umn.web.apply.actions.SaveAppPerInfoSectionAction"> <forward name="next" path="/apply/do/exitAppPerInfoSection" redirect="true"/></action><action path="/exitAppPerInfoSection" scope="session"

type="edu.umn.web.apply.actions.ExitAppPerInfoSectionAction"> <forward name="next" path="/apply/do/enterAppMajClgSection" redirect="true"/>

</action>

Config file Action Mapping Code SnippetConfig file Action Mapping Code Snippet

Using Jakarta Struts to Rewrite the University Admission Application

EnterAppPerInfoSectionAction CodeEnterAppPerInfoSectionAction Code

package edu.umn.web.apply.actions;

import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;import org.apache.struts.action.ActionForward;import org.apache.struts.action.ActionMapping;

public class EnterAppPerInfoSectionAction extends ApplyAbstractAction {

public ActionForward perform(ActionMapping mapping, ActionForm xform, HttpServletRequest request, HttpServletResponse response) {

setCurrentSection(request, "personalInfo");return performStdSectionEnterFormAction(mapping, xform, request, response) ;

}

}

Using Jakarta Struts to Rewrite the University Admission Application

Name.java Entity Code SnippetName.java Entity Code Snippetprivate INameAccessor getDBA() { return dba ; }private void setDBA(INameAccessor dba) { this.dba = dba ; }

public IApplication getApplication() { return application ; }public void setApplication(IApplication application) { this.application = application ; }

public String getApplId() { try {return getApplication().getApplId() ;

}catch (Exception e) {} ;return null;}

public String getFirstName() { return getDBA().getFirstName() ; }public void setFirstName(String firstName) { getDBA().setFirstName(firstName) ; }public String getMiddleName() { return getDBA().getMiddleName() ; }public void setMiddleName(String middleName) { getDBA().setMiddleName(middleName) ; }public String getLastName() { return getDBA().getLastName() ; }public void setLastName(String lastName) { getDBA().setLastName(lastName) ; }public String getSuffix() { return getDBA().getSuffix() ; }public void setSuffix(String suffix) { getDBA().setSuffix(suffix) ; }

Using Jakarta Struts to Rewrite the University Admission Application

EnterAppPerInfoNamesAction CodeEnterAppPerInfoNamesAction Code

package edu.umn.web.apply.actions;

import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;import org.apache.struts.action.ActionForward;import org.apache.struts.action.ActionMapping;

public class EnterAppPerInfoNamesAction extends ApplyAbstractAction {

public ActionForward perform(ActionMapping mapping, ActionForm xform, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) {

return performStdEnterFormAction(mapping, xform, request, response) ;}

}

Using Jakarta Struts to Rewrite the University Admission Application

AppPerInfoImpl Code SnippetAppPerInfoImpl Code Snippet

public class AppPerInfoNamesImpl extends ApplyAbstractImpl implements AppPerInfoNames {

private String lglFirstName = "" ;

private String lglMiddleName = "" ;

private String lglLastName = "" ;

private String lglSuffix = "" ;

public java.lang.String getLglFirstName() { return lglFirstName ; }

public void setLglFirstName( String lglFirstName ) { this.lglFirstName = lglFirstName ; }

public java.lang.String getLglMiddleName() { return lglMiddleName ; }

public void setLglMiddleName( String lglMiddleName ) { this.lglMiddleName = lglMiddleName ; }

public java.lang.String getLglLastName() { return lglLastName ; }

public void setLglLastName( String lglLastName ) { this.lglLastName = lglLastName ; }

Using Jakarta Struts to Rewrite the University Admission Application

Impl con’t – loadFormInfo SnippetImpl con’t – loadFormInfo Snippetpublic boolean loadFormInfo(IMyAccountSessionInfo myAccountSessionInfo) {

try {

MyAccountInfo myAccountInfoBean = myAccountSessionInfo.getMyAccountInfoBean() ;

setLglFirstName(myAccountInfoBean.getLglFirstName()) ;

setLglMiddleName(myAccountInfoBean.getLglMiddleName()) ;

setLglLastName(myAccountInfoBean.getLglLastName()) ;

setLglSuffix(myAccountInfoBean.getLglSuffix()) ;

AdmissionApplication applicationBean = myAccountSessionInfo.getAdmissionApplicationBean() ;

AdmissionForm admissionForm = applicationBean.getAdmissionForm() ;

setFrmrFirstName(admissionForm.getFormerFirstName()) ;

setFrmrMiddleName(admissionForm.getFormerMiddleName()) ;

setFrmrLastName(admissionForm.getFormerLastName()) ;

setFrmrSuffix(admissionForm.getFormerSuffix()) ;

return true;

}

catch (Exception e) { e.printStackTrace(); }

return false;

}

Using Jakarta Struts to Rewrite the University Admission Application

Impl con’t – saveFormInfo SnippetImpl con’t – saveFormInfo Snippetpublic boolean saveFormInfo(IMyAccountSessionInfo myAccountSessionInfo) {

try {

MyAccountInfo myAccountInfoBean = myAccountSessionInfo.getMyAccountInfoBean() ;

myAccountInfoBean.setLglFirstName(getLglFirstName()) ;

myAccountInfoBean.setLglMiddleName(getLglMiddleName()) ;

myAccountInfoBean.setLglLastName(getLglLastName()) ;

myAccountInfoBean.setLglSuffix(getLglSuffix()) ;

AdmissionApplication applicationBean = myAccountSessionInfo.getAdmissionApplicationBean() ;

AdmissionForm admissionForm = applicationBean.getAdmissionForm() ;

admissionForm.setFormerFirstName(getFrmrFirstName()) ;

admissionForm.setFormerMiddleName(getFrmrMiddleName()) ;

admissionForm.setFormerLastName(getFrmrLastName()) ;

admissionForm.setFormerSuffix(getFrmrSuffix()) ;

return true;

}

catch (Exception e) { e.printStackTrace(); }

return false;

}

Using Jakarta Struts to Rewrite the University Admission Application

AppPerInfoNamesForm Code SnippetAppPerInfoNamesForm Code Snippet

public final class AppPerInfoNamesForm extends ApplyAbstractForm {

private static final String validatorName = "AppPerInfoNamesForm" ;

IAppPerInfoNames appPerInfo = null ;

public ApplyAbstractImpl getFormInfo() { return (ApplyAbstractImpl) appPerInfo ; }

private void setFormInfo(IAppPerInfoNames appPerInfo) { this.appPerInfo = appPerInfo ; }

public void reset(ActionMapping mapping, HttpServletRequest request) {

setSessionInfo(request) ;

IMyAccountSessionInfo masi = getSessionInfo() ;

setFormInfo(new AppPerInfoNamesImpl()) ;

getFormInfo().loadFormInfo(masi) ;

}

Using Jakarta Struts to Rewrite the University Admission Application

Old JSP CodeOld JSP Code

JSP code for one page of the old application PersonalInfo.jsp (saved as .txt for easy viewing)

A lot of logic (java code) Contained many scriplet tags Long files Hard to work with and manage

Using Jakarta Struts to Rewrite the University Admission Application

JSP Code Snippet (New App)JSP Code Snippet (New App)<%@ taglib uri="/WEB-INF/lib/struts-bean.tld" prefix="bean" %><%@ taglib uri="/WEB-INF/lib/struts-html.tld" prefix="html" %><%@ taglib uri="/WEB-INF/lib/struts-logic.tld" prefix="logic" %><%@ taglib uri="/WEB-INF/lib/nested-tags.tld" prefix="nested" %><jsp:useBean id="suffixList" scope="application" class="edu.umn.web.util.dropdown.SuffixDropDown"/><html:html> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><head>

<title><bean:message key="prompt.appl.perInfo"/>-<bean:message key="prompt.appl.perInfo.names"/></title><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"><META HTTP-EQUIV="Pragma" CONTENT="no-cache"><html:base/> <script language="JavaScript" src="../js/browserDetectApp.js"></script><script language="JavaScript" src="../js/popUp.js"></script>

</head><body>

<jsp:include page="/banner.jsp" flush="true"></jsp:include><jsp:include page="/applyTitle.jsp" flush="true"></jsp:include><jsp:include page="/navApply.jsp" flush="true"></jsp:include><jsp:include page="/navBar.jsp" flush="true"></jsp:include>

<html:form action="/exitAppPerInfoNames" focus="submit"> …

Using Jakarta Struts to Rewrite the University Admission Application

JSP Code Snippet con’t.JSP Code Snippet con’t.…<nested:nest property="formInfo">…

<tr> <td nowrap width="1%" class="label"><span class="required">*</span>

<bean:message key="prompt.lglLastName"/> </td><td width="99%" class="text">

<nested:text property="lglLastName" size="30" maxlength="30"/> </td>

</tr><tr>

<td nowrap width="1%" class="label"><bean:message key="prompt.lglSuffix"/>

</td> <td width="99%" class="text">

<nested:select property="lglSuffix" size="1"> <html:option value="">Select Suffix</html:option> <html:options collection="suffixList" property="value" labelProperty="name"/></nested:select>

</td></tr>

… </nested:nest> <jsp:include page="/formButtons.jsp" flush="true"></jsp:include></table>

</html:form> </body>

</html:html>

Using Jakarta Struts to Rewrite the University Admission Application

ExitAppPerInfoNamesAction CodeExitAppPerInfoNamesAction Code

package edu.umn.web.apply.actions;

import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;import org.apache.struts.action.ActionForward;import org.apache.struts.action.ActionMapping;

public class ExitAppPerInfoNamesAction extends ApplyAbstractAction {

public ActionForward perform(ActionMapping mapping, ActionForm xform, HttpServletRequest request, HttpServletResponse response) { return performStdExitFormAction(mapping, xform, request, response) ;

}

}

Using Jakarta Struts to Rewrite the University Admission Application

SaveAppPerInfoSectionAction CodeSaveAppPerInfoSectionAction Code…public class SaveAppPerInfoSectionAction extends ApplyAbstractAction {

public ActionForward perform(ActionMapping mapping, ActionForm xform, HttpServletRequest request, HttpServletResponse response) {

updateAdmissionApplicationEntry(mapping, xform, request, response) ;String dispatchTo = "next" ;IMyAccountSessionInfo masi = getSessionInfo(request) ;masi.setAppStatus("T");MyAccountInfo myAccountInfoBean = masi.getMyAccountInfoBean() ;AdmissionApplication applicationBean = masi.getAdmissionApplicationBean() ;MyAccountApplication myAccountApplicationBean = masi.getMyAccountApplicationBean() ;try {

myAccountInfoBean.commitMyAccountInfo() ;applicationBean.commitApplication() ;myAccountApplicationBean.commitMyAccountApplication() ;

} catch (Exception ex) {ex.printStackTrace() ;dispatchTo = "failCommit" ;

}return mapping.findForward( dispatchTo ) ;

}}

Using Jakarta Struts to Rewrite the University Admission Application

ExitAppPerInfoSectionAction CodeExitAppPerInfoSectionAction Codepackage edu.umn.web.apply.actions;

import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.ActionForm;import org.apache.struts.action.ActionForward;import org.apache.struts.action.ActionMapping;

import edu.umn.web.apply.ientities.*;

import edu.umn.web.myAccount.iform.* ;import edu.umn.web.myAccount.ientities.* ;

public class ExitAppPerInfoSectionAction extends ApplyAbstractAction {

public ActionForward perform(ActionMapping mapping, ActionForm xform, HttpServletRequest request, HttpServletResponse response) {

setSectionStatus(request, "personalInfo", "C"); return performStdSectionExitFormAction(mapping, xform, request, response) ;

}}

Using Jakarta Struts to Rewrite the University Admission Application

Struts Affordances, Environments Struts Affordances, Environments and Lessons Learnedand Lessons Learned

Using Jakarta Struts to Rewrite the University Admission Application

Code ReuseCode Reuse

The data and business logic components were split from the old EJBs Struts Actions house the business logic Struts ActionForms house the data components

Data accessors and the business logic remained (for the most part) unchanged to minimize the need to retest all business functionality

Majority of the Business Logic was removed from the JSPs and moved to the Action Classes

Validation was removed from the JSPs and moved to the Forms

Using Jakarta Struts to Rewrite the University Admission Application

How We Met Our DeadlineHow We Met Our Deadline

Code reuse Minimized testing requirements Facilitated minimal changes to the PeopleSoft data

load Allowed developers to concentrate on the new

application features Struts

Provided the means to meet our customers needs• Ability to jump around• Auto-save• Required vs. Optional sections

Provided instant framework for application architecture

Provided easy division of server side development labor

Using Jakarta Struts to Rewrite the University Admission Application

Development and TestingDevelopment and Testing

Three developed in Linux environment One developed in Win2K CVS for source control Testing tools

JProbe Mercury LoadRunner

Each developer had a development silo Integration silo Unit testing – daily Test silo

Thirteen rounds of functional testing Six rounds of “monkey tests” before we purchased our

testing tool Fifteen rounds of load balance testing using

LoadRunner Production

Using Jakarta Struts to Rewrite the University Admission Application

Lessons Learned (the Good)Lessons Learned (the Good)

Modularity allowed for quick conversion Nested tags allowed for easier view (JSP)

development Properties file

Allowed for reuse of common terminology, easy text changes, and, (if wanted), easier conversion for internationalization

Once the basic actions were complete – development became “cookie cutter”

Learning curve for experienced developers is acceptable Four months from development to implementation

Using Jakarta Struts to Rewrite the University Admission Application

Lessons Learned (the Bad)Lessons Learned (the Bad) Config file length became difficult to manage Any method in a servlet cannot be larger than 64K when

compiled Problem: Our JSP include files contained navigation logic that

exceeded this size restriction Solution: Use <jsp:include> vs. include directive

• <jsp:include> Includes a static file or sends a request to a dynamic file.

• Ex. <jsp:include page="/banner.jsp" flush="true"></jsp:include>• Include directive includes a file of text or code in a JSP file at

translation time, when the JSP file is compiled. • Ex. <%@ include file="checkSession.jsp" %>

Problem: Large included JSPs hide runtime errors Solution: Comment out the included files to get to the error

messages Servlet filters caused performance problems

Extended ActionServlet to manage authentication and session state

Unit Testing was not feasible until all three pieces were in place

Using Jakarta Struts to Rewrite the University Admission Application

Lessons Learned (the Ugly)Lessons Learned (the Ugly)

Testing, testing, testing There is no such thing as too much testing It is crucial to develop and test in an

environment that is EXACTLY the same as the production environment

Performance, performance, performance

Using Jakarta Struts to Rewrite the University Admission Application

Production EnvironmentProduction Environment

Tomcat 4.0.3 with Apache Web server to handle HTTP

Virtual Memory set to 256MB/512MB Dual Processor Intel Machines with RedHat Linux

7.3 E-Network Dispatcher, (END), provides load

balancing 70 Concurrent (Virtual) User “Sweet Spot” per

Tomcat instance Sustained three hour load test showed consistent

under five second response time with most transactions under one second response time

Using Jakarta Struts to Rewrite the University Admission Application

Testing and StatsTesting and Stats

Using Jakarta Struts to Rewrite the University Admission Application

Mercury LoadRunner TestMercury LoadRunner Test

• Three Hour, 70 User Load Test• Sub-second Average Transaction Response

Time Under Load• Average 35 Hits Per Second• Handled an average of 150,000 Bytes Per

Second• 117,287 Completed Transactions

Using Jakarta Struts to Rewrite the University Admission Application

Mercury LoadRunner Test ResultMercury LoadRunner Test Result

Using Jakarta Struts to Rewrite the University Admission Application

Stats as of 12/4/02 *Stats as of 12/4/02 *

8896 Accounts Created 8768 Applications

5258 Applications In Progress 3510 Applications Submitted

Applicants who have multiple in progress applications 177 have two applications 7 have three applications

Applicants who have multiple completed applications 114 have two applications 5 have three applications

* Stats do not account for multiple accounts, abandoned accounts, or duplicates.

Using Jakarta Struts to Rewrite the University Admission Application

For More Info on StrutsFor More Info on Struts

Jakarta Struts Official Site http://jakarta.apache.org/struts/

Nested Tag Lib Resource http://www.keyboardmonkey.com/next/index.JSP

JavaServer Pages (JSP) http://java.sun.com/products/JSP/ JSP v1.2  Syntax Reference

• http://java.sun.com/products/JSP/tags/12/syntaxref12.html

Ted Husted – Struts guru http://husted.com/struts/ http://husted.com/about/scaffolding/strutByStrut.htm

Message board -- very helpful! http://www.mail-archive.com/struts-user%40jakarta.apache.o

rg/