620 likes | 839 Views
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
E N D
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
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.java public 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 • 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 Interceptor • Just 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 Interceptors • Setting Parameters • ParameterInterceptor • StaticParameterInterceptor • ChainingInterceptor • ConversionErrorInterceptor • FileUploadInterceptor • Defining Workflow • DefaultWorkflowInterceptor • PrepareInterceptor • ServletConfigInterceptor • ExecuteAndWaitInterceptor • Preventing duplicate posts • 2 types of token interceptors
Interceptor Stacks • Interceptors can be grouped into named Interceptor Stacks • Several defined in webwork-default.xml • 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
Model-Driven vs. Field-Driven • 2 types of Actions possible: • 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 • 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 ModelDriven • Make 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.jsp Hello <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 • 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
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
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
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 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
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 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
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 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 / WebWork • XWork provides a simple IoC container • WebWork provides a hierarchy of containers mapped to web scopes • Application • Session • Request • 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 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 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 Containers • The 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