Open Symphony Web Work Simple Powerful Web Application
Open. Symphony Web. Work Simple, Powerful Web Application Development
What is Open. Symphony? l An open source group encompassing quality enterprise Java components l Many well known quality components • • Web. Work / XWork Site. Mesh OSCache OSUser OSWorkflow Quartz Test. NG OGNL l http: //www. opensymphony. com
What is Web. Work? l The second generation of Web. Work l A web MVC framework l A wrapper on top of a generic Command Pattern implementation (XWork) l A tag library that encourage componentization and reuse
How does Web. Work use XWork? l XWork is a generic command pattern implementation l Web. Work translates between the web world and XWork
What does XWork provide? l Command pattern implementation • Actions are command objects in XWork l Adds advanced features • Interceptors • Results • • Simple Io. C (or “Dependency Injection”) container Powerful expression language – OGNL Flexible type conversion Metadata driven validation framework § Includes setting parameters, workflow, etc § Includes one for chaining to another Action
What does Web. Work add? l Adapter for HTTP request / response l Integration of Session / Application scopes l Servlet. Dispatcher translates HTTP requests into Action execution l Request parameters passed to Action l Results for servlet redirect & dispatch, Velocity, Freemarker, Jasper. Reports, XSLT, etc.
Results: The “View” in MVC l Results are what happens after an Action l Displaying a page -> JSP / Velocity / Free. Marker / Jasper. Reports / XSLT l Chaining to another Action l Add your own • Email? Command line output?
Actions l Actions are command objects l Actions should be simple! l Actions are not tied to any web classes l Action interface has only one method: interface Action { String execute() throws Exception; }
Action. Support l Actions are only required to implement the Action interface l Action. Support is a useful base class • • Implements Action Provides error message support • Provides message internationalization • All features based on Interfaces, so you can implement your own from scratch! § Field and Action level messages § Messages automatically used by UI tags § Message bundle for each Action § Looks up class hierarchy § UI tags use internationalization support to find text
A “Hello World” example l 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 l Shows • • • Implementing an Action Configuring Web. Work Using the taglib Type conversion Error messages
Hello. World. Action. java public class Hello. World. Action extends Action. Support { private User user; public User get. User() { return user; } public int get. Days. Till. Next. Birthday() { … } public String execute() throws Exception { String name = get. User(). get. Name(); if ((name == null) || (name. trim(). equals(""))) { add. Field. Error("user. name", "You must enter a name. "); } if (has. Errors()) { return INPUT; } return SUCCESS; }
User. java public class User { private String name; private Date birthday; public String get. Name() { return name; } public void set. Name(String name) { this. name = name; } public Date get. Birthday() { return birthday; } public void set. Birthday(Date birthday) { this. birthday = birthday; } }
form. jsp <%@ taglib uri= "webwork" prefix= "webwork" %> <html> <head><title>Hello World Example</title></head> <body> <ww: form action="Hello. World"> <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="days. Till. Next. Birthday"/> 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="ex 1" extends="default" namespace="/ex 1"> <action name="Hello. World" class="example. ex 1. Hello. World. Action"> <interceptor-ref name="default. Stack"/> <result>/field/success. jsp</result> <result name="input" type="dispatcher">/field/form. jsp</result> </action> </package> </xwork>
Unit testing Actions l XWork / Web. Work shines in testability l Actions have no web dependencies, so you don’t have to set up mocks, etc. l 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 Xwork. Test. Case base class which defaults a lot of set up
Hello. World. Action. Test public void test. Field. Error. Added. When. No. User. Name() throws Exception { Hello. World. Action action = new Hello. World. Action(); assert. Equals(Action. INPUT, action. execute()); assert. True(action. has. Field. Errors()); Map field. Errors = action. get. Field. Errors(); assert. True(field. Errors. contains. Key("user. name")); List user. Name. Errors = (List) field. Errors. get("user. name"); assert. Equals(1, user. Name. Errors. size()); assert. Equals("You must enter a name. ", user. Name. Errors. get(0)); }
Notes on the example l Compose page model from many objects using expression language l UI tags automatically show field error messages next to the form field l Return code from Action determines which page to display l Much of the “magic” is in the interceptors…
Interceptors: Domain AOP l Interceptors allow custom code into the call stack l Much of the core functionality of XWork and Web. Work is implemented as Interceptors l Add custom Interceptors
Timer. Interceptor l Timer. Interceptor is the simplest Interceptor l Just times the execution of the Action public String intercept(Action. Invocation invocation) throws Exception { if (log. is. Info. Enabled()) { long start. Time = System. current. Time. Millis(); String result = invocation. invoke(); long execution. Time = System. current. Time. Millis() - start. Time; String namespace = invocation. get. Proxy(). get. Namespace(); … }
Logging. Interceptor l Logging. Interceptor extends the Around. Interceptor l Around. Interceptor provides callbacks for before() and after() the Action is executed public class Logging. Interceptor extends Around. Interceptor { protected void after(Action. Invocation invocation, String result) throws Exception { log. Message(invocation, FINISH_MESSAGE); } protected void before(Action. Invocation invocation) throws Exception { log. Message(invocation, START_MESSAGE); } }
Other Interceptors l Setting Parameters • • • Parameter. Interceptor Static. Parameter. Interceptor Chaining. Interceptor Conversion. Error. Interceptor File. Upload. Interceptor l Defining Workflow • • Default. Workflow. Interceptor Prepare. Interceptor Servlet. Config. Interceptor Execute. And. Wait. Interceptor l Preventing duplicate posts • 2 types of token interceptors
Interceptor Stacks l Interceptors can be grouped into named Interceptor Stacks l Several defined in webwork-default. xml l default. Stack <interceptor-stack name="default. Stack"> <interceptor-ref name="static-params"/> <interceptor-ref name="conversion. Error"/> </interceptor-stack> l validation. Workflow. Stack <interceptor-stack name="validation. Workflow. Stack"> <interceptor-ref name="default. Stack"/> <interceptor-ref name="validation"/> <interceptor-ref name="workflow"/> </interceptor-stack> l Stacks can be built of other stacks and interceptors
Model-Driven vs. Field-Driven l 2 types of Actions possible: 1. Model-driven Action has methods returning your model classes (my. Action. get. User()) § 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=‘days. Till. Next. Birthday’) § Useful where form is not parallel to model § 2. l As we can see in our Action, the two can be mixed
Model. Driven Interface l XWork / Web. Work also supports modeldriven Actions more directly l The Model. Driven Interface has one method: public Object get. Model() l Properties of the model will be directly available, i. e. “name” instead of “user. name” l Applies to UI tags, form field names, etc.
Making Hello. World Model. Driven l Make the class implement Model. Driven l Change get. User() to get. Model() public class Hello. World. Action extends Action. Support implements Model. Driven { … public Object get. Model() { return user; } }
Model. Driven: Changes to the JSP l. Change “user. name”, etc. to just “name” l. In form. jsp <ww: textfield label="Name" name="name"/> <ww: textfield label="Birthday" name="birthday"/> l. In success. jsp Hello <ww: property value="name"/>! <ww: if test="birthday != null"> <ww: property value="days. Till. Next. Birthday"/> days till your next birthday. </ww: if>
Applying the Model. Driven. Interceptor l. In xwork. xml <package name="ex 2" extends="webwork-default" namespace="/ex 2"> <action name="main" class="com. opensymphony. xwork. Action. Support"> <result>/model/form. jsp</result> </action> <action name="Hello. World" class="example. ex 2. Hello. World. Action"> <interceptor-ref name="model-driven"/> <interceptor-ref name="default. Stack"/> <result>/model/success. jsp</result> <result name="input">/model/form. jsp</result> </action> </package>
Looking at the Model. Interceptor l The Model. Interceptor pushes the Model onto the Value. Stack
What is the Value. Stack? l The Value. Stack builds a stack of objects l Objects are used to find property values l The Value. Stack allows the expression language to find property values across multiple objects
How is the Value. Stack used? l The Action instance is always pushed onto the Value. Stack l The Model is pushed on by the Model. Interceptor l 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 Value. Stack All tag attribute values are evaluated against the stack when being set onto the tag instances
The OGNL expression language l 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, Value. Stack, *. xml Independently run Open Source project http: //www. ognl. org
OGNL samples OGNL Result user. name get. User(). get. Name() user. to. String() get. User(). to. String() 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 get. Name() on each Category in the collection, returning a new collection (projection)
The XWork Validation Framework l Separates validation from Action classes l Allows for different validations in different contexts for the same object l Provides hooks for localized validation messages l 2 types of validators, Object level and field level
Hello. World. Action-validation. xml <validators> <field name="user. name"> <field-validator type="requiredstring"> <message>You must enter a name. </message> </field-validator> </field> </validators> l Validation file in the same package as the class l Defines one field validator and the error message to add if it fails
Bundled Validators Validator Required. Field Result field != null Required. String field != null && string. length() > 0 Int. Range Integer in a given range Date. Range Date in a given range Email Valid email field URL Valid URL field Expression / Any OGNL expression evaluates to true Field. Expressio eg. pet. name != “dog” n Allows you to create very powerful validations using just XML and your existing model
Changes to xwork. xml <package name="ex 3" extends="default" namespace="/ex 3"> <action name="Hello. World" class="example. ex 3. Hello. World. Action"> <interceptor-ref name="validation. Workflow. Stack"/> <result name="success" type="dispatcher">/field/success. jsp</result> <result name="input" type="dispatcher">/field/form. jsp</result> </action> </package> l validation. Workflow. Stack is from webwork-default. xml <interceptor-stack name="validation. Workflow. Stack"> <interceptor-ref name="default. Stack"/> <interceptor-ref name="validation"/> <interceptor-ref name="workflow"/> </interceptor-stack>
Changes to the Action l The execute() method can just return SUCCESS public String execute() throws Exception { return SUCCESS; } l The validation of the “user. name” property is handled by the validation interceptor l The workflow interceptor returns INPUT if there any errors added to the Action
Client-Side Java. Script Validation l Xml. Http. Request validation on the server side l Doesn’t require two versions of each validator – all the actual validation logic happens on the server side l Provides near-instant validation feedback, just like normal Java. Script validation
Client-Side Java. Script Validation Flow l Invoked using on. Blur and on. Change events l Asynchronous Javascript Xml. Http. Request call to the server l Uses server-side validations and returns results to the client l Any validation errors are rendered using DHTML and Java. Script
What is Inversion of Control? l Io. C removes the onus of managing components from your business logic into a container. l Container manages lifecycles and dependencies between components. l EJB is Io. C, but with a static list of services • Security, persistence and transactions l Jakarta Avalon, Spring’s Bean. Factory, and Pico. Container are all Io. C containers
Advantages of Io. C l Promotes simplicity and decoupling l Components describe themselves l Dependencies are discovered automatically l Adheres to Law of Demeter • • Classes coupled to only what they use Encourages smaller responsibility classes l Leads to better interface/impl separation l Unit tests become far simpler • they become ‘mini-containers’
Io. C in XWork / Web. Work l XWork provides a simple Io. C container l Web. Work provides a hierarchy of containers mapped to web scopes 1. Application 2. Session 3. Request 4. XWork Action l Components specify only which services they require • via interfaces (eg Shopping. Cart. Aware) l Configuration file defines component implementations and scopes
A Shopping. Cart Service l A Shopping. Cart service - provides a user’s cart (session scoped) Shopping. Cart. Aware. java: public interface Shopping. Cart. Aware { public void set. Shopping. Cart(Shopping. Cart cart); } Shopping. Cart. java: public interface Shopping. Cart { public void add. Pet(Pet pet); public void remove. Pet(Pet pet); public boolean is. Empty(); public int size(); public List get. Pets(); }
A Pet. Store Service l A Pet. Store service - provides management of our pet inventory (application scoped) Pet. Store. Aware. java: public interface Pet. Store. Aware { public void set. Pet. Store(Pet. Store store); } Pet. Store. java: public interface Pet. Store { void save. Pet(Pet pet); void remove. Pet(Pet pet); List get. Pets(); Pet get. Pet( long id); }
A service client Action public class Add. To. Cart implements Action, Pet. Store. Aware, Shopping. Cart. Aware {. . . public void set. Pet. Store(Pet. Store ps) { this. pet. Store = ps; } public void set. Shopping. Cart(Shopping. Cart c) { this. cart = c; } public String execute() throws Exception { if (cart == null || pet. Id == 0) return ERROR; Pet pet = pet. Store. get. Pet(pet. Id); cart. add. Pet(pet); return SUCCESS; } }
Configuring Services l These services are configured in components. xml like so: <components> <component> <scope>application</scope> <class>org. petsoar. pets. Default. Pet. Store</class> <enabler>org. petsoar. pets. Pet. Store. Aware</enabler> </component> <scope>session</scope> <class>org. petsoar. cart. Simple. Shopping. Cart</class> <enabler>org. petsoar. cart. Shopping. Cart. Aware</enabler> </components>
Integrating with other Io. C Containers l The Xwork-optional project includes integrations and extension projects l Xwork-Spring provides integration with Spring’s Io. C Container • Actions, Interceptors, Results, etc. can be autowired or configured in Spring’s application context l Integrations exist with other Io. C Containers, including Pico/Nano. Container
Achieving reuse with Web. Work l Web. Work 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 Io. C Create Action base classes Create reusable UI components Create reusable application modules
Reusable UI components l The Web. Work UI tags are implemented using Velocity templates l You can provide your own templates for the tags l The <ww: component> tag lets you use any template l Examples • • A date picker template Error message template
UI tag rendering l 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. template. Dir/xhtml/controlheader. vm") #parse("/$parameters. template. Dir/simple/text. vm") #parse("/$parameters. template. Dir/xhtml/controlfooter. vm")
UI tag templates l Web. Work 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 l WW allows you to easily create custom UI components l Requires writing a single Velocity template l Excellent for componentizing views l Example: a date picker to allow users to enter dates into text fields easily…
A custom date picker (from Jira) l Here’s the form field and popup:
Using the custom UI component l View (addpet. jsp): <ww: component label="Created After" template="datepicker. vm" name="pet. created"> <ww: param name="formname" value="editform" /> </ww: component> l Component template (datepicker. vm) #parse( "/decorators/xhtml/controlheader. vm" ) <script language="Java. Script" src="/decorators/datepicker. js" /> <input type="text" name="${tag. Name}" value="$!{tag. Actual. Value}" /> <a href="javascript: show_calendar('${tag. Params. get("formname")}', '${tag. Name }'); "><img src="/images/icons/cal. gif"></a> #parse( "/decorators/xhtml/controlfooter. vm" ) l You can also extend the component tag like the other UI tags do create new UI tags
Reusable application modules l You can build reusable modules which can be included in any Web. Work app l Xwork. xml include allows you to merge in external configuration l Velocity allows you to include both Actions and templates in a jar file l Example: The Web. Work configuration browser
Adding the Config Browser to your Web. Work app l Add the webwork-config-browser. jar (12 Kb) to your WEB-INF/lib l Add an include in your xwork. xml <xwork> <include file="webwork-default. xml"/> <include file="config-browser. xml"/> … </xwork> l Add a velocity. properties file under WEB-INF • velocimacro. library = webwork. vm, tigris-macros. vm l That’s it!
The Config Browser in action
What’s coming in 2. 2? l IDE plugins for Eclipse and Intellij IDEA l AJAX component theme for the JSP tags l JSR-168 Portlet support l Improved configuration API – Much better runtime action configuration! l Even better collections and Map support
What’s coming in 2. 2? (continued) l Improved productivity: better error reporting, configuration problem reporting l Improved client-side validation calling the server with DWR l Annotation support for validations l Request parameter filtering for security
For More Information l For release downloads, see the java. net project sites: http: //xwork. dev. java. net http: //webwork. dev. java. net l Check out the project documentation on the Open. Symphony wiki: http: //wiki. opensymphony. com/space/XWork http: //wiki. opensymphony. com/space/Web. Work 2 l For project issue tracking, see: http: //jira. opensymphony. com/secure/Dashboard. jspa l The project mailing list is available at dev@webwork. dev. java. net And is synchronized with the Opensymphony Forums: http: //forums. opensymphony. com l Questions?
Fill out your Evaluations!
- Slides: 62