Struts

Apache Struts link

From Apache site:

Apache Struts

Apache Struts is a free open-source framework for creating Java web applications.

Web applications differ from conventional websites in that web applications can create a dynamic response. Many websites deliver only static pages. A web application can interact with databases and business logic engines to customize a response.

Web applications based on JavaServer Pages sometimes commingle database code, page design code, and control flow code. In practice, we find that unless these concerns are separated, larger applications become difficult to maintain.

One way to separate concerns in a software application is to use a Model-View-Controller (MVC) architecture. The Model represents the business or database code, the View represents the page design code, and the Controller represents the navigational code. The Struts framework is designed to help developers create web applications that utilize a MVC architecture.

The framework provides three key components:

  • A "request" handler provided by the application developer that is mapped to a standard URI.
  • A "response" handler that transfers control to another resource which completes the response.
  • A tag library that helps developers create interactive form-based applications with server pages.

The framework's architecture and tags are buzzword compliant. Struts works well with conventional REST applications and with nouveau technologies like SOAP and AJAX.

The Apache Struts Project

The Apache Struts Project is the open source community that creates and maintains the Apache Struts framework. The project consists of a diverse group of volunteers who share common values regarding collaborative, community-based open source development. The Apache Struts Project is proud to share these values with our parent organization: The Apache Software Foundation. The project is called "Struts" because the framework is meant to furnish the "invisible underpinnings" that support professional application development. Struts provides the glue that joins the various elements of the standard Java platform into a coherent whole. Our goal is to leverage existing standards by producing the missing pieces we need to create enterprise-grade applications that are easy to maintain over time.

The Apache Struts Project offers two major versions of the Struts framework. Struts 1 is recognized as the most popular web application framework for Java. The 1.x framework is mature, well-documented, and widely supported. Struts 1 is the best choice for teams who value proven solutions to common problems.

Struts 2 was originally known as WebWork 2. After working independently for several years, the WebWork and Struts communities joined forces to create Struts 2. The 2.x framework is the best choice for teams who value elegant solutions to difficult problems.

Struts for Newbies

If you are new to development with Struts, here are some pointers on how to get started.

The framework "stands on the shoulders of giants". To use Struts well, it's important to have a good grasp of the fundamentals. Start by reviewing the Key Technologies primer, and studying any unfamiliar topics.

Next, branch to either the Struts 2 or Struts 1 documentation

The Struts 2 documentation is maintained as a wiki, but don't let that stop you from scrolling through. We have organized the wiki so that it can be read like a book, cover to cover. Just keep following the Next links.

[Starting Struts 2]Better yet, turn first to the Starting with Struts2 book. At 122 pages, it's a small book that doesn't try to replicate the online Struts 2 Documentation. Instead, the book is a perfect complement to the Struts 2 website. The "minibook" is available both as a free PDF and in conventional printed form.

For Struts 1, the most up-to-date book would be Struts: The Complete Reference. For newbies, Struts for Dummies is another good choice.

And don't hesitate to get involved. The best way to help with any open source project is to improve the documentation! There are mountains of Struts know-how posted to the user mailing list that could be sholved into the documentation. (How do you think this section started?)

My notes: If you download struts you’ll have a struts-blank.war file in the distribution.  Copy and rename it to something else and put it in its own directory.  You can unzip it to see the various files in the web app.  These include source files.  If you drop this war or directory into your Tomcat/webapps it will deploy.  There’s not a lot there but it looks like this (selecting Spanish displays the same in Spanish):

Much of the Coreservlets example (shown later) is already here. Here is the Welcome page:

Clicking sign in gives:

 

 

 

CoreServlets Struts intro: http://courses.coreservlets.com/Course-Materials/struts.html#Struts-Intro

Note: the struts-actions lib directory needs apache commons jar files (beanutils.commons and digester.ruleset)

Flow of control:

The user requests a form

– For now, we use normal HTML to build the form

• Later we will use the Struts html:form tag

• The form is submitted to a URL of the form blah.do.

– That address is mapped by struts-config.xml to an Action class

• The execute method of the Action object is invoked

– One of the arguments to execute is a form bean that is automatically

created and whose properties are automatically populated with the

incoming form data

– The Action object then invokes business logic and data-access logic,

placing the results in normal beans stored in request, session, or application

scope.

– The Action uses mapping.findForward to return a condition, and the

conditions are mapped by struts-config.xml to various JSP pages.

• Struts forwards request to the appropriate JSP page

– The page can use bean:write or the JSP 2.0 EL to output bean properties

–        The page can use bean:message to output fixed strings

The Six Basic Steps in Using

Struts

1. Modify struts-config.xml.

Use WEB-INF/struts-config.xml to:

– Map incoming .do addresses to Action classes

– Map return conditions to JSP pages

– Declare any form beans that are being used.

– Be sure to restart the server after modifyingstruts-config.xml; the file is read only when the Web

application is first loaded.

2. Define a form bean.

– This bean is a class the extends ActionForm and will represent the data submitted by the user. It is

automatically populated when the input form issubmitted. Beans are postponed until the next section.

3. Create results beans.

– In the MVC architecture, the business-logic and data-access code create the results and the JSP pages present them. To transfer the

results from one layer to the other, they are stored in beans. These beans differ from form beans in that they need extend no particular

class, and they represent the output of the computational process, not the input to the process. Beans will be discussed in the next

section.

4. Define an Action class to handle requests.

– The struts-config.xml file designates the Action classes that handle

requests for various URLs. The Action objects themselves need to

do the real work: invoke the appropriate business- and data-accesslogic,

store the results in beans, and designate the type of situation

(missing data, database error, success category 1, success category

2, etc.) that is appropriate for the results. The struts-config.xml file

then decides which JSP page should apply to that situation.

 

5. Create form that invokes blah.do.

– Create an input form whose ACTION corresponds to one of the .do

addresses listed in struts-config.xml.

– In a later lecture, we will discuss the advantages of using the Struts

html:form tag to build this input form.

6. Display results in JSP.

– Since Struts is built around MVC, these JSP pages should avoid

JSP scripting elements whenever possible. For basic Struts, these

pages usually use the bean:write tag, but in JSP 2.0 the JSP 2.0

expression language is a viable alternative.

– In most cases, the JSP pages only make sense when the request is

funneled through the Action, so the pages go in WEB-INF.

– If the JSP pages makes sense independently of the Action (e.g., if

they display session data), then the JSP pages should be placed in a

regular subdirectory of the Web application, and the forward

entries in struts-config.xml should say

<forward ... redirect="true"/>.

Example 1: One Result Mapping

• URL

– http://hostname/struts-actions/register1.do

• Action Class

– RegisterAction1

• RegisterAction1 extends Action and is in the coreservlets

package.

• The execute method of RegisterAction1 always returns

"success"

• Results page

– /WEB-INF/results/confirm.jsp

• But the URL shown will still be register1.do

Step 1A (Modify struts-config.xml)

 

• Map incoming .do addresses to Action classes

– In this case, we designate that RegisterAction1 should

handle requests for register1.do. To accomplish this, we

add an action entry to action-mappings, where action has

the following attributes.

• path: the relative path that should be mapped to the Action,

minus the .do extension. Thus, path="/register1" refers to

http://hostname/webAppName/register1.do.

• type: the fully qualified class name of the Action class that

should be invoked when a request for the path is received.

<action-mappings>

<!-- .do implied automatically -->

<action path="/register1"

type="coreservlets.RegisterAction1">

...

</action>

</action-mappings>

Step 1B (Modify struts-config.xml)

• Map return conditions to JSP pages

– In this case, we use the forward element to say that

confirm.jsp applies when the execute method of

RegisterAction1 returns "success", as follows:

<forward name="success"

path="/WEB-INF/results/confirm.jsp"/>

– Note: if the same forward is used by multiple actions, you

can put the forward declaration in a global-forwards

section (before action-mappings) instead of in the action.

<global-forwards>

<forward name="success"

path="/WEB-INF/results/confirm.jsp"/>

</global-forwards>

Step 1 (Modify struts-config.xml) –

Final struts-config.xml

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC ... >

<struts-config>

<action-mappings>

<action path="/register1"

type="coreservlets.RegisterAction1">

<forward name="success"

path="/WEB-INF/results/confirm.jsp"/>

</action>

</action-mappings>

</struts-config>

Steps 2 and 3

• Define a form bean.

– Beans are postponed until the next section, so this step is

omitted for now.

• Create results beans.

– Beans are postponed until the next section, so this step is omitted for now.

Step 4 (Define an Action Class to

Handle Requests)

• Action subclasses should… be in a package.

– In this case, we have

package coreservlets;

• This means that the class file should go in your_web_app/WEBINF/

classes/coreservlets/.

• Action subclasses should… add Struts-specific

import statements to whatever imports are

otherwise needed.

– In this case, we have

import javax.servlet.http.*;

import org.apache.struts.action.*;

Step 4 (Define an Action Class to

Handle Requests)

• Action subclasses should ... extend Action

• Action subclasses should ... override execute

– In this case, we have

• Action subclasses should ... return

mapping.findForward.

– The execute method should have one or more return

values.

– These values will then be mapped to specific JSP pages

by forward entries in struts-config.xml. In this case, we

simply return "success" in all situations.

 

package coreservlets;

imports…

public ActionForward

execute(ActionMapping mapping,

ActionForm form,

HttpServletRequest request,

HttpServletResponse response)

throws Exception {

return(mapping.findForward("success"));}

Step 5 (Create form that invokes

blah.do)

• We need an HTML form that invokes

http://hostname/struts-actions/register1.do.

<html>

<form action=”register1.do” method =”post”>

….

Step 6 (Display results in JSP)

• In general, there can be several possible JSP

pages

– Corresponding to the various possible return values of the

execute method of the Action.

• In struts-config.xml, each JSP page is declared

in a forward entry within the appropriate action.

– In this simple case, the only return value is "success", so

/WEB-INF/results/confirm.jsp is used in all cases.

• This JSP page will just display a simple message

<html>

…

You have successfully register…

Step 1 (Modify struts-config.xml) –

Final Code

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC ... >

<struts-config>

<action-mappings>

...

<action path="/register2"

type="coreservlets.RegisterAction2">

<forward name="bad-address"

path="/WEB-INF/results/bad-address.jsp"/>

<forward name="bad-password"

path="/WEB-INF/results/bad-password.jsp"/>

<forward name="success"

path="/WEB-INF/results/confirm.jsp"/>

</action>

</action-mappings>

</struts-config>

Steps 2 and 3

• Define a form bean.

– Beans are postponed until the next section, so this step is

omitted for now.

• Create results beans.

– Beans are postponed until the next section, so this step is

omitted for now.

Step 4 (Define an Action Class to

Handle Requests)

• Similar to the previous example except for

multiple mapping.findForward entries

– We return "bad-address" if the email address is missing,

is less then three characters long, or does not contain an

"@" sign.

– We return "bad-password" if the password is missing or

is less than six characters long.

– Otherwise we return "success".

• In this simple example we use

request.getParameter explicitly.

– In later examples we let Struts automatically populate a

bean from the request data.

4 (Define an Action Class to

Handle Requests) – Final Code

public class RegisterAction2 extends Action {

public ActionForward

execute(ActionMapping mapping,

ActionForm form,

HttpServletRequest request,

HttpServletResponse response)

throws Exception {

String email = request.getParameter("email");

String password = request.getParameter("password");

if ((email == null) ||

(email.trim().length() < 3) ||

(email.indexOf("@") == -1)) {

return(mapping.findForward("bad-address"));

} else if ((password == null) ||

(password.trim().length() < 6)) {

return(mapping.findForward("bad-password"));

} else {

return(mapping.findForward("success"));

}}}

 

Step 5 (Create Form that Invokes

blah.do)

• We need an HTML form that invokes

http://hostname/struts-actions/register2.do.

<!DOCTYPE ...>

<HTML>

<HEAD><TITLE>New Account Registration</TITLE></HEAD>

<BODY BGCOLOR="#FDF5E6">

<CENTER>

<H1>New Account Registration</H1>

<FORM ACTION="register2.do" METHOD="POST">

EMAIL ADDRESS <INPUT TYPE="TEXT" name=”Email”></br>

PASSWORD <input type="PASSWORD" name=”password”></br>

<INPUT TYPE="SUBMIT" VALUE="Sign Me Up!">

</FORM>

</CENTER>

</BODY></HTML>

 

Step 6 (Display results in JSP)

First Possible Page

<!DOCTYPE ...>

<HTML>

<HEAD><TITLE>Illegal Email Address</TITLE></HEAD>

<BODY BGCOLOR="#FDF5E6">

<CENTER>

<H1>Illegal Email Address</H1>

Address must be of the form username@host.

Please <A HREF="register2.jsp">

try again</A>.

</CENTER>

</BODY></HTML>

 

Step 6 (Display results in JSP)

Second Possible Page

<!DOCTYPE ...>

<HTML>

<HEAD><TITLE>Illegal Password</TITLE></HEAD>

<BODY BGCOLOR="#FDF5E6">

<CENTER>

<H1>Illegal Password</H1>

Password must contain at least six characters.

Please <A HREF="register2.jsp">

try again</A>.

</CENTER>

</BODY></HTML>

Step 6 (Display results in JSP)

Same confirm.jsp Shown Earlier

<!DOCTYPE ...>

<HTML>

<HEAD><TITLE>Success</TITLE></HEAD>

<BODY BGCOLOR="#FDF5E6">

<CENTER>

<H1>You have registered successfully.</H1>

Congratulations

</CENTER>

</BODY></HTML>

Combining Shared Condition

(Forward) Mappings

• Idea

– If the same condition is mapped to the same JSP page in

multiple actions, you can move the forward to a globalforwards

section to avoid repetition

• Syntax

– The global-forwards section goes before

action-mappings, not within it

– The forward entries within global-forwards have the same

syntax and behavior as forward entries within action

• Example

<global-forwards>

<forward name="success"

path="/WEB-INF/results/confirm.jsp"/>

</global-forwards>

 

CoreServle Combining Shared Condition

(Forward) Mappings: Old

<action-mappings>

<action path="/register1"

type="coreservlets.RegisterAction1">

<forward name="success"

path="/WEB-INF/results/confirm.jsp"/>

</action>

<action path="/register2"

type="coreservlets.RegisterAction2">

<forward name="bad-address"

path="/WEB-INF/results/bad-address.jsp"/>

<forward name="bad-password"

path="/WEB-INF/results/bad-password.jsp"/>

<forward name="success"

path="/WEB-INF/results/confirm.jsp"/>

</action>

...

</action-mappings>

Combining Shared Condition

(Forward) Mappings: New

<global-forwards>

<forward name="success"

path="/WEB-INF/results/confirm.jsp"/>

</global-forwards>

<action-mappings>

<action path="/register1"

type="coreservlets.RegisterAction1">

</action>

<action path="/register2"

type="coreservlets.RegisterAction2">

<forward name="bad-address"

path="/WEB-INF/results/bad-address.jsp"/>

<forward name="bad-password"

path="/WEB-INF/results/bad-password.jsp"/>

</action>

...

</action-mappings>

Summary

• Modify struts-config.xml

– Map blah.do addresses to subclasses of Action

– Map return conditions (from execute) to JSP pages

– Declare any form beans that are being used.

• Define a form bean

• Create results beans

• Define an Action class to handle requests

– Extend Action

– Override execute

– Return mapping.findForward

• Create form that invokes blah.do

• Display results in JSP

coreservlets pdf: http://courses.coreservlets.com/Course-Materials/pdf/struts/02-Struts-Actions.pdf

This tutorial includes/builds the following webapp

Here is struts-blank:

Some of the files/pages from struts-blank

Index.jsp

<%@ taglib uri="/tags/struts-logic" prefix="logic" %>

<logic:redirect forward="welcome"/>

<%--Redirect default requests to Welcome global ActionForward.

by using a redirect, the user-agent will change address to match the path of our Welcome ActionForward.

--%>

Pages/Welcome.jsp

<%@ taglib uri="/tags/struts-bean" prefix="bean" %>

<%@ taglib uri="/tags/struts-html" prefix="html" %>

<%@ taglib uri="/tags/struts-logic" prefix="logic" %>

<html:html locale="true">

<head>

<title><bean:message key="welcome.title"/></title>

<html:base/>

</head>

<body bgcolor="white">

<logic:notPresent name="org.apache.struts.action.MESSAGE" scope="application">

  <font color="red">

    ERROR:  Application resources not loaded -- check servlet container

    logs for error messages.

  </font>

</logic:notPresent>

<h3><bean:message key="welcome.heading"/></h3>

<p><bean:message key="welcome.message"/></p>

</body>

</html:html>

Struts-config.xml

<?xml version="1.0" encoding="ISO-8859-1" ?>

 

<!DOCTYPE struts-config PUBLIC

          "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"

          "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">

 

<!--

     This is a blank Struts configuration file with an example

     welcome action/page and other commented sample elements.

     Tiles and the Struts Validator are configured using the factory defaults

     and are ready-to-use.

     NOTE: If you have a generator tool to create the corresponding Java classes

     for you, you could include the details in the "form-bean" declarations.

     Otherwise, you would only define the "form-bean" element itself, with the

     corresponding "name" and "type" attributes, as shown here.

-->

<struts-config>

<!-- ============================================ Data Source Configuration -->

<!--

<data-sources>

<data-source type="org.apache.commons.dbcp.BasicDataSource">

    <set-property

      property="driverClassName"

      value="org.postgresql.Driver" />

    <set-property

      property="url"

      value="jdbc:postgresql://localhost/mydatabase" />

    <set-property

      property="username"

      value="me" />

    <set-property

      property="password"

      value="test" />

    <set-property

      property="maxActive"

      value="10" />

    <set-property

      property="maxWait"

      value="5000" />

    <set-property

      property="defaultAutoCommit"

      value="false" />

    <set-property

      property="defaultReadOnly"

      value="false" />

    <set-property

      property="validationQuery"

      value="SELECT COUNT(*) FROM market" />

</data-source>

</data-sources>

-->

<!-- ================================================ Form Bean Definitions -->

    <form-beans>

    <!-- sample form bean descriptor for an ActionForm

        <form-bean

            name="inputForm"

            type="app.InputForm"/>

    end sample -->

 

    <!-- sample form bean descriptor for a DynaActionForm

        <form-bean

            name="logonForm"

            type="org.apache.struts.action.DynaActionForm">

            <form-property

                name="username"

                type="java.lang.String"/>

            <form-property

                name="password"

                type="java.lang.String"/>

       </form-bean>

    end sample -->

    </form-beans>

<!-- ========================================= Global Exception Definitions -->

    <global-exceptions>

        <!-- sample exception handler

        <exception

            key="expired.password"

            type="app.ExpiredPasswordException"

            path="/changePassword.jsp"/>

        end sample -->

    </global-exceptions>

<!-- =========================================== Global Forward Definitions -->

    <global-forwards>

        <!-- Default forward to "Welcome" action -->

        <!-- Demonstrates using index.jsp to forward -->

        <forward

            name="welcome"

            path="/Welcome.do"/>

    </global-forwards>

 

<!-- =========================================== Action Mapping Definitions -->

 

    <action-mappings>

            <!-- Default "Welcome" action -->

            <!-- Forwards to Welcome.jsp -->

        <action

            path="/Welcome"

            forward="/pages/Welcome.jsp"/>

    <!-- sample input and input submit actions

        <action

            path="/Input"

            type="org.apache.struts.actions.ForwardAction"

            parameter="/pages/Input.jsp"/>

        <action     path="/InputSubmit"              type="app.InputAction"              name="inputForm"

            scope="request"

            validate="true"

            input="/pages/Input.jsp"/>

 

            <action

                path="/edit*"

                type="app.Edit{1}Action"

                name="inputForm"

                scope="request"

                validate="true"

                input="/pages/Edit{1}.jsp"/>

    end samples -->

    </action-mappings>

<!-- ============================================= Controller Configuration -->

    <controller

       processorClass="org.apache.struts.tiles.TilesRequestProcessor"/>

 

<!-- ======================================== Message Resources Definitions -->

    <message-resources parameter="MessageResources" />

<!-- =============================================== Plug Ins Configuration -->

  <!-- ======================================================= Tiles plugin -->

  <!--

     This plugin initialize Tiles definition factory. This later can takes some

             parameters explained here after. The plugin first read parameters from

             web.xml, thenoverload them with parameters defined here. All parameters

             are optional.

     The plugin should be declared in each struts-config file.

       - definitions-config: (optional)

            Specify configuration file names. There can be several comma

                            separated file names (default: ?? )

       - moduleAware: (optional - struts1.1)

            Specify if the Tiles definition factory is module aware. If true

            (default), there will be one factory for each Struts module.

                                    If false, there will be one common factory for all module. In this

            later case, it is still needed to declare one plugin per module.

            The factory will be initialized with parameters found in the first

            initialized plugin (generally the one associated with the default

            module).

                                      true : One factory per module. (default)

                                      false : one single shared factory for all modules

               - definitions-parser-validate: (optional)

                    Specify if xml parser should validate the Tiles configuration file.

                                      true : validate. DTD should be specified in file header (default)

                                      false : no validation

 

              Paths found in Tiles definitions are relative to the main context.  -->

    <plug-in className="org.apache.struts.tiles.TilesPlugin" >

      <!-- Path to XML definition file -->

      <set-property property="definitions-config"

                       value="/WEB-INF/tiles-defs.xml" />

      <!-- Set Module-awareness to true -->

      <set-property property="moduleAware" value="true" />

    </plug-in>

  <!-- =================================================== Validator plugin -->

  <plug-in className="org.apache.struts.validator.ValidatorPlugIn">

    <set-property

        property="pathnames"

        value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>

  </plug-in>

</struts-config>

messageresources.properties file in struts-blank\WEB-INF\classes\resources

# -- standard errors --

errors.header=<UL>

errors.prefix=<LI>

errors.suffix=</LI>

errors.footer=</UL>

# -- validator --

errors.invalid={0} is invalid.

errors.maxlength={0} can not be greater than {1} characters.

errors.minlength={0} can not be less than {1} characters.

errors.range={0} is not in the range {1} through {2}.

errors.required={0} is required.

errors.byte={0} must be an byte.

errors.date={0} is not a date.

errors.double={0} must be an double.

errors.float={0} must be an float.

errors.integer={0} must be an integer.

errors.long={0} must be an long.

errors.short={0} must be an short.

errors.creditcard={0} is not a valid credit card number.

errors.email={0} is an invalid e-mail address.

# -- other --

errors.cancel=Operation cancelled.

errors.detail={0}

errors.general=The process did not complete. Details should follow.

errors.token=Request could not be completed. Operation is not in sequence.

# -- welcome --

welcome.title=Struts Blank Application

welcome.heading=Welcome!

welcome.message=To get started on your own application, copy the struts-blank.war to a new WAR file using the name for your application. Place it in your container's "webapp" folder (or equivalent), and let your container auto-deploy the application. Edit the skeleton configuration files as needed, restart your container, and you are on your way! (You can find the application.properties file with this message in the /WEB-INF/src/java/resources folder.)

From a RoseIndia Struts tutorial

What is Struts?

Struts Frame work is the implementation of Model-View-Controller (MVC) design pattern for the JSP. Struts is maintained as a part of Apache Jakarta project and is open source. Struts Framework is suited for the application of any size. Latest version of struts can be downloaded from http://jakarta.apache.org/. We are using jakarta-struts-1.1 and jakarta-tomcat-5.0.4 for this tutorial.

What is Model-View-Controller (MVC) Architecture?

Model-View-Controller architecture is all about dividing application components into three different categories Model, View and the Controller. Components of the MVC architecture has unique responsibility and each component is independent of the other component. Changes in one component will have no or less impact on other component. Responsibilities of the components are:

Model: Model is responsible for providing the data from the database and saving the data into the data store. All the business logic is implemented in the Model. Data entered by the user through View are check in the model before saving into the database. Data access, Data validation and the data saving logic are part of Model.  It is possible to reuse the same model for many page requests. Struts provides the ActionForm and the Action classes which can be extended to create the model objects.

View: View represents the user view of the application and is responsible for taking the input from the user, dispatching the request to the controller and then receiving response from the controller and displaying the result to the user. HTML, JSPs, Custom Tag Libraries and Resources files are the part of view component.  The view in the struts framework is mainly a jsp page which is responsible for producing the output to the user.

Controller: Controller is intermediary between Model and View. Controller is responsible for receiving the request from client. Once request is received from client it executes the appropriate business logic from the Model and then produce the output to the user using the View component. ActionServlet, Action, ActionForm and struts-config.xml are the part of Controller.  Most Struts application will have only one controller that is ActionServlet which is responsible for directing several Actions. The controller determines what action is required and sends the information to be processed by an action Bean. The key advantage of having a controller is its ability to control the flow of logic through the highly controlled, centralized points. 

Each application we develop has a deployment descriptor i.e. WEB-INF/web.xml.

This file has all the configuration information which we have defined for our web application. The configuration information includes the index file, the default welcome page, the mapping of our servlets including path and the extension name, any init parameters, information related to the context elements. 
In the file WEB-INF/web.xml of struts application we need to configure the Struts ActionServlet which handles all the request made by the web browsers to a given mapping.  ActionServlet is the central component of the Struts controller. This servlet extends the HttpServlet. This servlet basically performs two important things. First is : When the container gets start, it reads the Struts Configuration files and loads it into memory in the init() method. You will know more about the Struts Configuration files below. Second point is: It intercepts the HTTP request in the doGet() and doPost() method and handles it appropriately.
  

  1. In struts application we have another xml file which is a Struts configuration file named as struts.config.xml. The name of this file can be changed. The name of the struts configuration file can be configured in the web.xml file. This file is placed under the WEB-INF directory of the web application.  It is an XML document that describes all or part of Struts application. This file has all the information about many types of Struts resources and configures their interaction.  This file is used to associate paths with the controller components of your application., known as Action classes like <action path ="/login" type = "LoginAction">.  This tag tells the Struts ActionServlet that whenever the incoming request is http://myhost/myapp/login.do, then it must invoke the controller component LoginAction. Above, you can see that we have written .do in the URL. This mapping is done to tell the web application that whenever a request is received with the .do extension then it should be appended to the URL.  
      
  2. For each action we also have to configure Struts with the names of the resulting pages that will be shown as a result of that action. In our application there can be more than one view which depends on the result of an action. One can be for a success and the other for the failure. If the result action is "success" then the action tells the ActionServlet that the action has been successfully accomplished or vice- versa.  The struts knows how  to forward the specific page to the concerned destination. The model which we want to use is entirely to you, the model is called from within the controller components. 
      
  3. Action can also get associate with a JavaBean in our Struts configuration file. Java bean is nothing but a class having getter and setter methods that can be used to communicate between the view and the controller layer. These java beans are validated by invoking the validate() method on the ActionForm by the help of the Struts system. The client sends the request by the normal form submission by using Get or Post method, and the Struts system updates that data in the Bean before calling the controller components.
      
  4. The view we use in the struts can be either Jsp page, Velocity templates, XSLT pages etc. In struts there are set of JSP tags which has been bundled with the struts distribution, but it is not mandatory to use only Jsp tags, even plain HTML files can be used within our Struts application but the disadvantage of using the html is that it can't take the full advantage of all the dynamic features provided in the struts framework. 
    The framework includes a set of custom tag libraries that facilitate in creating the user interfaces that can interact gracefully with ActionForm beans. The struts Jsp taglibs has a number of generic and struts specific tags tags which helps you to use dynamic data in your view. These tags helps us to interact with your controller without writing much java code inside your jsp. These tags are used create forms, internally forward to other pages by interacting with the bean and helps us to invoke other actions of the web application. 
    There are many tags provided to you in the struts frameworks which helps you in sending error messages, internationalization etc. 
      

Note: The points we have described above will be in effect if and only if when the ActionServlet is handling the request. When the request is submitted to the container which call the ActionServlet, make sure that the extension of the file which we want to access should have the extension .do.

Struts working:

http://www.roseindia.net/struts/struts-flow.gif

Process flow:

web.xml : Whenever the container gets start up the first work it does is to check the web.xml file and determine what struts action Servlets exist. The container is responsible for mapping all the file request to the correct action Servlet. 

A Request : This is the second step performed by the container after checking the web.xml file. In this the user submits a form within a browser and the request is intercepted by the controller. 

struts.config.xml : Struts has a configuration file to store mappings of actions. By using this file there is no need to hard code the module which will be called within a component. The one more responsibility of the controller is to check the struts.config.xml file to determine which module to be called upon an action request. Struts only reads the struts.config.xml file upon start up. 

Struts tag libraries : These are struts components helps us to integrate the struts framework within the project's logic. These struts tag libraries are used within the JSP page. This means that the controller and the model part can't make use of the tag library but instead use the struts class library for strut process control.

Property file : It is used to store the messages that an object or page can use. Properties files can be used to store the titles and other string data. We can create many property files to handle different languages. 

Business objects :  It is the place where the rules of the actual project exists. These are the modules which just regulate the day- to- day site activities.

The Response : This is the output of the View JSP object.

In this section I will describe you the Controller part of the Struts Framework. I will show you how to configure the struts-config.xml file to map the request to some destination servlet or jsp file.

The class org.apache.struts.action.ActionServlet is the heart of the Struts Framework. It is the Controller part of the Struts Framework. ActionServlet is configured as Servlet in the web.xml file as shown in the following code snippets.

<!-- Standard Action Servlet Configuration (with debugging) -->
<servlet>
     <servlet-name>action</servlet-name>
      <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
      <init-param>
        <param-name>config</param-name>
        <param-value>/WEB-INF/struts-config.xml</param-value>
      </init-param>
     <init-param>
        <param-name>debug</param-name>
        <param-value>2</param-value>
        </init-param>
        <init-param>
        <param-name>detail</param-name>
        <param-value>2</param-value>
     </init-param>
    <load-on-startup>2</load-on-startup>
</servlet>

This servlet is responsible for handing all the requests for the Struts Framework, user can map the specific pattern of request to the ActionServlet. <servlet-mapping> tag in the web.xml file specifies the url pattern to be handled by the servlet. By default it is *.do, but it can be changed to anything. This piece of  web.xml file shows the mapping:

<!-- Standard Action Servlet Mapping -->
<servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

The above mapping maps all the requests ending with .do to the ActionServlet. ActionServlet uses the configuration defined in struts-config.xml file to decide the destination of the request. Action Mapping Definitions (described below) is used to map any action. For this lesson we will create Welcome.jsp file and map the "Welcome.do" request to this page.

Welcome.jsp

<%@ taglib uri="/tags/struts-bean" prefix="bean" %>
<%@ taglib uri="/tags/struts-html" prefix="html" %>
<html:html locale="true">
<head>
   <title><bean:message key="welcome.title"/></title>
   <html:base/>
</head>
  <body bgcolor="white">
  <h3><bean:message key="welcome.heading"/></h3>
  <p><bean:message key="welcome.message"/></p>
</body>
</html:html>

Forwarding the Welcome.do request to Welcome.jsp

The "Action Mapping Definitions" is the most important part in the struts-config.xml. This section takes a form defined in the "Form Bean Definitions" section and maps it to an action class.

Following code under the <action-mappings> tag is used to forward the request to the Welcome.jsp.

<action  path="/Welcome"
        forward="/pages/Welcome.jsp"
/>
  

To call this Welcome.jsp file we will use the following code.

<html:link page="/Welcome.do">First Request to the controller</html:link>

Once the use clicks on on First Request to the controller link on the index page, request (for Welcome.do) is sent to the Controller and the controller forwards the request to Welcome.jsp. The content of Welcome.jsp is displayed to the user.

 

 

 

http://www.roseindia.net/struts/strutsstructure.gif

Setting Up Development Environment

You need java and Tomcat to proceed.

Installing Struts Application:

Download latest version of Struts from the official site of Struts http://jakarta.apache.org/struts. Extract the file to your favorite directory and copy struts-blank.war, struts-documentation.war and struts-example.war from "jakarta-struts-1.1\webapps" directory into "jakarta-tomcat-5.0.4\webapps" directory.

struts-blank.war is the blank struts application which is useful in creating struts application from scratch. We will use this file to create our web application.

struts-documentation.war contains API and important documents for the struts application development.

struts-example.war is a simple MailReader Demonstration Application.

Developing First Struts Application

Rename struts-blank.war  to struts-tutorial.war from jakarta-tomcat-5.0.4\webapps and copy it to the "jakarta-tomcat-5.0.4\webapps" directory. Tomcat automatically extracts the file and loads the application. 

What is Action Class?

An Action class in the struts application extends Struts 'org.apache.struts.action.Action" Class. Action class acts as wrapper around the business logic and provides an inteface to the application's Model layer. It acts as glue between the View and Model layer. It also transfers the data from the view layer to the specific business process layer and finally returns the procssed data from business layer to the view layer.

An Action works as an adapter between the contents of an incoming HTTP request and the business logic that corresponds to it. Then the struts controller (ActionServlet) slects an appropriate Action and creates an instance if necessary, and finally calls execute method.

To use the Action, we need to  Subclass and overwrite the execute() method. In the Action Class don't add the business process logic, instead move the database and business process logic to the process or dao layer.

The ActionServlet (commad) passes the parameterized class to Action Form using the execute() method. The return type of the execute method is ActionForward which is used by the Struts Framework to forward the request to the file as per the value of the returned ActionForward object.

Developing our Action Class?

Our Action class (TestAction.java) is a simple class that forwards the TestAction.jsp. Our Action class returns the ActionForward  called "testAction", which is defined in the struts-config.xml file (action mapping is show later in this page). Here is code of our Action Class:

TestAction.java



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

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

public class TestAction extends Action
{
  public ActionForward execute(
    ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request,
    HttpServletResponse response) throws Exception{
      return mapping.findForward("testAction");
  }
}

   
Understanding Action Class
Here is the signature of the Action Class.

public ActionForward execute(ActionMapping mapping,
                             ActionForm form,
                             javax.servlet.http.HttpServletRequest request,
                             javax.servlet.http.HttpServletResponse response)
                      throws java.lang.Exception

Action Class processes the specified HTTP request, and creates the corresponding HTTP response (or forwards to another web component that will create it), with provision for handling exceptions thrown by the business logic. Return an ActionForward instance describing where and how control should be forwarded, or null if the response has already been completed.

Parameters:

mapping - The ActionMapping used to select this instance

form - The optional ActionForm bean for this request (if any)

request - The HTTP request we are processing

response - The HTTP response we are creating

Throws:

Action class throws java.lang.Exception - if the application business logic throws an exception

Adding the Action Mapping in the struts-config.xml
To test the application we will add a link in the index.jsp 
<html:link page="/TestAction.do">Test the Action</html:link>

Following code under the <action-mappings> tag is used for mapping the TestAction class.

   <action
      path="/TestAction"
      type="TestAction">
      <forward name="testAction" path="/pages/TestAction.jsp"/>
   </action>           

To test the new application click on Test the Action link on the index page. The content of TestAction.jsp should be displayed on the user browser.

A Struts CRUD example

http://www.learntechnology.net/content/struts/struts_crud.jsp

 

This is a complete example which builds the application shown below.  It does not use a DB, but just allocates (hardcodes) a List of employees.  All the files in the app are not shown in the tutorial – although I have somewhat augmented that here, pasting in some more of the original files - but they all do come in the source download.  War files can also be downloaded. Source has an ant script which makes it easy to work on.

 

Here is the project (looks like he used netbeans but I just used textpad to edit the java and blackscreen/ant to build war file.)

 

Here is employeeAction –below-  from net.reumann.demo.action package which is referenced in the config file: (You would need a DepartmentsAction class to add CRUD to departments table)

 

package net.reumann.demo.action;

 

import net.reumann.demo.Constants;

import net.reumann.demo.form.EmployeeForm;

import net.reumann.demo.service.DepartmentService;

import net.reumann.demo.service.EmployeeService;

import net.reumann.demo.service.DepartmentDaoService;

import net.reumann.demo.service.EmployeeDaoService;

import net.reumann.demo.vo.Employee;

import org.apache.commons.beanutils.BeanUtils;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.apache.struts.action.*;

import org.apache.struts.actions.DispatchAction;

 

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.util.List;

 

public class EmployeeAction extends DispatchAction {

    private Log logger = LogFactory.getLog(this.getClass());

    private static EmployeeService empService = new EmployeeDaoService();

    private static DepartmentService deptService = new DepartmentDaoService();

 

    public ActionForward getEmployees(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {

        logger.debug("getEmployees");

        populateEmployees(request);

        return mapping.findForward(Constants.SUCCESS);

    }

 

    public ActionForward setUpForInsertOrUpdate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {

        logger.debug("setUpForInsertOrUpdate");

        EmployeeForm employeeForm = (EmployeeForm)form;

        if (isUpdate(request, employeeForm)) {

            Integer id = Integer.valueOf(employeeForm.getEmployeeId());

            Employee employee = empService.getEmployee(id);

            BeanUtils.copyProperties(employeeForm, employee);

        }

        prep(request);

        return mapping.findForward(Constants.SUCCESS);

    }

 

    public ActionForward delete(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {

        logger.debug("delete");

        EmployeeForm employeeForm = (EmployeeForm)form;

        Integer id = Integer.valueOf(employeeForm.getEmployeeId());

        empService.deleteEmployee(id);

        populateEmployees(request);

        return mapping.findForward(Constants.SUCCESS);

    }

 

    public ActionForward insertOrUpdate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {

        logger.debug("insertOrUpdate");

        EmployeeForm employeeForm = (EmployeeForm)form;

        if (validationSuccessful(request, employeeForm)) {

            Employee employee = new Employee();

            BeanUtils.copyProperties(employee, employeeForm);

            if (isUpdate(request, employeeForm)) {

                logger.debug("update");

                empService.updateEmployee(employee);

            } else {

                logger.debug("insert" );

                empService.insertEmployee(employee);

            }

            populateEmployees(request);

            return mapping.findForward(Constants.SUCCESS);

        } else {

            prep(request);

            return mapping.findForward(Constants.FAILURE);

        }

    }

 

    private void populateEmployees(HttpServletRequest request) {

        List employees = empService.getAllEmployees();

        request.setAttribute(Constants.EMPLOYEES, employees);

        prep(request);

    }

 

    private void prep(HttpServletRequest request) {

        request.setAttribute(Constants.DEPARTMENTS, deptService.getAllDepartments());

    }

 

    private boolean isUpdate(HttpServletRequest request, EmployeeForm empForm) {

        boolean updateFlag = true;

        //if ID is null or 0 we know we are doing an insert. You could check other

        //things to decide, like a dispatch param

        //It's annoying that BeanUtils will convert nulls to 0 so have to do 0 check also,

        //or you could register a converter, which is the preferred way to handle it, but goes

        //beyond this demo

        String id = empForm.getEmployeeId();

        if (id == null || id.trim().length() == 0 || Integer.parseInt(id) == 0) {

            updateFlag = false;

        }

        request.setAttribute("updateFlag", Boolean.valueOf(updateFlag));

        return updateFlag;

    }

 

    private boolean validationSuccessful(HttpServletRequest request, EmployeeForm form) {

        //if you really like using the validation framework stuff, you can just

        //call  ActionErrors errors = form.validate( mapping, request ); in this method

        //and check for errors being empty, if not save them and you're done.

        //I end up finding the validation framework a bit annoying to work with, so I do it

        //old-Skool way. Inevitably in a more complex app you end up having to perform

        //more complex validation than the validation framework provides, so I just assume

        //keep it all here in one place, versus having some handled by xml configuration and

        //some hardcoded.

        boolean isOk = true;

        ActionMessages errors = new ActionMessages();

        if (form.getAge() == null || form.getAge().trim().length() == 0) {

            errors.add("age", new ActionMessage("errors.required", "Age"));

        } else {

            try {

                Integer.parseInt(form.getAge());

            } catch (NumberFormatException e) {

                errors.add("age", new ActionMessage("errors.number", "Age"));

            }

        }

        if (form.getFirstName() == null || form.getFirstName().trim().length() == 0) {

            errors.add("firstName", new ActionMessage("errors.required", "First Name"));

        }

        if (form.getLastName() == null || form.getLastName().trim().length() == 0) {

            errors.add("lastName", new ActionMessage("errors.required", "Last Name"));

        }

        if (!errors.isEmpty()) {

            saveErrors(request, errors);

            isOk = false;

        }

        return isOk;

    }

 

}

 

Original example code ( to fit this lesson) and the one shown above:

rr-struts-crud source code
rr-struts-crud.war

 

This example comes with an ant build.  For CRUD: Add db tables for departments and employees and populate lists from these.

Higgins note—I used above source files, not the one below with a nested class below.

NOTE: This lesson (and the above source and war) was written using an Employee object that contained a "departmentId." It makes more sense to work with a Department object nested inside of the Employee object, so I've provided updated source code and a war to reflect this change. I haven't gotten around to updating this lesson to use this latter design (any volunteers much appreicated:). The new source and war can be downloaded below:

rr-struts-crud2 source code
rr-struts-crud2.war

Introduction

Assumes some previous knowledge of Struts. This download application demonstrates a very simple application that handles your typical CRUD - Create, Retrieve, Update, Delete operations. I've noticed many people understand the basics of struts but have a difficult time putting together all the pieces. Hopefully this application will serve as a decent guide.

Requirements

This application requires an application server that implements the Servlet 2.4 and JavaServer Pages 2.0 specifications. The examples should all work on Tomcat 5.x (Discussed in next section). Please do not e-mail about getting your application to run on a server other than Tomcat. The source code (and an Ant build file) is provided for all the lessons so you should be able to build a war from the source and run it on you application server of choice.

Jars

This application uses the following jars:

  • commons-beanutils.jar
  • commons-digester.jar
  • commons-logging.jar
  • jstl-1.1.1.jar
  • standard-1.1.1.jar
  • log4j-1.2.9.jar
  • struts.jar

You can just use the jars that come with the source code for this application, but if you want the latest versions:


The latest commons jar files commons-beanutils, commons-digester, commons-logging can be found here http://jakarta.apache.org/commons/, but it's esaier to just download the latest Struts Action Framwork http://struts.apache.org/acquiring.html and just use the commons jars that are in the example apps provided with Struts. Obviously you should get the latest struts jar from there as well.

JSTL jars (standard, jstl) http://cvs.apache.org/builds/jakarta-taglibs/nightly/

log4j http://logging.apache.org/log4j/docs/download.html

web.xml

 
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
 
    <display-name>Rick Reumann Struts-CRUD Demo</display-name>
    <description/>
 
    <servlet>
        <servlet-name>action</servlet-name>
        <servlet-class>
        org.apache.struts.action.ActionServlet</servlet-class>
        <init-param>
            <param-name>config</param-name>
            <param-value>/WEB-INF/struts-config.xml</param-value>
        </init-param>
        <init-param>
            <param-name>debug</param-name>
            <param-value>2</param-value>
        </init-param>
        <init-param>
            <param-name>detail</param-name>
            <param-value>2</param-value>
        </init-param>
        <load-on-startup>2</load-on-startup>
    </servlet>
 
    <servlet-mapping>
        <servlet-name>action</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
 
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
 
    
    <error-page>
        <exception-type>java.lang.Exception</exception-type>
        <location>/error.jsp</location>
    </error-page>
 
    <context-param>
        <param-name>
            javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
        <param-value>MessageResources</param-value>
    </context-param>
 
</web-app>

I like to define a global error.jsp in my web.xml that all my errors from the application will trickle up to. You could also define this in the struts-config but I prefer defining it in web.xml.

Notice the definition of the MessageResources file in the web.xml. Instead of using the old bean:write tag to display messages from our resources file, we're using the JSTL format tag. In order to use this tag the message bundle needs to be defined in the web.xml (*We still need to define this Resources file in the struts-config file so that errors and messages can be set up). If you look at the actual MessageResources file in the src directory you'll see it is actually called "MessageResources_en.properties." This is nice since you can provide different Locale resource files for different languages _it (Italian), _de (German), _fr (French), etc.

struts-config.xml

 
<?xml version="1.0" encoding="ISO-8859-1" ?>
 
<!DOCTYPE struts-config PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 1.2//EN"
        "http://jakarta.apache.org/struts/dtds/struts-config_1_2.dtd">
 
<struts-config>
 
    <form-beans>
        <form-bean name="employeeForm"
        type="net.reumann.demo.form.EmployeeForm"/>
    </form-beans>
 
    <action-mappings>
 
        <action
                path="/employeeSetUp"
                name="employeeForm"
                type="net.reumann.demo.action.EmployeeAction"
                scope="request"
                parameter="dispatch">
            <forward name="success" path="/employeeForm.jsp"/>
        </action>
 
        <action
                path="/employeeProcess"
                name="employeeForm"
                type="net.reumann.demo.action.EmployeeAction"
                scope="request"
                parameter="dispatch">
            <forward name="failure" path="/employeeForm.jsp"/>
            <forward name="success" path="/employees.jsp"/>
        </action>
 
    </action-mappings>
 
    <message-resources parameter="MessageResources" null="false"/>
 
</struts-config>

Nothing new and exciting here. I could have actually just had one mapping for this simple app /employeeProcess, but since I want the 'success' to go to a different page for setUp I just made its own mapping. A common mistake people make is that they forget that even if you use a DispatchAction you can still have multiple mappings that go to the same DispatchAction.

 Added by Higgins---Interface DepartmentDao

… notice, that to enable CRUD you’d have to add signatures/methods as in employeeDAO and implementing class and service etc shown below.

package net.reumann.demo.persistence;

 

import java.util.List;

//import java.util.Map;

 

public interface DepartmentDao {

    public List getAllDepartments();

    //this not neededΰpublic Map getDepartmentsMap();

}

 

 

 

Interface EmployeeDao

 
public interface EmployeeDao {
    public List getAllEmployees();
    public Employee getEmployee(Integer id);
    public void update(Employee emp);
    public void insert(Employee emp);
    public void delete(Integer id);
}

Always a good practice to code to an Interface.

EmployeeNoDBdao *****this is one class you need to change to use a db

 
public class EmployeeNoDBdao implements EmployeeDao {
    private static List employees;
    static {
        employees = new ArrayList();
        employees.add(
            new Employee(
                new Integer(1), "John", "Doe", new Integer(36),
                new Integer(100) ) );
        employees.add(
            new Employee(
                new Integer(2), "Bob", "Smith", new Integer(25),
                new Integer(300) ) );
    }
 
    Log logger = LogFactory.getLog(this.getClass());
 
    public List getAllEmployees() {
        return employees;
    }
 
    public Employee getEmployee(Integer id) {
        Employee emp = null;
        Iterator iter = employees.iterator();
        while( iter.hasNext() ) {
            emp = (Employee)iter.next();
            if ( emp.getEmployeeId().equals( id ) ) {
                 break;
            }
        }
        return emp;
    }
 
    public void update(Employee emp) {
        Integer id = emp.getEmployeeId();
        for(int i = 0; i < employees.size(); i++ ) {
            Employee tempEmp = (Employee)employees.get(i);
            if ( tempEmp.getEmployeeId().equals( id ) ) {
                 employees.set( i, emp );
                 break;
            }
        }
    }
 
    public void insert(Employee emp) {
        int lastId = 0;
        Iterator iter = employees.iterator();
        while( iter.hasNext() ) {
            Employee temp = (Employee)iter.next();
            if ( temp.getEmployeeId().intValue() > lastId ) {
                lastId = temp.getEmployeeId().intValue();
            }
        }
        emp.setEmployeeId(new Integer(lastId + 1));
        employees.add(emp);
    }
 
    public void delete(Integer id) {
        for(int i = 0; i < employees.size(); i++ ) {
            Employee tempEmp = (Employee)employees.get(i);
            if ( tempEmp.getEmployeeId().equals( id ) ) {
                 employees.remove( i );
                 break;
            }
        }
    }
}

There is no database or other persistence, we're just updating a static List of employees.

EmployeeService

 
public interface EmployeeService {
    public List getAllEmployees();
    public void updateEmployee(Employee emp);
    public void deleteEmployee(Integer id);
    public Employee getEmployee(Integer id);
    public void insertEmployee(Employee emp);
}

Typical interface for our Service class..

EmployeeDaoService

 
public class EmployeeDaoService implements EmployeeService {
    private EmployeeDao dao;
 
    public EmployeeDaoService() {
        this.dao = new EmployeeNoDBdao();//change this to your new db-enabled class
    }
 
    public List getAllEmployees() {
        return dao.getAllEmployees();
    }
 
    public void updateEmployee(Employee emp) {
        dao.update(emp);
    }
 
    public void deleteEmployee(Integer id) {
        dao.delete(id);
    }
 
    public Employee getEmployee(Integer id) {
        return dao.getEmployee(id);
    }
 
    public void insertEmployee(Employee emp) {
        dao.insert(emp);
    }
}

An implementation of our EmployeeService that we are using. Notice the constructor initializes our dao. If you were going to have different types of DAOs that could be used, you'd want to code something more flexible - like having a factory return you the proper dao. Or better yet, just use Spring which will inject the correct DAO for you based on what you define in a simple xml file. You might be wondering why we are even bothering with a Service class when it doesn't do anything but simply call our DAO methods. Why not just use our DAO object directly from the Action class and forget about this whole Service class? The reason I like to have this extra layer is that in a real-life more complex application there are often other business operations you might want to perform besides just calling you DAO. For example, maybe when an "update" is doine you need to call some process that sends out an e-mail or some kind of notification. If you don't use a Service class you are stuck now between coding this business logic either in your Action class or in the DAO. Neither of those places is really a good place for that kind of logic - hence we provide an extra service class to handle business rules that shouldn't be in the Action and don't belong in a DAO. Of course for rapid development, you could possibly skip the Service classes and just use the DAOs directly within your Action.

EmployeeActionΰhiggins note… you’d add a DepartmentAction to handle Department table CRUD

 
public class EmployeeAction extends DispatchAction {
    private Log logger = LogFactory.getLog(this.getClass());
    private static EmployeeService empService =
        new EmployeeDaoService();
    private static DepartmentService deptService =
        new DepartmentDaoService();
 
    public ActionForward getEmployees(ActionMapping mapping,
    ActionForm form, HttpServletRequest request,
    HttpServletResponse response) throws Exception {
        logger.debug("getEmployees");
        populateEmployees(request);
        return mapping.findForward(Constants.SUCCESS);
    }
 
    public ActionForward setUpForInsertOrUpdate(ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request, HttpServletResponse response)
        throws Exception {
        logger.debug("setUpForInsertOrUpdate");
        EmployeeForm employeeForm = (EmployeeForm)form;
        if (isUpdate(request, employeeForm)) {
            Integer id = Integer.valueOf(employeeForm.getEmployeeId());
            Employee employee = empService.getEmployee(id);
            BeanUtils.copyProperties(employeeForm, employee);
        }
        prep(request);
        return mapping.findForward(Constants.SUCCESS);
    }
 
    public ActionForward delete(ActionMapping mapping,
    ActionForm form,
    HttpServletRequest request, HttpServletResponse response)
        throws Exception {
        logger.debug("delete");
        EmployeeForm employeeForm = (EmployeeForm)form;
        Integer id = Integer.valueOf(employeeForm.getEmployeeId());
        empService.deleteEmployee(id);
        populateEmployees(request);
        return mapping.findForward(Constants.SUCCESS);
    }
 
    public ActionForward insertOrUpdate(ActionMapping mapping,
    ActionForm form, HttpServletRequest request,
    HttpServletResponse response) throws Exception {
        logger.debug("insertOrUpdate");
        EmployeeForm employeeForm = (EmployeeForm)form;
        if (validationSuccessful(request, employeeForm)) {
            Employee employee = new Employee();
            BeanUtils.copyProperties(employee, employeeForm);
            if (isUpdate(request, employeeForm)) {
                logger.debug("update");
                empService.updateEmployee(employee);
            } else {
                logger.debug("insert" );
                empService.insertEmployee(employee);
            }
            populateEmployees(request);
            return mapping.findForward(Constants.SUCCESS);
        } else {
            prep(request);
            return mapping.findForward(Constants.FAILURE);
        }
    }
 
    private void populateEmployees(HttpServletRequest request) {
        List employees = empService.getAllEmployees();
        request.setAttribute(Constants.EMPLOYEES, employees);
        prep(request);
    }
 
    private void prep(HttpServletRequest request) {
        request.setAttribute(Constants.DEPARTMENTS,
        deptService.getAllDepartments());
    }
 
    private boolean isUpdate(HttpServletRequest request,
        EmployeeForm empForm) {
        boolean updateFlag = true;
        String id = empForm.getEmployeeId();
        if (id == null || id.trim().length() == 0 ||
            Integer.parseInt(id) == 0) {
            updateFlag = false;
        }
        request.setAttribute("updateFlag", Boolean.valueOf(updateFlag));
        return updateFlag;
    }
 
    private boolean validationSuccessful(HttpServletRequest request,
    EmployeeForm form) {
        boolean isOk = true;
        ActionMessages errors = new ActionMessages();
        if (form.getAge() == null ||
            form.getAge().trim().length() == 0) {
            errors.add("age", new ActionMessage("errors.required", "Age"));
        } else {
            try {
                Integer.parseInt(form.getAge());
            } catch (NumberFormatException e) {
                errors.add("age", new ActionMessage("errors.number", "Age"));
            }
        }
        if (form.getFirstName() == null ||
            form.getFirstName().trim().length() == 0) {
            errors.add("firstName",
                new ActionMessage("errors.required", "First Name"));
        }
        if (form.getLastName() == null ||
            form.getLastName().trim().length() == 0) {
            errors.add("lastName",
                new ActionMessage("errors.required", "Last Name"));
        }
        if (!errors.isEmpty()) {
            saveErrors(request, errors);
            isOk = false;
        }
        return isOk;
    }
 
}

I like using standard DispatchActions. Using a DispatchAction you can keep related functionality in one class. This class is our 'controller' for our Employee related tasks.

  • The service classes are initialized as statics at the top of the Action. For a more robust app you'll probably want a more flexible way to initialize the Service class implementations (ie factory, or Spring).
  • I prefer to call validation manually in my Action class. The main reason for this is it allows me to never have that annoying problem of Lists not being in scope on my form if validation fails. Notice how if (validationSuccessful(request, employeeForm)) returns false the "prep(request)" method is called. In this example prep makes sure to put my Departments list in Request scope. Of course if you were certain that the list of departments was never going to change you could use Application scope, in which case you could set that list in scope somewhere else, but I'm simply demonstrating here how to easily make sure your Lists stay in Request scope even when validation fails. (Session would also work, but I think the Session is a bad place to store form Lists - you're basically adding extra overhead for no gain.)

If you want to use the validation framework and configure your validation rules in an xml file you could still use the approach above where you manually call validate. Thus the validationSuccessful method could be shortened to:

 
private boolean validationSuccessful(HttpServletRequest request,
EmployeeForm form) {
    ActionMessages errors =  form.validate();
    if (!errors.isEmpty()) {
        saveErrors(request, errors);
        return false;
    } else {
      return true;
    }
}

I tend to not use the validation framework to handle my validation because inevitably I end up with some complex validation that isn't easily handled by an xml configuration, so since I end up having to write up some validation code, I find it easier to have all of it one place versus some in a config file and some custom in a validation method. If you validation needs are definitely going to be simple, I'd use the validation framework and call the forms validate method manually as just described.

employee.jsp

 
<html>
<head>
    <link href="<c:url value='main.css'/>"
    rel="stylesheet" type="text/css"/>
    <title><fmt:message key="label.employees"/></title>
</head>
<body>
<div class="titleDiv"><fmt:message key="application.title"/></div>
<h1><fmt:message key="label.employees"/></h1>
<c:url var="url" scope="page" value="/employeeSetUp.do">
    <c:param name="dispatch" value="setUpForInsertOrUpdate"/>
</c:url>
<a href="${url}">Add New Employee</a>
<br/><br/>
<table class="borderAll">
<tr>
    <th><fmt:message key="label.firstName"/></th>
    <th><fmt:message key="label.lastName"/></th>
    <th><fmt:message key="label.age"/></th>
    <th><fmt:message key="label.department"/></th>
    <th> </th>
</tr>
<c:forEach var="emp" items="${employees}" varStatus="status">
    <tr class="${status.index%2==0?'even':'odd'}">
        <td class="nowrap"><c:out value="${emp.firstName}"/></td>
        <td class="nowrap"><c:out value="${emp.lastName}"/></td>
        <td class="nowrap"><c:out value="${emp.age}"/></td>
        <td class="nowrap">
            <%-- NOTE: this is NOT the best way to get the department name.
                 Just using this approach just for ease of backend objects --%>
            <c:forEach var="dept" items="${departments}">
                <c:if test="${dept.departmentId == emp.departmentId}">
                    <c:out value="${dept.name}"/>
                </c:if>
            </c:forEach>
        </td>
        <td class="nowrap">
            <c:url var="url" scope="page" value="/employeeSetUp.do">
                <c:param name="employeeId" value="${emp.employeeId}"/>
                <c:param name="dispatch" value="setUpForInsertOrUpdate"/>
            </c:url>
            <a href="${url}">Edit</a>
               
            <c:url var="url" scope="page" value="/employeeProcess.do">
                <c:param name="employeeId" value="${emp.employeeId}"/>
                <c:param name="dispatch" value="delete"/>
            </c:url>
            <a href="${url}">Delete</a>
        </td>
    </tr>
</c:forEach>
</table>
</body>
</html>

employeeForm.jsp

 
    
<html>
<head>
    <link href="<c:url value='main.css'/>" rel="stylesheet" type="text/css"/>
    <style>td { white-space:nowrap; }</style>
    <title><c:out value="${insertUpdateTitle}"/></title>
</head>
<body>
<div class="titleDiv"><fmt:message key="application.title"/></div>
<h1><c:out value="${insertUpdateTitle}"/></h1>
<html:form action="/employeeProcess">
<table>
 <tr>
    <td class="tdLabel"><fmt:message key="label.firstName"/>:</td>
    <td><html:text property="firstName" size="40"/>
    <html:errors property="firstName"/></td>
</tr>
<tr>
    <td class="tdLabel"><fmt:message key="label.lastName"/>:</td>
    <td><html:text property="lastName" size="40"/>
    <html:errors property="lastName"/></td>
</tr>
<tr>
    <td class="tdLabel"><fmt:message key="label.age"/>:</td>
    <td><html:text property="age" size="20"/>
    <html:errors property="age"/></td>
</tr>
 <tr>
    <td class="tdLabel"><fmt:message key="label.department"/>:</td>
    <td>
        <html:select property="departmentId">
            <c:forEach var="dept" items="${departments}">
                <html:option value="${dept.departmentId}">
                    <c:out value="${dept.name}"/>
                </html:option>
            </c:forEach>
        </html:select>
    </td>
</tr>
<tr>
    <td colspan="2">
        <html:hidden property="employeeId"/>
        <input type="hidden" name="dispatch" value="insertOrUpdate"/>
        <br/>
 
        <input type="submit" value="<
        fmt:message key="button.label.submit"/>" class="butStnd"/>
 
           
        <input type="submit" value="<
        fmt:message key="button.label.cancel"/>"  class="butStnd"
        onclick="document.employeeForm.dispatch.value='getEmployees'"/>
    </td>
</tr>
</table>
</html:form>
</body>
</html>

There is a java file of string constants and app properties – not shown here.

 

I redid the example above with a db connection.  It looks like this:

 

I have only partially implemented CRUD (I did create and delete and read) but only show the populateEmployees here.  The interface and jsp are complete and need no changes. The data is loaded from the database (shown below).  To do this, you add functionality to the existing java dao to save to db when changes are made.

 

Here are my db tables (departments)

And employees:

 

 

For Employee table CRUD, I only changed two files, and the includes etc and constructor calls etc related to them elsewhere --- some are noted above, in Service classes where the data is created/persisted.  The example uses EmployeeNoDBDao and DepartmentNoDBDao.  I replaced these with EmployeeWithDBDao and DepartmentWithDBDao.  Here, for example, is the constructor for the former,

 

public  EmployeeWithDBdao() {

        con = null;

        stmt = null;

        rs = null;

        try{

        employees = new ArrayList();

        Class.forName("org.gjt.mm.mysql.Driver");

                     // Get a Connection to the database

               con = DriverManager.getConnection( "jdbc:mysql://localhost/strutex", "DMH", "DMH");//

                System.out.println("got con...employee dao ");

               stmt = con.createStatement();

 

               // Execute an SQL query, get a ResultSet

               rs = stmt.executeQuery("SELECT employeeId,FIRSTNAME,LASTNAME, Age,departmentId from Employees");//

                System.out.println("executed stmt...employee dao ");

        while(rs.next()){

                               String first=rs.getString("FIRSTNAME");

                               String last=rs.getString("LASTNAME");

                               Integer empId=new Integer(rs.getInt("employeeId"));

                               Integer age=new Integer(rs.getInt("Age"));

                               Integer deptId=new Integer(rs.getInt("departmentId"));

                                System.out.println("looping rs...employee dao ");

                               employees.add( new Employee( empId, first, last, age, deptId ) );

                }//while rs has next

        }

        catch(Exception e){System.out.println("sql prob");}

 }//constructor

 

 

It is be easy to add similar functionality to this implementation class which updates the database when any changes are made – basically the same db stuff we’ve done before.  Here’s the (already existing) jsp form to add an employee

Here’s the db table after I’ve submitted this:

 

 

Here I’ve deleted Lewis Holmes:

 

 

Then, checking the database:

 

 

You’ll have to check the db and get a screen shot of that to assure that the employee really has been added/changed/deleted or just show me your working application.

 

Regular exercise:  Complete this with all CRUD on Employees.

E.C. exercise: add jsp & java classes to view/edit the Departments table as well and provide CRUD for that.  This is non trivial as you’ll have to mimick employee jsp and java classes for departments, and edit the struts-config.xml also.

 

 

For example, here is the departments.jsp:

 

And the db table

 

After deleting int’l travel:

 

And in the database:

 

 

 

Adding a new department

 

Here’s the db table showing the dept has been added.

 

 

More Struts CRUD examples.  This one is Struts2 which has some powerful new features: http://www.jarvana.com/jarvana/view/org/apache/struts/struts2-assembly/2.0.14/struts2-assembly-2.0.14-all.zip!/struts-2.0.14/docs/docs/crud-demo-i.html

 

It is actually posted at Apache http://struts.apache.org/2.x/docs/crud-demo-i.html

 

Another struts 2:  http://www.rkcole.com/articles/struts/crudTutorial/step1.html