webwork 2 “strutting the opensymphony way” prepared by mike cannon-brookes - june, 2003...

52
WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 [email protected] - http://www.atlassian.com

Upload: rafe-allen

Post on 15-Jan-2016

215 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

WEBWORK 2“Strutting the OpenSymphony way”

Prepared by Mike Cannon-Brookes - June, 2003

[email protected] - http://www.atlassian.com

Page 2: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

Agenda

• WebWork 2 & XWork overview

• Actions

• Views

• Interceptors

• Validation

• Inversion of Control (IoC)

• Struts Comparison

Page 3: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 4: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 5: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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!

Page 6: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 7: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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…

Page 8: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

Basic Example: Add Pet

Use case - we want to add a Pet to our system:

Page 9: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 10: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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) …

}

Page 11: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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;

} }

Page 12: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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>

Page 13: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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>

Page 14: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 15: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 16: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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] ...}

Page 17: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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.

Page 18: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 19: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 20: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 21: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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)

Page 22: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 23: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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")

Page 24: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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:

Page 25: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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" )

Page 26: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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…

Page 27: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

Custom component example

• Here’s the form field and popup:

Page 28: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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" )

Page 29: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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!

Page 30: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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;

} }

Page 31: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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());

}

}

Page 32: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 33: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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) . . .

}

Page 34: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 35: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 36: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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>

Page 37: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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);

} }

}

Page 38: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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.

Page 39: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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’

Page 40: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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.

Page 41: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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…

Page 42: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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();

}

Page 43: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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);

}

Page 44: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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;

} }

Page 45: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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>

Page 46: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 47: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

Packaging Configuration

• xwork.xml:

<xwork><package name="default">. . .</package>

<package name="subpackage" extends="default">. . .</package>

<include file="myotherpackage.xml" /></xwork>

Page 48: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 49: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 50: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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

Page 51: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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 :)

Page 52: WEBWORK 2 “Strutting the OpenSymphony way” Prepared by Mike Cannon-Brookes - June, 2003 mike@atlassian.commike@atlassian.com - :

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 - [email protected] • 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