You are on page 1of 100

Struts 2: A Milehigh View

Simple, Powerful Web Application Development

Struts University Series


Struts 2: A Milehigh View
Struts 2: A Milehigh View
Ground Floor FAQ
Actions & Results: Our Dynamic Duo
Interceptors: The rest of the framework
Letting the Model Drive
The Mysterious ValueStack
Expressing it with OGNL
Validation by the Numbers
Inverting Control
Plugging in Components
Struts 2: A Milehigh View
Ground Floor FAQ
Actions & Results: Our Dynamic Duo
Interceptors: The rest of the framework
Letting the Model Drive
The Mysterious ValueStack
Expressing it with OGNL
Validation by the Numbers
Inverting Control
Plugging in Components
Ground Floor FAQ
What is the Apache Software Foundation?
What is Struts?
How does Use XWork?
What Does XWork Provide?
What is the ASF?
A not-for-profit corporation
Many well-known projects

 HTTPD  iBATIS
 Ant  Lucene
 Cocoon  SpamAssassin
 Commons  Tapestry

http://www.Apache.org/
What is Struts
An extensible "front controller" that
dispatches requests to an "action"
handler
A location handler that transfers control
to another resource
A tag library that helps developers create
interactive form-based applications.
What is Struts 2
Second generation of Struts
Merger of Struts and WebWork 2
A wrapper on top of a generic Command
Pattern implementation (XWork)
How Does Struts
Use XWork?

XWork is a generic front


controller
Struts is an adapter
between the web world
and XWork
What Does XWork Provide?
Command pattern implementation
 Actions are command objects in XWork

Adds advanced features


 Interceptors
 Includes setting parameters, workflow, etc
 Results
 Includes one for chaining to another Action
 Powerful expression language – OGNL
 Flexible type conversion
 Metadata driven validation framework
What Does WebWork Add?
Adapter for HTTP request / response
Integration of Session / Application
scopes
ServletDispatcher translates HTTP
requests into Action execution
Request parameters passed to Action
Web-specific Result types
Struts 2: A Milehigh View
Ground Floor FAQ
Actions & Results: Our Dynamic Duo
Interceptors: The rest of the framework
Letting the Model Drive
The Mysterious ValueStack
Expressing it with OGNL
Validation by the Numbers
Inverting Control
Plugging in Components
Our Dynamic Duo
Results – What you see
Actions – How you get it
Results: The “View” in MVC
Results are what happens after an Action
Displaying a page
 JSP / Velocity / FreeMarker
 JasperReports / XSLT

Chaining to another Action


Add your own
 Email? Command line output?
Actions: The Decider
Actions are command objects
Actions should be simple!
Actions are not tied to any web classes
Action interface has only one method:
interface Action {
String execute() throws Exception;
}
ActionSupport
Actions are only required to implement the
Action interface
ActionSupport is a useful base class
 Implements Action
 Provides error message support
 Field and Action level messages
 Messages automatically used by UI tags
 Provides message internationalization
 Message bundle for each Action
 Looks up class hierarchy
 UI tags use internationalization support to find text
 All features based on Interfaces, so you can implement your own
from scratch!
A “Happy Birthday” Example
Workflow
 Take the users name and generate a hello message
personalized for them
 If the user enters a birthday, calculate how long until their
birthday

Demonstrates
 Implementing an Action
 Configuring Struts
 Using the taglib
 Type conversion
 Error messages
Birthday.java
public class Birthday extends ActionSupport {
private User user;
public User getUser() { return user; }
public int getDaysTillNextBirthday() { ... }
public String execute() throws Exception {
String name = getUser().getName();
if ((name == null) || (name.trim().equals(""))) {
addFieldError("user.name", "You must enter a name.");
}
if (hasErrors()) {
return INPUT;
}
return SUCCESS;
}
}
User.java
public class User {
private String name;
private Date birthday;

public String getName() { return name; }


public void setName(String name) { this.name = name; }

public Date getBirthday() { return birthday; }


public void setBirthday(Date birthday) {
this.birthday = birthday; }
}
Input.jsp
<%@ taglib prefix="s" uri="/tags" %>
<html>
<head><title>Happy Birthday Example</title></head>
<body>
<s:form action="Birthday">
<s:textfield label="Name" name="user.name"/>
<s:textfield label="Birthday” name="user.birthday"/>
<s:submit value="Say Hello"/>
</s:form>
</body>
</html>
Result.jsp
<%@ taglib prefix="s" uri="/tags" %>
<html>
<head>
<title>Hello <s:property value="user.name"/></title>
</head>
<body><p>
Hello <s:property value="user.name"/>!
<s:if test="user.birthday != null">
<s:property value="daysTillNextBirthday"/>
days till your next birthday.
</s:if>
</p></body>
</html>
struts.xml for Happy Birthday
<struts>
<include file="struts-default.xml"/>
<package name="default" extends="struts-default" abstract="true">
<action name="Open">
<result>/pages/Happy/Input.jsp</result>
</action>
</package>
<package name="Happy" extends="default" namespace="/Happy">
<action name="Birthday" class="cookbook2.happy.Birthday">
<result>/pages/Happy/Result.jsp</result>
<result name="input">/pages/Happy/Input.jsp</result>
</action>
</package>
</struts>
Happy Birthday
A “Happy Birthday” Example
Birthday.java
 Validates input
User.java
 Encapsulates input
Input.jsp
 Collects input
Result.jsp
 Displays input
A “Happy Birthday” Example
Birthday.java
 Validates input
User.java
 Encapsulates input
Input.jsp
 Collects input
Result.jsp struts.xml
 Displays input  Wires it all together
Review – Match Columns
Birthday.java Success page
User.java Form page
Input.jsp Business object
Configuration file
Result.jsp
Action class
struts.xml
Unit test
Review – Match Columns
Birthday.java Action class
User.java Business object
Input.jsp Form page
Success page
Result.jsp
Configuration file
xwork.xml
Unit Test
Unit Testing Actions
Struts 2 shines in testability
Actions have no web dependencies
 No need for mocks, containers, et cetera.
Unit Testing Actions
Three ways of testing Actions
1) Create a new instance, set some
properties, and execute!
2) Use the framework directly in your tests to
execute Actions within a workflow
3) Extend the XworkTestCase base class
which provides a default configuration
BirthdayTest.java
public void testFieldErrorAddedWhenNoUserName()
throws Exception {
HelloWorldAction action = new Birthday();
assertEquals(Action.INPUT, action.execute());
assertTrue(action.hasFieldErrors());
Map fieldErrors = action.getFieldErrors();
assertTrue(fieldErrors.containsKey("user.name"));
List userNameErrors = (List)
fieldErrors.get("user.name");
assertEquals(1, userNameErrors.size());
assertEquals("You must enter a name.",
userNameErrors.get(0));
}
Notes on the Example
Compose page model from many objects
using expression language
UI tags automatically show field error
messages next to the form field
Return code from Action determines
which page to display
Much of the “magic” is in the rest of the
framework ... the Interceptors
Struts University Series
Review
Struts is
 An extensible "***** **********" that
dispatches requests to an "action" handler
 A location ******* that transfers control to
another resource
 A *** ******* that helps developers create
interactive form-based applications.
Review
Struts is
 An extensible "front controller" that
dispatches requests to an "action" handler
 A location handler that transfers control to
another resource
 A tag library that helps developers create
interactive form-based applications.
Review – Ground Floor
***** is a generic front controller
FilterDispatcher translates HTTP
requests into ****** execution
******* are the View in MVC
Actions are ******* objects
Actions should be (simple / complex)
Review – Ground Floor
XWork is a generic front controller
FilterDispatcher translates HTTP
requests into Action execution
Results are the View in MVC
Actions are command objects
Actions should be (simple / complex)
Struts 2: A Milehigh View
Ground Floor FAQ
Actions & Results: Our Dynamic Duo
Interceptors: The rest of the framework
Letting the Model Drive
The Mysterious ValueStack
Expressing it with OGNL
Validation by the Numbers
Inverting Control
Plugging in Components
Interceptors: Domain AOP
Interceptors allow
custom code into the call
stack
Much of the core
functionality of the
framework is
implemented as
Interceptors
Custom Interceptors are
easy to add
TimerInterceptor
TimerInterceptor is the simplest Interceptor
Times the execution of the Action
public String intercept(ActionInvocation invocation)
throws Exception {
if (log.isInfoEnabled()) {
long startTime = System.currentTimeMillis();
String result = invocation.invoke();
long executionTime =
System.currentTimeMillis() - startTime;
String namespace =
invocation.getProxy().getNamespace();

LoggingInterceptor
public class LoggingInterceptor implements Interceptor {
public String intercept(ActionInvocation invocation)
throws Exception {
logMessage(invocation, START_MESSAGE);
String result = invocation.invoke();
logMessage(invocation, FINISH_MESSAGE);
return result;
}
}
Other Interceptors
Setting Parameters
 ParameterInterceptor
 StaticParameterInterceptor
 ChainingInterceptor
 ConversionErrorInterceptor
 FileUploadInterceptor

Defining Workflow
 DefaultWorkflowInterceptor
 PrepareInterceptor
 ServletConfigInterceptor
 ExecuteAndWaitInterceptor

Preventing duplicate posts


 Two types of token interceptors
Interceptor Stacks
Interceptors can be grouped into named
Interceptor Stacks
Several stacks come pre-defined in
struts-default.xml
Interceptor Stacks
defaultStack
<interceptor-stack name="defaultStack">
<interceptor-ref name="static-params"/>
<interceptor-ref name="params"/>
<interceptor-ref name="conversionError"/>
</interceptor-stack>

validationWorkflowStack
<interceptor-stack name="validationWorkflowStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="validation"/>
<interceptor-ref name="workflow"/>
</interceptor-stack>

Stacks can be built of other stacks and


interceptors
Review - Interceptor
Interceptors allow custom **** into the
request processing pipeline
Much of the core ************* of the
framework is implemented as
Interceptors
Custom Interceptors are (hard / easy) to
add
Review - Interceptor
Interceptors allow custom code into the
request processing pipeline
Much of the core functionality of the
framework is implemented as
Interceptors
Custom Interceptors are easy to add
Review - Interceptors
Interceptors can be grouped into named
Interceptor (Sets / Stacks)
To suspend processing of an Interceptor
class and go on to the next, we use the
idiom
 String result = invocation.invoke();
 String result = interceptor.intercept();
 String result = action.execute();
Review - Interceptors
Interceptors can be grouped into named
Interceptor Stacks
To suspend processing of an Interceptor
class and go on to the next, we use the
idiom
 String result = invocation.invoke();
WebWork2: A Milehigh View
Ground Floor FAQ
Actions & Results: Our Dynamic Duo
Interceptors: The rest of the framework
Letting the Model Drive
The Mysterious ValueStack
Expressing it with OGNL
Validation by the Numbers
Inverting Control
Plugging in Components
Letting the Model Drive
Two ways to collect input for Actions:
1. Model-driven
 Action has methods returning your model classes
(myAction.getUser())
 Fields in the view are fields of your model
 Views refer directly to your model
(property=‘user.name’)
 Excellent because it allows model reuse
2. Field-driven
 Action has fields of its own, which are fields in the view
 execute() collates fields and interacts with the model
 Views refer to action fields
(property=‘daysTillNextBirthday’)
 Useful where form is not parallel to model

The two can be mixed and matched


ModelDriven Interface
Direct support of model-driven Actions
The ModelDriven Interface has one
method: public Object getModel()
Properties of the model are flattened --
“name” instead of “user.name”
Applies to UI tags, form field names, and
so forth
Making Happy Birthday ModelDriven

Make the class implement ModelDriven


Change getUser() to getModel()

public class Birthday extends ActionSupport


implements ModelDriven {

public Object getModel() {
return user;
}
}
ModelDriven: Changes to the JSP
Change “user.name” to just “name”
In form.jsp
<s:textfield label="Name" name="name"/>
<s:textfield label="Birthday" name="birthday"/>

In success.jsp
Hello <s:property value="name"/>!
<s:if test="birthday != null">
<s:property value="daysTillNextBirthday"/>
days till your next birthday.
</s:if>
Applying the ModelDrivenInterceptor

In struts.xml
<package name="Model" extends="struts-default"
namespace="/Model">
<action name="main"
class="com.opensymphony.xwork.ActionSupport">
<result>/Birthday/form.jsp</result>
</action>
<action name="HappyBirthday" class="happy.Birthday">
<interceptor-ref name="model-driven"/>
<interceptor-ref name="defaultStack"/>
<result>/Model/success.jsp</result>
<result name="input">/Model/form.jsp</result>
</action>
</package>
Looking at the ModelDrivenInterceptor

The ModelDrivenInterceptor pushes the Model onto the


ValueStack
Review – Model Driven
The two ways to collect input for Actions
is model-driven and *****-driven.
Properties of the model are ********* --
“name” instead of “user.name”
The ModelDrivenInterceptor pushes the
***** object onto the **********.
The object can be a rich ******** with
typed properties.
Review – Model Driven
The two ways to collect input for Actions
is model-driven and field-driven.
Properties of the model are flattened --
“name” instead of “user.name”
The ModelDrivenInterceptor pushes the
model object onto the ValueStack.
The object can be a rich JavaBean with
typed properties.
WebWork2: A Milehigh View
Ground Floor FAQ
Actions & Results: Our Dynamic Duo
Interceptors: The rest of the framework
Letting the Model Drive
The Mysterious ValueStack
Expressing it with OGNL
Validation by the Numbers
Inverting Control
Plugging in Components
What is the
ValueStack?
The ValueStack builds a
stack of objects
Objects are examined to
find property values
The ValueStack allows
the expression language
to find property values
across multiple objects
How is the ValueStack used?
The Action instance is always pushed onto
the ValueStack
The Model is pushed on by the
ModelDrivenInterceptor
The UI tags use it to push values on during
their scope and evaluate expressions
 The <s:iterator> tag pushes the current item onto the stack
 The <s:bean> tag pushes a bean instance on
 The <s:property> tag evaluates an expression against the
ValueStack
 All tag attribute values are evaluated against the stack when being
set onto the tag instances
WebWork2: A Milehigh View
Ground Floor FAQ
Actions & Results: Our Dynamic Duo
Interceptors: The rest of the framework
Letting the Model Drive
The Mysterious ValueStack
Expressing it with OGNL
Validation by the Numbers
Inverting Control
Plugging in Components
Review - ValueStack
The ValueStack builds a ***** of objects.
Objects are examined to find property ******.
The ValueStack allows the ********** ******** to find
property values across multiple objects.
Review - ValueStack
The ValueStack builds a stack of objects
Objects are examined to find property values
The ValueStack allows the expression language to find
property values across multiple objects
OGNL Expression Language
For expressions, the framework uses
OGNL (Object Graph Navigation Language)
 An expression and binding language for getting and setting
properties of Java objects
 Normally the same expression is used for both getting and
setting the value of a property
 Easy to learn, yet powerful
 Incrementally compiled expressions - fast!
 Embedded everywhere – views, ValueStack, *.xml
 Independent Open Source project - http://www.ognl.org
OGNL samples
OGNL Result

user.name getUser().getName()

user.toString() getUser().toString()

item.categories[0] First element of Categories


collection
@com.example.Test@foo Calls the static foo() method on
() the com.example.Test class

name in {null,”fred”} True if name is null or “fred”

categories.{name} Calls getName() on each Category in


the collection, returning a new
collection (projection)
Review - OGNL
OGNL stands for Object Graph **********
Language
OGNL is an expression and *******
language for getting and setting
properties of Java objects
Within the framework, OGNL is ********
everywhere – views, ValueStack, xml
configuration files.
Review - OGNL
OGNL stands for Object Graph
Navigation Language
OGNL is an expression and binding
language for getting and setting
properties of Java objects
Within the framework, OGNL is
embedded everywhere – views,
ValueStack, xml configuration files.
Struts 2: A Milehigh View
Ground Floor FAQ
Actions & Results: Our Dynamic Duo
Interceptors: The rest of the framework
Letting the Model Drive
The Mysterious ValueStack
Expressing it with OGNL
Validation by the Numbers
Inverting Control
Plugging in Components
Validation Framework
Separates validation from Action classes
Allows for different validations in different
contexts for the same object
Provides hooks for localized validation
messages
Two types of validators:
 Object level
 Field level
HelloWorldAction-validation.xml
<validators>
<field name="user.name">
<field-validator type="requiredstring">
<message>You must enter a name.</message>
</field-validator>
</field>
</validators>

Validation file in the same package as


the class
Defines one field validator and the error
message to add if it fails
Bundled Validators
Validator Result
RequiredField field != null
RequiredString field != null && string.length() > 0
IntRange Integer in a given range
DateRange Date in a given range
Email Valid email field
URL Valid URL field
Expression / Any OGNL expression evaluates to true
FieldExpression eg. pet.name != “dog”
Allows you to create very powerful
validations using just XML and your existing
model
Changes to the Action
The execute() method can just return
SUCCESS
public String execute() throws
Exception { return SUCCESS; }
The validation of the “user.name” property is
handled by the validation interceptor
The workflow interceptor returns INPUT if
there are any errors added to the Action
Changes to the Action
public class Birthday extends ActionSupport {
private User user;
public Object getModel() { return user; }
public int getDaysTillNextBirthday() { ... }
public String execute() throws Exception {
String name = getUser().getName();
if ((name == null) || (name.trim().equals(""))) {
addFieldError("user.name", "You must enter a name.");
}
if (hasErrors()) {
return INPUT;
}
return SUCCESS;
}
}
Client-Side JavaScript Validation Flow

Invoked using onBlur and onChange


events
Asynchronous Javascript
XmlHttpRequest call to the server
Uses server-side validations and returns
results to the client
Any validation errors are rendered using
DHTML and JavaScript
Client-Side JavaScript Validation
XmlHttpRequest validation on the server
side
Doesn’t require two versions of each
validator – all the actual validation logic
happens on the server side
Provides near-instant validation
feedback, just like normal JavaScript
validation
Review - Validation
The framework separates validation from
****** classes.
There are two flavors of validators: object
level and ***** level.
The workflow interceptor returns ***** if
there are any errors added to the Action.
JavaScript validation is invoked using
onBlur and ******** events.
Review - Validation
The framework separates validation from
Action classes.
There are two flavors of validators: object
level and field level.
The workflow interceptor returns INPUT if
there are any errors added to the Action.
JavaScript validation is invoked using
onBlur and onChange events.
Struts 2: A Milehigh View
Ground Floor FAQ
Actions & Results: Our Dynamic Duo
Interceptors: The rest of the framework
Letting the Model Drive
The Mysterious ValueStack
Expressing it with OGNL
Validation by the Numbers
Inverting Control
Plugging in Components
What is Inversion of Control?
IoC removes the onus of managing
components from your application code
to a container.
Container manages lifecycles and
dependencies between components.
EJB is IoC, but with a static list of
services
 Security, persistence and transactions

Jakarta Avalon, Spring’s BeanFactory,


and PicoContainer are all IoC containers
Advantages of IoC and
Dependency Injection
Promotes simplicity and decoupling
Components describe themselves
Dependencies are discovered automatically
Adheres to Law of Demeter
 Classes coupled to only what they use
 Encourages smaller responsibility classes

Leads to better interface/implementation


separation
Unit tests become far simpler
 They become ‘mini-containers’
A ShoppingCart Service
A ShoppingCart service - provides a user’s cart (session scoped)

ShoppingCartAware.java:
public interface ShoppingCartAware {
public void setShoppingCart(ShoppingCart cart);
}

ShoppingCart.java:
public interface ShoppingCart {
public void addPet(Pet pet);
public void removePet(Pet pet);
public boolean isEmpty();
public int size();
public List getPets();
}
A Petstore Service
A PetStore service - provides management of our pet inventory
(application scoped)

PetStoreAware.java:
public interface PetStoreAware {
public void setPetStore(PetStore store);
}

PetStore.java:
public interface PetStore {
void savePet(Pet pet);
void removePet(Pet pet);
List getPets();
Pet getPet( long id);
}
A Service Client Action
public class AddToCart implements Action, PetStoreAware,
ShoppingCartAware {
...
public void setPetStore(PetStore ps) { this.petStore = ps; }
public void setShoppingCart(ShoppingCart c)
{ this.cart = c; }

public String execute() throws Exception {


if (cart == null || petId == 0)
return ERROR;
Pet pet = petStore.getPet(petId);
cart.addPet(pet);
return SUCCESS;
}
}
Configuring Services
These services are configured in an XML document, like so:
<components>
<component>
<scope>application</scope>
<class>org.petsoar.pets.DefaultPetStore</class>
<enabler>org.petsoar.pets.PetStoreAware</enabler>
</component>
<component>
<scope>session</scope>
<class>org.petsoar.cart.SimpleShoppingCart</class>
<enabler>org.petsoar.cart.ShoppingCartAware</enabler>
</component>
</components>
Review – IoC
IoC removes the onus of managing
components from your application code
to a *********.
A container manages ********** and
dependencies between components.
A container can ******** dependencies
automatically.
Dependency injection promotes
simplicity and **********.
Review – IoC
IoC removes the onus of managing
components from your application code
to a container.
The container manages lifecycles and
dependencies between components.
A container can discover (or inject)
dependencies automatically.
Dependency injection promotes
simplicity and decoupling.
Struts 2: A Milehigh View
Ground Floor FAQ
Actions & Results: Our Dynamic Duo
Interceptors: The rest of the framework
Letting the Model Drive
The Mysterious ValueStack
Expressing it with OGNL
Validation by the Numbers
Inverting Control
Plugging in Components
Achieving reuse with Struts
Struts provides many opportunities for
modularizing and reusing components
 Make Interceptors to do work before and after the Action is
executed
 Create services which are applied via IoC
 Create Action base classes
 Create reusable UI components
 Create reusable application modules
Reusable UI Components
The Struts UI tags are implemented
using Freemarker templates
You can provide your own templates for
the tags
The <s:component> tag lets you use any
template
Examples
 A date picker template
 Error message template
UI Tag Rendering
UI tags use Freemarker to actually render HTML fragments, e.g. in your
JSP view:
<s:textfield label=“Name" name=“project.name" />

renders via text.ftl:

<#include
"/${parameters.templateDir}/${parameters.theme}/controlheader
.ftl" />
<#include "/${parameters.templateDir}/simple/text.ftl" />
<#include "/${parameters.templateDir}/xhtml/controlfooter.ftl"
/>
UI Tag Templates
WebWork comes with default UI
templates
 Show the label beside the form field
 Show the field errors above the form field

<ww:textfield label="Name" name="user.name"/>


renders as (with an error message)
Custom UI Components
The framework allows you to easily
create custom UI components
Requires writing a single Freemarker
template
Excellent for componentizing views
Example: a date picker to allow users to
enter dates into text fields easily…
A Custom Date Picker (from JIRA)
Here’s the form field and popup:
Using the custom UI Component
View (addpet.jsp):
<s:component label="Created After" template="datepicker.vm"
name="pet.created">
<s:param name="formname" value="editform" />
</s:component>

Component template (datepicker.ftl)


<#include
"/${parameters.templateDir}/${parameters.theme}/controlheader.ftl"
/>
<#include "/${parameters.templateDir}/simple/datepicker.ftl" />
<#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />

You can also extend the component tag like the other UI tags do create
new UI tags
Reusable Application Modules
You can build reusable modules which can be included in any
Struts application
struts.xml include allows you to merge in external configuration
Struts allows you to include both Actions and templates in a JAR
file
Example: The Struts configuration browser
Adding the Config Browser to Your Application

Step 1: Add the struts-config-browser.jar (12 Kb) to WEB-INF/lib


Step 2: Add an include in struts.xml
<struts>
<include file="struts-default.xml"/>
<include file="config-browser.xml"/>

</struts>
Step 3: Add a velocity.properties file under WEB-INF
 velocimacro.library = struts.vm, tigris-macros.vm
Step 4: That’s it! There is no step four!
The Config Browser in Action
Struts University Series

You might also like