opensymphony webwork
Post on 08-Jan-2016
52 Views
Preview:
DESCRIPTION
TRANSCRIPT
OpenSymphony WebWork
Simple, Powerful Web Application Development
What is OpenSymphony?
An open source group encompassing quality enterprise Java components
Many well known quality components• WebWork / XWork• SiteMesh• OSCache • OSUser • OSWorkflow • Quartz• TestNG• OGNL
http://www.opensymphony.com
What is WebWork?
The second generation of WebWork
A web MVC framework
A wrapper on top of a generic Command
Pattern implementation (XWork)
A tag library that encourage componentization
and reuse
How does WebWork use XWork?
XWork is a generic command
pattern implementation
WebWork translates between
the web world and XWork
WebWork
XWork
HTTP
ServletDispatcher
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
• Simple IoC (or “Dependency Injection”) container• 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
Results for servlet redirect & dispatch,
Velocity, Freemarker, JasperReports, XSLT, etc.
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
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 “Hello World” example
Simple requirements• Take the users name and generate a hello
message personalized for them• If the user enters a birthday, calculate how long
until their birthday
Shows• Implementing an Action• Configuring WebWork• Using the taglib• Type conversion• Error messages
HelloWorldAction.javapublic class HelloWorldAction 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; }
}
form.jsp
<%@ taglib uri= "webwork" prefix= "webwork" %>
<html>
<head><title>Hello World Example</title></head>
<body>
<ww:form action="HelloWorld">
<ww:textfield label="Name" name="user.name"/>
<ww:textfield label="Birthday" name="user.birthday"/>
<ww:submit value="Say Hello"/>
</ww:form>
</body>
</html>
success.jsp
<%@ taglib uri= "webwork" prefix= "webwork" %>
<html>
<head><title>Hello <ww:property value="user.name"/></title></head>
<body>
Hello <ww:property value="user.name"/>!
<ww:if test="user.birthday != null">
<ww:property value="daysTillNextBirthday"/> days till your next birthday.
</ww:if>
</body>
</html>
Xwork.xml for Hello World<xwork>
<include file="webwork-default.xml"/>
<package name="default" extends="webwork-default" abstract="true">
<action name="main">
<result>/field/form.jsp</result>
</action>
</package>
<package name="ex1" extends="default" namespace="/ex1">
<action name="HelloWorld" class="example.ex1.HelloWorldAction">
<interceptor-ref name="defaultStack"/>
<result>/field/success.jsp</result>
<result name="input" type="dispatcher">/field/form.jsp</result>
</action>
</package>
</xwork>
Unit testing Actions
XWork / WebWork shines in testability
Actions have no web dependencies, so you don’t have to set up mocks, etc.
3 ways of testing Actions• Just create a new instance, set some properties, and
execute
• Use the framework directly in your tests to execute it with the Interceptors, etc.
• Extend the XworkTestCase base class which defaults a lot of set up
HelloWorldActionTest
public void testFieldErrorAddedWhenNoUserName()
throws Exception {
HelloWorldAction action = new HelloWorldAction();
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 interceptors…
Interceptors: Domain AOP
XWork
InterceptorInterceptor
Interceptor
Action
Result
Interceptors allow custom code into the call stack
Much of the core functionality of XWork and WebWork is implemented as Interceptors
Add custom Interceptors
TimerInterceptor
TimerInterceptor is the simplest InterceptorJust 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 LoggingInterceptor extends the AroundInterceptor AroundInterceptor provides callbacks for before() and
after() the Action is executed
public class LoggingInterceptor extends AroundInterceptor {
protected void after(ActionInvocation invocation, String result) throws Exception {
logMessage(invocation, FINISH_MESSAGE);
}
protected void before(ActionInvocation invocation) throws Exception {
logMessage(invocation, START_MESSAGE);
}
}
Other InterceptorsSetting Parameters• ParameterInterceptor
• StaticParameterInterceptor
• ChainingInterceptor
• ConversionErrorInterceptor
• FileUploadInterceptor
Defining Workflow• DefaultWorkflowInterceptor
• PrepareInterceptor
• ServletConfigInterceptor
• ExecuteAndWaitInterceptor
Preventing duplicate posts• 2 types of token interceptors
Interceptor StacksInterceptors can be grouped into named
Interceptor StacksSeveral defined in webwork-default.xmldefaultStack
<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
Model-Driven vs. Field-Driven2 types of Actions possible:
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
As we can see in our Action, the two can be mixed
ModelDriven Interface
XWork / WebWork also supports model-
driven Actions more directly
The ModelDriven Interface has one method:
public Object getModel()
Properties of the model will be directly
available, i.e. “name” instead of
“user.name”
Applies to UI tags, form field names, etc.
Making HelloWorld ModelDrivenMake the class implement ModelDriven
Change getUser() to getModel()
public class HelloWorldAction extends ActionSupport implements ModelDriven {
…
public Object getModel() {
return user;
}
}
ModelDriven: Changes to the JSP
Change “user.name”, etc. to just “name”
In form.jsp<ww:textfield label="Name" name="name"/><ww:textfield label="Birthday" name="birthday"/>
In success.jspHello <ww:property value="name"/>! <ww:if test="birthday != null"> <ww:property value="daysTillNextBirthday"/> days till your next birthday.
</ww:if>
Applying the ModelDrivenInterceptor
In xwork.xml<package name="ex2" extends="webwork-default"
namespace="/ex2">
<action name="main" class="com.opensymphony.xwork.ActionSupport">
<result>/model/form.jsp</result>
</action>
<action name="HelloWorld" class="example.ex2.HelloWorldAction">
<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 ModelInterceptor
Model
ValueStack
Action
Push
ValueStack
Action
Model
The ModelInterceptor pushes the Model onto
the ValueStack
What is the ValueStack?
The ValueStack builds a
stack of objects
Objects are used to find
property values
The ValueStack allows
the expression language
to find property values
across multiple objects
ValueStack
OG
NL Ex
pressio
n
Find P
roper
ties
Find P
roper
ties
Model
Action
How is the ValueStack used?
The Action instance is always pushed onto the ValueStack
The Model is pushed on by the ModelInterceptor
The UI tags use it to push values on during their scope and evaluate expressions• The <ww:iterator> tag pushes the current item
onto the stack• The <ww:bean> tag pushes a bean instance on• The <ww:property> tag evaluates an expression
against the ValueStack• All tag attribute values are evaluated against the
stack when being set onto the tag instances
The OGNL expression language
For expressions WW 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• Independently run 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)
The XWork Validation Framework
Separates validation from Action classes
Allows for different validations in different contexts for the same object
Provides hooks for localized validation messages
2 types of validators, Object level and 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 /
FieldExpressio
n
Any OGNL expression evaluates to true
eg. pet.name != “dog”
Allows you to create very powerful
validations using just XML and your
existing model
Changes to xwork.xml<package name="ex3" extends="default" namespace="/ex3">
<action name="HelloWorld" class="example.ex3.HelloWorldAction">
<interceptor-ref name="validationWorkflowStack"/>
<result name="success" type="dispatcher">/field/success.jsp</result>
<result name="input" type="dispatcher">/field/form.jsp</result>
</action>
</package>
validationWorkflowStack is from webwork-default.xml
<interceptor-stack name="validationWorkflowStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="validation"/>
<interceptor-ref name="workflow"/>
</interceptor-stack>
Changes to the ActionThe 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
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
Client-Side JavaScript Validation FlowInvoked 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
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
Jakarta Avalon, Spring’s BeanFactory, and PicoContainer are all IoC containers
Advantages of IoCPromotes 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 / WebWork XWork provides a simple IoC container WebWork provides a hierarchy of
containers mapped to web scopes 1.Application2.Session3.Request4.XWork Action
Components specify only which services they require• via interfaces (eg ShoppingCartAware)
Configuration file defines component implementations and scopes
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 Actionpublic 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 ServicesThese 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>
Integrating with other IoC ContainersThe Xwork-optional project includes
integrations and extension projects
Xwork-Spring provides integration with
Spring’s IoC Container
• Actions, Interceptors, Results, etc. can be auto-
wired or configured in Spring’s application context
Integrations exist with other IoC Containers,
including Pico/NanoContainer
Achieving reuse with WebWork
WebWork 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 WebWork UI tags are implemented using Velocity templates
You can provide your own templates for the tags
The <ww:component> tag lets you use any template
Examples• A date picker template• Error message template
UI tag rendering
UI tags use Velocity to actually render HTML fragments, eg in your JSP view:
<ww:textfield label=“Name" name=“project.name" />
renders via textfield.vm:
#parse("/$parameters.templateDir/xhtml/controlheader.vm")
#parse("/$parameters.templateDir/simple/text.vm")
#parse("/$parameters.templateDir/xhtml/controlfooter.vm")
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
WW allows you to easily create custom UI
components
Requires writing a single Velocity 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 componentView (addpet.jsp):<ww:component label="Created After" template="datepicker.vm"
name="pet.created">
<ww:param name="formname" value="editform" />
</ww: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" )
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 WebWork app
Xwork.xml include allows you to merge in external
configuration
Velocity allows you to include both Actions and
templates in a jar file
Example: The WebWork configuration browser
Adding the Config Browser to your WebWork app
Add the webwork-config-browser.jar (12 Kb) to your WEB-INF/lib
Add an include in your xwork.xml
<xwork>
<include file="webwork-default.xml"/>
<include file="config-browser.xml"/>
…
</xwork>Add a velocity.properties file under WEB-INF• velocimacro.library = webwork.vm, tigris-macros.vm
That’s it!
The Config Browser in action
What’s coming in 2.2?
IDE plugins for Eclipse and Intellij IDEA
AJAX component theme for the JSP tags
JSR-168 Portlet support
Improved configuration API – Much better
runtime action configuration!
Even better collections and Map support
What’s coming in 2.2? (continued)
Improved productivity: better error
reporting, configuration problem reporting
Improved client-side validation calling the
server with DWR
Annotation support for validations
Request parameter filtering for security
For More Information For release downloads, see the java.net project sites:
http://xwork.dev.java.nethttp://webwork.dev.java.net
Check out the project documentation on the OpenSymphony wiki:http://wiki.opensymphony.com/space/XWorkhttp://wiki.opensymphony.com/space/WebWork2
For project issue tracking, see:http://jira.opensymphony.com/secure/Dashboard.jspa
The project mailing list is available atdev@webwork.dev.java.net
And is synchronized with the Opensymphony Forums:http://forums.opensymphony.com
Questions?
Fill out your Evaluations!
top related