webwork 2 - Atlassian Blogs

advertisement
WEBWORK 2
“Strutting the OpenSymphony way”
Prepared by Mike Cannon-Brookes - June, 2003
mike@atlassian.com - http://www.atlassian.com
Agenda
•
•
•
•
•
•
•
WebWork 2 & XWork overview
Actions
Views
Interceptors
Validation
Inversion of Control (IoC)
Struts Comparison
WebWork 2 Overview
•
•
•
•
•
•
‘Pull-based’ MVC framework
Focus on componentization & code reuse
Implementation of the command pattern
Second generation of WebWork
Not tied to the web!
Currently in beta - but being well used
XWork
•
•
•
•
Generic command pattern framework
Commands represent a unit-of-work
Split off from Webwork 1
Why command pattern?
WebWork 2
Web
WebWork 1
XWork 1
Non-web
XWork provides…
• Core command pattern framework for request /
response environment
• Interceptor framework
• Action chaining
• IoC componentization framework
• Runtime attribute validation framework
• Built-in type conversion using OGNL
• Doesn’t provide: anything to do with the web!
WebWork 2 provides..
• Tied to HTTP request / response
• Integration with session / application scopes
• Servlet-based dispatcher to turn incoming
requests into action/s.
• Automatically set properties of action based
on request parameters
• View integration (JSP, Velocity etc)
• User interface / form components
Actions
•
•
•
•
An Action is a command.
Each action should be a ‘unit of work’
Actions should be simple!
Action interface has only one method:
interface Action {
String execute() throws Exception;
}
• Let’s look at small example…
Basic Example: Add Pet
Use case - we want to add a Pet to our system:
Basic Example: Add Pet
• A basic example of an action, view and
configuration.
–
–
–
–
Model:
Controller:
View:
Config:
Pet.java (simple bean)
AddPet.java (WW action)
addpet.jsp
xwork.xml
Basic Example: Pet Model
...
public class Pet {
private long id;
private String name;
public long getId() …
public void setId(long id) …
public String getName() …
public void setName(String name) …
}
Basic Example: AddPet action
public class AddPet implements Action {
protected Pet pet = new Pet();
public String execute() throws Exception {
if (pet.getName() == null )
return ERROR;
Registry.getPetStore().savePet(pet);
return SUCCESS;
}
public Pet getPet() {
return pet;
}
}
Basic Example: addpet.jsp
<%@ taglib uri= "webwork" prefix= "webwork" %>
<html>
<head><title>Add A Pet</title></head>
<body>
<form action= "AddPet.action">
<webwork:textfield label="Name" name="pet.name" />
<input type= "submit" value= "Add">
</form>
</body>
</html>
Basic Example: xwork.xml
<xwork>
<package name="default">
...
<action name="AddPet” class="org.petsoar...AddPet">
<interceptor-ref name="defaultStack" />
<result name="error">addpet.jsp</result>
<result name="success">success.jsp</result>
</action>
...
</package>
</xwork>
ActionSupport
• Useful base class, providing:
– error message support
• action and field specific errors
• field errors are automatically supported by views
– internationalisation support
• 1 resource bundle per action
• pervasive UI support for retrieving messages
Model-Driven vs Field-Driven
•
2 types of Actions possible:
1. Model-driven
–
–
–
–
Action has methods returning your model classes
(myAction.getPet())
Fields in the view are fields of your model
Views refer directly to your model (property=‘pet.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=‘name’)
Useful where form is not parallel to model
Action Composition
• Problem: traditional MVC actions contain duplication or deep class
hierarchies
• Solution: A single WW action can be composed of multiple smaller
reusable beans.
• Before:
public class Signup implements Action {
public String getName(); [+ setter]
public String getHomeInternationalCode(); [+ setter]
public String getHomeAreaCode(); [+ setter]
public String getHomeNumber(); [+ setter]
public String getWorkInternationalCode(); [+ setter]
public String getWorkAreaCode(); [+ setter]
public String getWorkNumber(); [+ setter]
...
}
Action Composition
• After:
public class Signup implements Action {
public String getName(); [+ setter]
public PhoneNumber getHome();
public PhoneNumber getWork();
...
}
public class PhoneNumber {
public String getInternationalCode(); [+setter]
public String getAreaCode(); [+setter]
public String getNumber(); [+setter]
}
• We can also reduce duplication in our views in the same way - using
UI components.
Action Dispatching
• A Dispatcher configures and executes an action.
• WebWork has ServletDispatcher and a
FilterDispatcher for the servlet environment
• XWork separates the implementation and
invocation of an action
• ClientDispatcher allows actions created by a client
to be executed on server
– execute an action over RMI (ie in an applet)
– execute an action via SOAP
WebWork Views
• Multiple supported view technologies:
–
–
–
–
–
JSP
Velocity
XML
JasperReports
… add your own
• Not being tied to the web allows multiple
pluggable ‘result types’
– - ie action chains, pooling, HTTP redirects etc
View Expression Language
• For expressions WW uses OGNL (Object
Graph Navigation Language)
–
–
–
–
–
Incrementally compiled expressions - fast!
Easy to learn, powerful expression language
Componentised (so you can embed anywhere)
Embedded everywhere - views and *.xml
Independently run Open Source project http://www.ognl.org
OGNL Samples
OGNL
Result
pet.name
getPet().getName()
pet.toString()
getPet().toString()
pet.categories[0]
First element of Categories collection
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)
UI Components
• Powerful for componentization of views
• Standard form components are built in
– text field, radio boxes, submit button etc.
• Skinnable using ‘themes’
– multiple sets of templates to render same
components
• Usable from any view
– JSP or Velocity at the moment
UI Component Usage
• JSP:
<ui:textfield label="Username" name="username" />
<ui:password label="Password" name="password" />
<ui:component template="/mytemplate.vm">
<ui:param name="param1" value="value1" />
</ui>
<ui:submit value="'login'" align="right" />
• Velocity:
#tag (TextField "label=Username" "name=username")
#tag (Password "label=Password" "name=password")
#bodytag (Component "template=/mytemplate.vm")
#param ("name=param1" "value=value1")
#end
#tag (Submit "value='login'" "align=right")
Component Rendering
•
<webwork:textfield label="Name" name="project.name" />
looks as follow (with added header) :
• UI components automatically present field error messages,
added by validation framework or action itself:
Component Rendering
• Uses Velocity to actually render HTML fragments,
eg in your JSP view:
<webwork:textfield label="Name" name="project.name" />
renders via textfield.vm:
#parse( "/decorators/xhtml/controlheader.vm" )
<input type="text" name="${tag.Name}"
value="$!{tag.ActualValue}"
#if ($tag.Size > 0) size="${tag.Size}"#end />
#parse( "/decorators/xhtml/controlfooter.vm" )
Custom components
• WW allows you to easily create custom UI
components
• Requires writing a single Velocity template
• Excellent for componentizing views (with
componentized or model-driven actions)
• Example: a date picker to allow users to
enter dates into text fields easily…
Custom component example
• Here’s the form field and popup:
Custom component example
•
View (addpet.jsp):
<webwork:component label="Created After" template="datepicker.vm" name="pet.created">
<webwork:param name="'formname'" value="'editform'" />
</webwork:component>
•
Component template (datepicker.vm)
#parse( "/decorators/xhtml/controlheader.vm" )
<script language="JavaScript" src="/decorators/datepicker.js" />
<input type="text" name="${tag.Name}" value="$!{tag.ActualValue}" />
<a href="javascript:show_calendar('${tag.Params.get("formname")}', '${tag.Name }');"><img
src="/images/icons/cal.gif"></a>
#parse( "/decorators/xhtml/controlfooter.vm" )
Interceptors
• “Practical AOP”
– very simple, no external dependencies
– allows you to intercept action invocations.
• Help decouple and componentize your code
• Interceptors are organized into ‘stacks’
– lists of interceptors applied in sequence.
– applied to any action or package of actions
• WebWork is mostly implemented as a series of
XWork interceptors!
Timing Interceptor
• A simple invocation interceptor:
public class TimerInterceptor implements Interceptor {
...
public String intercept(ActionInvocation dispatcher) ...{
long startTime = System.currentTimeMillis();
String result = dispatcher.invoke();
long exTime = System.currentTimeMillis() - startTime;
log.info(dispatcher.getProxy().getActionName() + " ran in " + exTime +
"ms.");
return result;
}
}
Logging Interceptor
• A before/after processing interceptor:
public class LoggingInterceptor extends AbstractInterceptor {
...
protected void before(ActionInvocation invocation) ... {
log.info("Starting execution stack for action " +
invocation.getProxy().getActionName());
}
protected void after(ActionInvocation invocation, String result) ...{
log.info("Finishing execution stack for action " +
invocation.getProxy().getActionName());
}
}
Complex Interceptor
• Problem: notifying users of events within our
application via email
• Solution: an XWork interceptor + XML config file
• The interceptor:
– parses the config file (if not loaded yet)
– intercepts the action
– matches action class & result to determine if any email
needs to be sent
– if it does, processes a Velocity template (email body)
and sends it
Complex Interceptor - class
public class EventNotifierInt extends AbstractInterceptor {
...
protected void after(ActionInvocation actionInvocation, String result) . . . {
List listeners = getListenersFor(actionInvocation, result);
for (int i = 0; i < listeners.size(); i++) {
ConfEventListener l = (ConfEventListener)listeners.get(i);
l.onEvent( result, actionInvocation.getAction() );
}
}
private void loadXmlConfiguration() ...
private List getListenersFor(ActionInvocation invocation, String result) . . .
}
Validation Framework
• Validation of action properties
• Decoupled from actions
– validations stored in XML files
– error messages stored in actions, flow through
to UI components
• Pluggable validator classes
• Validation is implemented as an interceptor
– You control when validation happens
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 /
FieldExpression
Any OGNL expression evaluates to true
eg. pet.name != “dog”
Allows you to create very powerful validations using just XML
and your existing model
Example Validation
•
adduser-validation.xml
<validators>
<field name="username">
<field-validator type="requiredstring">
<message>Please specify a username.</message>
</field-validator>
</field>
<field name="confirm">
<field-validator type="fieldexpression">
<param name="expression">
confirm == null || password.equals(confirm)
</param>
<message key="passwords.dontmatch">no i18n msg!</message>
</field-validator>
</field>
</validators>
Validator Class
• checks that a String field is non-null and has a length > 0
public class RequiredStringValidator extends FieldValidatorSupport {
public void validate(Action action) throws ValidationException {
String fieldName = getFieldName();
Object value = this .getFieldValue(fieldName, action);
if (!(value instanceof String) ||
value == null ||
"".equals((String) value))
{
addFieldError(fieldName, action);
}
}
}
What is Inversion of Control?
• IoC removes the onus of managing
components from your business logic into a
container.
• Container manages lifecycles and
dependencies between components.
• EJB is IoC, but with a static list of services
– Security, persistence and transactions
• The Jakarta Avalon project is all about IoC.
Advantages of IoC
•
•
•
•
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/impl separation
• Unit tests become far simpler
– they become ‘mini-containers’
IoC in XWork
•
First off: IoC can be controversial - it is
optional! Use it if it suits you :)
•
XWork and WW provide a web-native IoC
architecture
Components specify only which services they
require
•
–
•
via interfaces (eg ShoppingCartAware)
Configuration file defines component
implementations and scopes.
Component Scopes
• WW has 4 component ‘scopes’ (lifetimes):
1.Application
2.HTTP Session
3.HTTP Request
4.XWork Action
• Let’s look at an example with 2 services…
IoC Example Service #1
•
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();
}
IoC Example Service #2
•
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);
}
IoC Example -Being serviced!
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;
}
}
IoC Example - Config
• These services are configured in components.xml
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>
Action Packaging
• A package is a JAR containing:
– Actions, views, interceptors, validators, i18n
properties and configuration
• Packages are:
– namespace aware
– hierarchical
• inherit capabilities from super packages
• Promotes componentisation
Packaging Configuration
• xwork.xml:
<xwork>
<package name="default">
...
</package>
<package name="subpackage" extends="default">
...
</package>
<include file="myotherpackage.xml" />
</xwork>
Struts vs WebWork
• Jakarta Struts is the 500-lb gorilla of the
MVC ‘space’
• No MVC presentation complete without a
Struts comparison
– I’ll try to be unbiased as possible :)
• Not an apples-for-apples comparison
– Think of it as a list of differences
WebWork Pros
• Simpler framework
– No more writing ‘junk code’ to fulfill Struts’ contracts
• No more actionbean/formbean classes
– Use simple model-driven actions and your own model
• Actions are easy to unit test
– Instantiate, call setters, run execute()
• WW ‘plays well with others’
– Multiple view technologies well supported
WebWork Pros
• Simpler views
– more powerful expression language
– no more pages with 1000’s of Struts tags
• No need to make actions thread safe
– One action instantiated per request
• Actions not coupled to the web
– Can be invoked remotely eg ClientDispatcher & RMI
• Struts has no interceptors, packages, IoC etc
WebWork Cons
• Not as well supported as Struts
– No books, tool support etc (changing slowly)
• Smaller community
• No action pooling yet
• Less standards support (eg JSTL & JSF)
– JSF support might come when spec released
– JSTL can be done by using tags
• WebWork not part of Jakarta :)
Want More?
• http://www.opensymphony.com for website,
mailing list, CVS etc
• http://wiki.opensymphony.com for WW2/XW
documentation
• My blog - http://blogs.atlassian.com/rebelutionary
• Email me - mike@atlassian.com
• One chapter of my upcoming book on real world
development with Java OSS technologies
</shameless-plug>
Thank you for listening - questions?
Mike Cannon-Brookes
ATLASSIAN - www.atlassian.com
with XDoclet,
JUnit,WebWork
& Hibernate
Download