Introduction
Other Articles In The Framework Series Introduction To Maverick Introducture The Spring Framework Keel: The Next Generation Meta-Framework
WebWork is a Model 2 MVC web framework created by the OpenSymphony team which includes folks like Jason Carreira, Pat Lightbody, Mike Cannon-Brookes, Hani Suleiman and many more. Having already gained considerable reputation with their current 1.x release the team broke out AND HAS BEEN WORKING ON A 2.x Version Which Already HAS A Beta Release Out.
This article will take you through the development of the wafer weblog application using the WebWork 2.x release and will cover many of the basic features of WW2. Last time I wrote for TheServerSide it was about how to build the Wafer Weblog with the Maverick web framework. I choose Maverick because it was considered "lighter than Struts" and after building wafer I agreed and was very pleased. This time I chose WebWork2 mainly to learn about all the hype it was getting. I was curious to know what's all the hype WITH IOC, Interceptors, etc. and this time I was even more impressed.
The Executive Summary Of Webwork2
WebWork2 is a Model 2 MVC web framework that leverages the cutting edge solution strategies of IoC, Interceptors and the OGNL expression language. It is built with interfaces instead of abstract classes, allowing you to implement your solution loosely coupled to the framework yet strongly leveraging it ITS Independence from the J2EE Package Allows your solution to integrate smoothly int. Solustration as never before possible.
What's Different in The Webwork2 Beta Release?
Quite a bit actually. The core MVC component has been pulled out of WebWork and moved into a separate project called Xwork, now coined as a generic command pattern framework independent of the web. They also added Interceptors which allow you to take all of that pre and post processing you were doing in Action class hierarchies and pull them out into separate classes independent of Actions;. however, you still have access to them and their environment UI validation is now via done an XML file instead of in the Action class; however , That Feature Still EXISTS if you need it. and the've added the OGNL Expression Language and Ioc.Getting Started
Let's start by setting up your project. As easy as this sounds it's sometimes not that simple. Knowing which files you need, the correct directory structure to use and what Ant modifications will be required are always a challenge when starting with a new framework. Unfortunately WW2 does not provide much help with this but neither do other frameworks so the easiest way to get moving is to create a J2EE structure much like I did for the wafer weblog application. At the time of writing this article a neat tool called megg was released to do just that, create a WW2 project with almost everything you need to get started, like a complete project directory structure for src, config, jsp, html, lib and more. It even goes as far as creating a simple HelloWorld app with A JUnit Test And Has A Fully Functional Ant Build File Already Tailored to your Web Application's name!
CONFIGURATION
.. The first place to start for those familiar with Struts would be the configuration piece Like many other frameworks WW2 has an xml file to control the flow of actions and outputs Below is a portion of the wafer weblog xwork.xml:
The format is fairly clear. The action name is the reference name for that action and is most likely the name you will also use in your URL / HTML like /HelloWorld.do. The result defines what type of view this action should forward on to and it has a name and a type. The name (input, success, error, etc.) needs to map to your Action return value and the type field defines what type of View this action is going to, see below for the current list Of results Types.
Dispatcher - JSP Routing Redirect - Forward Redirecting Velocity - for a Velocity Templating View Chain - for Routing To Another Action Class
If you need to create a unique result type such as Excel output you can easily create your own result type by implementing the Result interface, adding the implementation code and adding the result to the list of possible result types in the webwork-default.xml file .
Interceptors
The next logical piece to discuss are the Action classes; however, it's almost impossible to continue without explaining the concept of Interceptors Most of all the work that is done in done via interceptors WW2 is Personally I find the implementation of interceptors to be one.. of the core unique factors in what separates WW2 from the rest of the frameworks out there. On the surface interceptors are much like filters. Others have compared them to "Practical AOP". Regardless of the name, the feature is very nice.Interceptors can be called before the action is invoked, after the action or both before and after. They have full access to the Action class and the environment at runtime allowing you to either call methods on the Action class or work with the environment of the Action class. A Great Example of Both Would Be The Timerinterceptor. Before The Action Is Invoked It Takes A TimeStamp, Then, After The Completion of Action It Gets Another TimeStamp and Calculates The Length of Time IT Took to Run. Poor man's profile!
Another joy of the interceptors is the ease at which you can configure the interceptor stack. Every
Create Your Own Interceptor
Enough talk, let's create one! All Interceptors must implement the Interceptor interface which basically has 3 methods, init, destroy and intercept. For my needs I extended the AroundInterceptor which allows me to call a before and after method. Let's create an Interceptor that requires The user to be logged in to success do this by checking to see letter before object is in the session before the action is executed and if the object is not found the user issent to the login.
public class AuthorizeInterceptor extends AroundInterceptor {private static final Log log = LogFactory.getLog (LoggingInterceptor.class); private boolean loggedIn = false; protected void before (ActionInvocation invocation) throws Exception {User u = null; ActionContext ctx = ActionContext.getContext () Map session = ctx.getSession (); u = (user) session.get ("user"); if (u == null) {log.info ("user not logged in"); loggedin = false;} else loggedIn = true;} protected void after (ActionInvocation invocation, String result) throws Exception {log.info ( "After");} public String intercept (ActionInvocation invocation) throws Exception {before (invocation); if (loggedIn == false) return action.LOGIN; // send em back to the login screen else {String result = invocation.invoke (); after (invocation, result); return result;}}} The before method is called before the execute is invoked allowing me To check if the user has logged ONTO this session and if not Then return the global value to log in, "Login", Which is define in
Now you have an AuthorizationInterceptor. For all of your action classes that you want to hide behind a login just reference this interceptor in your action config, as shown below, and you now have basic Action security.
Pitfalls to Interceptors
I love interceptors but it was also one of the hardest pieces to operate successfully right out of the gate. The problem is that some interceptors have dependencies on other interceptors to work so making sure that you have the correct interceptors assigned to your action class AND that they are in the correct order is crucial. For the sake of simplicity I only referenced the one standard interceptor stack called defaultStack. All other interceptors I referenced by name in the
Action Classes
. Actions, Controllers, Commands are pieces the developers deal with the most In the WebWork world they are called Actions and there are basically two types of them:. Field Driven and Model Driven Think of the Field Driven Actions as Controller-as-Model style ;. these are probably the best choice for one-off pages with very small models Most of the wafer web log application was done using this model.The other type is Model Driven This is where the model is its own POJO This style is.. better for larger models and promotes better code reuse. to define an Action class, regardless of the above mentioned types, you have to either extend ActionSupport or implement Action. In the development of my app I chose extension of ActionSupport because of all the helper features IT Has Like Ui Error Handling and Logging. Let's Start With A Field Driven Action, Registeraction.java
public class RegisterAction extends ActionSupport {String username, email, firstname, lastname, password; private User tempUser; public String getUserName () {return username;} public void setUserName (String username) {this.username = username;} public String getEmail ( ) {return email;} public void setEmail (String email) {this.email = email;} public String getFirstName () {return firstname;} public void setFirstName (String firstname) {this.firstname = firstname;} public String getLastName ( ) {return lastname;} public void setLastName (String lastname) {this.lastname = lastname;} public String getPassword () {return password;} public void setPassword (String password) {this.password = password;} public String execute ( ) throws Exception {if (hasErrors ()) return INPUT; else {tempUser = WebLogSystem.getUserStore (.) create (this.username, this.password); tempUser.setFirstName (this.getFirstName ()); tempUser.setLastName (this .GetlastName ()); tempuser.setemail (this.Getemail ()); TE mpUser.save (); return SUCCESS;.}} / ** * Do business logic validation by checking to see if the user entered is * already in the database * / public void validate () {LOG.info ( "Validating the registration "); try {if (WebLogsystem.getusersTore (). verify (this.username)) {this.addfielderror (" UserName "," Someone Already Has That Name);}} Catch (Exception E) {E.PrintStackTrace ();..}}} The one method you must implement is execute () Execute is called every time your action is invoked and it has a String return value There are default values defined in the Action Interface which are success, input, none And Error and these Values Map Directly to the Result Name Fields in the xwork.xml file.
ValidationWW2 has both UI and data validation. UI validation basically checks to see that your value types and ranges are correct for a field. For example, a number field is really a number or a date is really a valid date, etc. Data validation is when you need to check if the value given is valid from say a list of possible choices, maybe requiring a database lookup. This type of validation would be used on say US zip code checking where checking that a number simply falls within a given range isn 't Enough, and you need to check a list of valid zip cots to see if it is really valid.
User Interface Validation
THIS TYPE OF VALIDATION IS DONE IN AN XML File Which You Define In The Same Package As The Action Class By Calling IT
WW2 comes with many field validators like date, email, int, string, etc. and all valid Validators are defined in the validator.xml file; however, if you have a unique need, it is very easy to create your own field validator by implementing the validator interface and adding the reference to the validator.xml file. UI validation is called with the "validation" interceptor, so to turn on validation you also must reference the "validation" interceptor in your action configuration much like the RegisterAction has in The xwork.xml for Wafer Weblog.Data Validation
Data validation most likely involves you the programmer to write some code to check a scenario like "Is this a valid zip code, let me query the database." To do this create a no args method in your Action class called validate and put all of your functionality there. Then implement Validatable, referencing the "workflow" interceptor. This interceptor will call the validate method in the Action class before the execute is called, allowing you to add any errors to the context as needed.
Inversion of Control
IoC is a design pattern that favors loose coupling between classes / components. Currently, when you code you have some classes that depend on others to operate and those dependencies are coupled closely with the class. So why is IoC interesting? It promotes good design by .
The best way to describe it is with an example. Let's say your company creates scales for humans and aliens to use to weigh themselves and that these scales will be sold on Earth, Venus and Mars. The problem is that gravity is different on these different planets, so in order to ensure that they know their true weights in terms of Earth pounds the scales will have to be flexible to handle that need The ingredients needed for making IoC work are the following:. components.xml (IoC config file) Scale .java (Interface for all the components) ScaleAware.java (Interface for Action class) MarsScaleImpl.java (component) VenusScaleImpl.java (component) EarthScaleImpl.java (component) ScaleAction.java (Action class)
Lets' Take a Look At Component.xml:
Here I define the scope of the component, the implementation class and the interface that notifies the container that any Action class implementing this interface has a dependency to the above class Lets look at how this is done in the Action class.:
public class ScaleAction implements Action, ScaleAware {private Scale scale; public void setScale (Scale scale) {this.scale = scale;} public String execute () throws Exception {System.out.println ( "The weight of you is:" Scale.getWeight ()); Return Success;}}
Now the container sees that this implements ScaleAware; therefore, it will call setScale and pass in the implementing class via the interface Now for all those scales sold on Mars, all that needs to be done is to have the class definition in the components.. xml set to MarsScale and those on Earth set to EarthScale. There are many different reasons to do this, besides interstellar commerce, but either way the implmentation is the same. If given the opportunity the entire user management of the wafer weblog application could be done USING IOC.THE IOC CAPABILITY IS IOC.THE IOC CAPABILITY ISTING BUT BE CAREFUL NOT To Treat IT As a Solution Looking for A Problem. IOC Doesn't Work Everywhere So Use IT Appropriately, Otherwise It Might Go The Way Frames DID.
Working with the jsp view
THE TAGS
The most common way most frameworks send and receive information on a JSP page is via a tag library. Some use JSTL while others like Webwork have their own set of tags. For a complete listing see the WebWork Wiki. Most of the JSP's in the wafer Application Utilized The WW Tags Which i Found to Be Very Useful and easy to use. Here is a Simple usage of the tags:
oral
The first example calls getUser () in the Action that called this page, then it calls getFirstName () on that object. The second example will create an input box with a label of 'First Name' with name for the input box of firstName. While It Appears That That Tag Doesn't Do Much (Anyone CAN CREATE A Simple HTML INPUT TAG) IT Does Handle Inline Error Messaging Which I Find Very Nice (See Screen Shot Above).
JSTL
If You're A "Standards" Kind of Person kiln you can use jstl. The Below Tag Will Do The Same as The WW2 Tag Above of
Ognl and the Ognvaluestack
Ognl (Object Graphical Navigation Language) is much like JSTL except, unlike JSTL which is primarily used for getting things out, Ognl can be used to set things as well. With Ognl you can create a Map on the fly like this.
You can Also Pull Values from the an actionContext Like this:
WHERE Name Was Set in An Action Class Like this:
ActionContext CTX = ActionContext.getContext (); CTX.PUT ("Name", OtherUser.getuserName ());
Ognl also takes care of trivial and complete type conversion for you. For example, you can pass in from say an input text box the value of "10/14/1971" and it converts it into a Date object for you using a setDate accessor .
Lastly, the power Ognl provides Xwork is in the OgnlValueStack, which is basically a stack for storing request scoped values. If used with the parameterized interceptor you can place all of the parameters from a form on the stack for later retrieval in the code. This is another feature that acts like a J2EE component (HttpRequest) but it's not the same, which distinguishes it from the Servlet API. One nice way to use the OgnlValueStack is by simplifying a large Controller-as-model Action class. Lets say you have an Action class that maps to a form that 30 parameters has. that means if you are using the Controller-as-Model pattern you will have at least 30 set accessor methods to store the parameters from the submission in your class. But with the OgnlValueStack You can now Simply Have Calls Like: String Bla = (String) Stack.FindValue ("BLA");
.
Other Features Not Mentioned in Detail
Action packaging
This allows you to jar up a set of Action classes and include the xwork.xml file as an include in the master xwork.xml file. You can also do the same for Velocity views, which, if used together will allow you to componentize your .
UI Components and Custom Components
Webwork allows you to create reusable, Skinnable User Interface Components Like a Calendar Date Picker That So Many Weblogs Seem to Have these Days.
Namespaces and Packages
You can bundle up configurations in xwork.xml into packages which can be used to extend other packages, gaining access to all actions, interceptors, etc. Add namespaces with the packaging and you can create action aliases with different classes giving you the ability to have Registeraction.Action in One Namespace Point To A Different Class in Another Namespace.Why Do I like webwork2?
You may remember me ranting about how great Maverick is a few months back on TSS. So how does WebWork2 compare you ask? It's very similar. Both are built with a lot of interfaces to help promote extensibility and decoupling. Both are easy to use and have a well designed code base. But the big difference is that WebWork has Interceptors and IoC. You can handle much of the pre and post processing in Interceptors instead of in Action hierarchies like I did in the Wafer weblog application using Maverick when I was trying to do authentication. What about Struts? Struts does have much of the functionality that WW2 has BUT it is not part of the base release. for many that may not be a big deal but when it comes time for company standards those add-ons most LIKELY WON '' ', Struts HAS FULL Abstract Classes and Few Interfaces.
Webwork is a model-2 MVC Framework. How Many Other Frameworks Fit That Bill? Here is a Possible List of Reasons Why You Might Be Interested in Using WW2:
It's built with interfaces instead of concrete classes You like some of the features not included in the "other" framework like IoC or Interceptors. You are looking for an MVC that is not bound to the J2EE environment, making unit testing easier along with other tasks Many Companies Are Moving from Struts to Webwork As Their Standard. You Drink Pepsi Instead of Coke.conclusion
Building the Wafer weblog application was truly a dream with WebWork. Their use of interfaces and interceptors make it easy and flexible and make it difficult to not day dream of all the ways I could work with these new concepts. The community is big and strong with Leaders One Can Be Proud of. in My MAVERICK ARTICLE I MENTION THAT IF you are going to learn ONE Framework Maybe You Should Choose Struts over Maverick. This Time, I would say choose webwork.
About the Author
Kris Thompson is the lead of a local Java user group, www.frameworks-boulder.org in Boulder Colorado that focuses solely on web frameworks and is also a contributor on the Expresso framework and Wafer project and author of other framework articles. You can email Kris at info@flyingbuttressconsulting.com.
Resources
Mike Cannon's Talk At Tss, http://wiki.opensymphony.com/space/writeup of TSS TALK TSS N WEBWORK2
The opensysmphony wiki, http://wiki.opensymphony.com/space/webwork2
Enigmastation.com, http://www.enigmastation.com/technology/webwork/competency.html
PicoContainer, http://www.picocontainer.org/