webwork 2 “strutting the opensymphony way” prepared by mike cannon-brookes - june, 2003...
Post on 15-Jan-2016
215 Views
Preview:
TRANSCRIPT
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?
XWork 1
WebWork 1
WebWork 2 Web
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: Pet.java (simple bean)– Controller: AddPet.java (WW action)– View: addpet.jsp– Config: 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 trueeg. 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-BrookesATLASSIAN - www.atlassian.com
with XDoclet, JUnit,WebWork & Hibernate
top related