Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Arg.

The following is a sample application I did to study how webwork works . Hopefully it'll help other newbies gain footing on this great framework. Having said that, if I have made some mistakes in this example, or if there is a better way of doing things, please feel free to contribute to this wiki.The application works like a mini-BBS. Users login to the application with a nickname. The user session is saved in a session scoped component. Once logged in, they can leave quips or messages.

Basic Application and Environment Setup

webwork.properties

...

.

...

Action Classes

LoginAction extends ActionSupport, which already implements has default implementations of some of the basic action methods . Additionally, it and provides some validation support.

The two main methods we are concerned with in ActionSupport are validate() and execute().(Note : this has changed from an earlier beta which uses doValidation() and doExecute(). )

Validation

Validation is performed on your Action class if it implements Validatable (ActionSupport does), and your DefaultWorkflowInterceptor is activated on that action. The default configuration includes the workflow interceptor.

Execution

execute() returns a String. This String will be used to determine which result is used.

The framework provides some default return Strings, namely:

Code Block
Action.SUCCESS = "success"
Action.INPUT = "input"
Action.NONE = "none"
Action.ERROR = "error"
Action.LOGIN = "login"

For example, lets take a look at the relevant part of our xwork struts.xml configuration for LoginAction...

Code Block

...

titlestruts.xml

...

code
fragment
<action name="login" class="example.LoginAction"> 
  <result name="success" type="chain"> 
    <param name="actionName">viewMessages</param>
  </result>

  <result name="input" type="chain"> 
    <param name="actionName">viewMessages</param>
  </result>
</action>

If execute() returns a String of "success", the result with attribute "success" will be used. If doExecute execute() returns a String of "input", the result with attribute "successinput" will be used.

You can define your own return results. For example,:

Code Block
public String doExecuteexecute() {
  return "resetPassword";
}

We need only configure a result for the "resetPassword" name:

Code Block
<action name="login" class="example.LoginAction"> 
  <result name="resetPassword" type="chain"> 
    <param name="actionName">viewResetPassword</param>
  </result>
</action>

Context Variables / Mapping

Code Block
titleLoginAction.java

...

code
public class LoginAction extends ActionSupport {

	    private String loginName;

	    public String getLoginName() {
		        return loginName;
	    }

	    public void setLoginName(String loginName) {
		        this.loginName = loginName;
	    }
}

If you notice, login LoginAction has a bean property loginName. This property will be set automatically by webwork Struts 2 from your web forms containing a loginName element.

Code Block
<form method="POST" action="login.action">
  <input type="text" name="loginName" size="20">
  <input type="submit" value="Login">
</form>

Also, the bean property is available to your views. In velocityVelocity, this accessible via the VelocityContext.

Code Block
$loginName

which is mapped to getLoginName() from your Action in our LoginAction class.

getLoginName() is mapped to $loginName. You can map any other object you wish. For example, I could have a User object, and thus...:

Code Block
class User {
   private Account account;
   private String name;
   // with the relevant getX() methods...
}

in my We add a user property to an action class...:

Code Block
class MyExampleAction {
   //...
   User getUser() {
      returns user;
   }
   //...
}

and in my velocity template, have a In our Velocity template we can access User properties as expected:

Code Block
Welcome, $user.name.
You last logged on at $user.lastLogin.
You currently have $user.account.balance left in your account.

Result Types

Predefined in webwork-default.xml

Code Block

        <result-types>
            <result-type name="dispatcher" class="com.opensymphony.webwork.dispatcher.ServletDispatcherResult"/>
            <result-type name="redirect" class="com.opensymphony.webwork.dispatcher.ServletRedirectResult"/>
            <result-type name="velocity" class="com.opensymphony.webwork.dispatcher.VelocityResult"/>
            <result-type name="chain" class="com.opensymphony.xwork.ActionChainResult"/>
        </result-types>

In our example, we have only used the results of <chain> and <velocity>.

Interceptors

is there an order execution of the interceptors ? Which interceptor is executed first ?

Interceptors are called before the actions are invoked. With interceptors, you can "wrap" your action classes to provide additional services or functions. You can even prevent the execution of the action classes if you wish.

Some interceptors are predefined in webwork-default.xml

Code Block

        <interceptors>
            <interceptor name="component" class="com.opensymphony.xwork.interceptor.component.ComponentInterceptor"/>
            <interceptor name="validation" class="com.opensymphony.xwork.validator.ValidationInterceptor"/>
            <interceptor name="workflow" class="com.opensymphony.xwork.interceptor.DefaultWorkflowInterceptor"/>
            <interceptor-stack name="defaultStack">
                <interceptor-ref name="timer"/>
                <interceptor-ref name="logger"/>
                <interceptor-ref name="static-params"/>
                <interceptor-ref name="params"/>
            </interceptor-stack>
        </interceptors>

... more info on interceptor-ref

... more info on interceptor-stack

Components

components.xml

Components

Code Block
titlecomponents.xml

<components> 
Code Block

<components> 

    <component> 
        <scope>application</scope>
        <class>example.data.MessageList</class>
        <enabler>example.data.MessageListAware</enabler>
    </component>

    <component> 
        <scope>session</scope>
        <class>example.web.UserSession</class>
        <enabler>example.web.UserSessionAware</enabler>
    </component>

  </components>

Two components, one to hold the application scoped chat messages, another to hold the user's session information (login account name, etc.) For all practical purposes, you can replace the application scoped component with a database. i.e. instead of reading/writing to the component, read/write to the database.

Comments

Feedback welcome. I'm also a newbie, and may have misused the framework, coded some mistakes above. Apologies if that be the case.