WanTii, Inc. Tech Blog

Discussion of Java on the Web Server…

Writing and Configuring Struts2 Actions (Part 1)

July 7th, 2007 by Wes Wannemacher

The core component of the Struts2 framework is the Action. If you are unfamiliar with the Model 2 or MVC pattern, take a moment to read my previous post on the subject. The action is where you glue your business layer to your web-app. What does that mean, glue your business layer to your web-app? Well, if you have followed the guidelines of the Model 2 pattern, you will have created a separate set of classes to handle your business logic. Since this set of classes does not have any dependence on the the JSP/Servlet API, or any dependence on the API of the framework you have chosen (Struts2), then these classes need to be integrated into the web-app. To integrate these classes, you will use these classes in your actions.

Writing Actions

Actions are simple Java objects. This has a tendency to confuse developers learning Struts2 for the first time. The framework is so configurable that there are no concrete requirements for the implementation of an action. Although there are no concrete requirements, there are many considerations when writing an action. Actions are instantiated one object per request, this is contrary to previous versions of Struts. The Struts2 filter will look at the request URI and decide which action to instantiate. An instance of the action will be created and the method specified will be called. The action will then be placed on the value stack so that the view will be able to manipulate it.

Rather than trying to give you all of the details at once, I would rather start with a simple example and work my way up.

Simple.java ->

package com.wantii.simple;

public class Simple {
	public String execute() {
		return "success";
	}
}

Obviously this is a useless example :) . There are some important items to cover though. I mentioned earlier that Struts2 Actions are simple objects. In Java-speak, this is called a POJO (Plain Ol’ Java Object). The way that the Struts2 filter handles an action is to instantiate it, then it will execute the method that is specified and read the String return value to decide which view to present. In this process, the filter makes a few assumptions. First, if no method is specified, it will look for a method called “execute.” The second assumption is that a return value of “success” should generate the default view. The place where the filter looks for these configurations is the struts.xml file.

struts.xml ->

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
        "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>

    <package name="simple" namespace="/" extends="struts-default">

        <action name="Simple" class="com.wantii.simple.Simple">
            <result>/Simple.jsp</result>
        </action>

    </package>

</struts>

This example is not shaping up to much, but there is more than meets the eye. First off, the struts.xml file is broken down into logical units called packages. Packages are meant to help you group your application’s components. Every action configured within a package will inherits that package’s configuration. In this struts.xml file, it would appear that there is very little configuration done to the “simple” package. The namespace attribute indicates where the action can be reached. In this case, the namespace is “/” so that to reach the Simple action, the web browser will need to request “/Simple.action” within your web-app’s context. So, if your web-app is called “simple-app” and it is running on a server called www.yourserver.com, the URL is http://www.yourserver.com/simple-app/Simple.action.

The next attribute “extends” will be discussed further in a bit, first let’s take a look at the action’s configuration. The action’s “name” attribute indicates the name of the action within your web-app. To access this action, you take the package’s namespace and concatenate the action’s name to come up with the URL of the request (I know this is getting redundant). The class attribute should seem obvious, it indicates which Java class will be instantiated for the request. There is also an optional “method” attribute where you can specify which method to run. By not specifying a “method” value, the filter assumes that you wanted the execute method. Each action element can have one or more “result” elements. Each result is a possible view that the action can launch. The “result” tag has a few attributes, namely “type” and “name.” By not specifying a “name,” the filter assumes the name is the String “success.” Since we did not specify a type, the filter assumes we meant “dispatcher.” This forwards the browser to the view (JSP) specified. Dispatching is different than redirecting because the browser does not realize that the results are in a JSP file, rather it thinks that the contents came straight from the original requested resource.

Now that we are past the basic “Hello World” type example, I am going to move on to something more useful. Let’s say you want to add a simple guestbook to your web site. A guestbook is a simple tool that allows your website visitors to leave a note on your page that you and future visitors can read. Following the Model 2 pattern, I will first design the business objects that will support the guestbook. A guestbook is made up of a set of messages left by the visitors. First, I will create a message class to store individual messages.

Message.java ->

package com.wantii.guestbook;

import java.util.Date;

public class Message {

	private String guest;
	private String message;
	private Date when;
	
	public String getGuest() {
		return guest;
	}
	public void setGuest(String guest) {
		this.guest = guest;
	}
	public String getMessage() {
		return message;
	}
	public void setMessage(String message) {
		this.message = message;
	}
	public Date getWhen() {
		return when;
	}
	public void setWhen(Date when) {
		this.when = when;
	}
		
}

Since this class only stores individual messages, I need another class that will manage the collection of messages.

GuestBook.java ->

package com.wantii.guestbook;

import java.util.Vector;

public class GuestBook {

	private static Vector<Message> messages;
	
	public void addMessage(Message msg) {
		if (messages == null ) {
			messages = new Vector<Message>();
		}
		messages.add(msg);
	}
	
	public Vector<Message> getMessages() {
		return messages;
	}
	
}

The GuestBook is a really no more than a wrapper around a Vector. There are much better ways to handle this sort of processing. After I get sick of writing Struts2 articles, I will begin covering the Spring Framework which could possibly help me on this project. Since I have not covered Spring, I will not assume you know it. This class only uses basic Java techniques that will allow me to track the messages as they come in. If you are not familiar with Java’s “static” modifier, it creates a class-level variable so that no matter how many GuestBooks are instantiated, there will be only one “messages” that is shared amongst all the instantiated GuestBooks. Even when there are no instantiated GuestBooks, the “messages” will remain intact. The other issue I am avoiding in this code is persistence. A good GuestBook would store it’s entries in a database, or even a file if no databases were available. After I have written some articles on Spring and Struts2, I will delve into the Hibernate framework. Without persistence, this is not a particularly useful guestbook, but to make it useful, I would incorporate technologies that I have not yet covered. So, if you are planning on using this code on your site, please don’t. Enhance it by at least adding persistence, then use it. When I start talking about persistence in another article, I promise, this will be the first example.

The Message class and the GuestBook class are the business objects. They can accept, store (in memory) and retrieve messages. Now, it is time to incorporate these classes into the web-app. When you create a Struts2 Action, it is a good idea to think through the Use-Cases of the action you are going to create. On a large project, formal Use-Cases created with the assistance of a CASE tool would be a good idea. In this case, I am not going to go that far, but I am going to informally think this through before I start coding. The action will be responsible for two tasks, first displaying the entire guestbook, second pushing entries into the guestbook. Users will be able to view the guestbook and from the view, there will be a link to sign the guestbook. If a user signs the guestbook, the entry will be added and the user will be forwarded back to the page to view the entries. Before I write the code for the action, I will configure the action to support this interaction.

Action Configuration ->

<package name="guestbook" namespace="/guestbook" extends="struts-default">
    	<action name="GuestBook_*" class="com.wantii.actions.GuestBookAction" method="{1}">
		<result name="input">/WEB-INF/jsp/GuestBook_input.jsp</result>
    		<result name="success">/WEB-INF/jsp/GuestBook_messages.jsp</result>
    		<result name="list">/WEB-INF/jsp/GuestBook_messages.jsp</result>
    	</action>
    </package>

The configuration of the GuestBookAction has a few more items then we discussed before. First off, you will probably notice that the “name” attribute contains an asterisk. This does not mean that users have to go to http://yourserver/your-app/guestbook/GuestBook_*.action. The asterisk is a wildcard. It means that the “name” attribute will be treated as a pattern, rather than simply the string specified. So, requests like http://yourserver/your-app/GuestBook_input.action or http://yourserver/your-app/GuestBook_anythingyouwant.action will all be handled by this action. The “class” attribute is the same as before. There is now a “method” attribute, this allows you to specify which of the action’s methods should be launched. In this case, by using “{1}” we are back-referencing the wildcard in the “name” attribute. So, if the request comes in as http://yourserver/your-app/guestbook/GuestBook_input.action, the Struts2 filter will run the “input” method and dispatch to the result that matches the return value of the “input” method. In this particular setup, the Struts2 filter knows that when no method is specified (as in the request: http://yourserver/your-app/guestbook/GuestBook.action) the execute method will be used. There are three “result”s configured for the GuestBook action, if the specified method returns “input,” then the /WEB-INF/jsp/GuestBook_input.jsp JSP will be dispatched. If the called method returns “list” or “success,” then the /WEB-INF/jsp/GuestBook_messages.jsp will be dispatched.

GuestBookAction.java ->

package com.wantii.actions;

import java.util.Date;
import java.util.Vector;

import org.apache.struts2.interceptor.validation.SkipValidation;

import com.opensymphony.xwork2.ActionSupport;

import com.wantii.guestbook.GuestBook;
import com.wantii.guestbook.Message;

public class GuestBookAction extends ActionSupport {

	private static final long serialVersionUID = -8577843349235520003L;
	private String guest;
	private String message;
	
	@Override
	public String execute() {
		Message msg = new Message();
		msg.setGuest(guest);
		msg.setMessage(message);
		msg.setWhen(new Date());
		GuestBook book = new GuestBook();
		book.addMessage(msg);
		return SUCCESS;
	}

	@SkipValidation
	public String list() {
		return "list";
	}
	
	public Vector<Message> getMessages() {
		GuestBook book = new GuestBook();
		return book.getMessages();
	}

	public String getGuest() {
		return guest;
	}

	public void setGuest(String guest) {
		this.guest = guest;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}

The GuestBookAction class is still a POJO. The biggest difference between GuestBookAction.java and Simple.java is that GuestBookAction extends the ActionSupport class. This may seem like a framework dependency (and in some ways it is), but a framework dependency is not the same as JSP/Servlet Container dependency. The ActionSupport class is fairly simple, it does not add much to the class’s signature other than a few useful utilities. First off, the ActionSupport class has some useful constants such as SUCCESS, INPUT, LOGIN and ERROR. These are String constants that make the code a bit cleaner since a statement like ‘return “success”;’ could conceivably be considered a magic number. These constants all point to the commonly used return values for Struts2 Action methods. ActionSupport also has implementations for commonly used methods such as an implementation of the “input” method. When you use ActionSupport’s implementation of a method, it will return the default value. In the case of ActionSupport.input(), the String “input” will be returned. This is helpful in this case because I have a result mapped to “input” that allows users to add a message to the guestbook. Since ActionSupport’s execute method will not suffice for this action, I overrided with my own implementation, hence the @Override annotation.

GuestBookAction has a “list” method which is marked with the “@SkipValidation” annotation. When I explain the validation setup later, I will explain why this method is marked to skip validation.

There is a “getMessages” method on this action that retrieves and returns the entire guestbook. Since this method is a public “getter,” it is available to the view. When I cover the GuestBook_messages.jsp file, you will see how this is helpful. The rest of the methods are getters and setters so that parameters can be passed in for the action to handle. In this action, the values passed in and retrieved are Strings, but other datatypes can be used and Struts2 will perform the conversions for you. I will cover the automatic type conversion later. Right now, I want to take a look at the GuestBook_input.jsp.

GuestBook_input.jsp->

<%@ 
    page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"
%><%@
    taglib prefix="s" uri="/struts-tags"
%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Sign the Guest Book</title>
<s:head theme="ajax" />
</head>
<body>
<h3>Sign the Guest Book</h3>
<s:form action="GuestBook" namespace="/guestbook">
<s:textfield required="true" key="Your Name" name="guest" />
<s:textarea required="true" rows="4" cols="36" key="Your Message" name="message" />
<s:submit />
</s:form>
</body>
</html>

In the input JSP file listed above, I made use of a few of the Struts2 tags. First off, in the HTML <head> section of the JSP, I added the <s:head> tag. The <s:head> tag adds the appropriate javascript code so that the any of the Struts2 functionality that requires javascript will work appropriately. The <s:head> tag also is the place where you set a “theme” for a page. There are a few choices for the theme, and I will cover the options further when I write an article on the Struts2 tags. For now, I used the “ajax” theme, but if you have a reason to avoid AJAX, then the “simple” or “xhtml” themes could be used as well. I would suggest using “ajax” or “xhtml” until you become familiar with the Struts2 tags.

I also used the <s:form>, <s:textfield>, <s:textarea>, and <s:submit> tags. These are form tags that do a bit more than traditional HTML form tags. In the <s:form> tag, you can specify the action and the namespace as I have done. This will create a URL and all the necessary markup so that when the form is submitted, it will send the data to the action you specify. The <s:textfield> and <s:textarea> tags generate input fields. Struts2 tags can be convenient because if you use “ajax” or “xhtml” themes, the form will be marked up and laid out in a table much the same way that most HTML developers would do it themselves. I prefer to let Struts2 do the markup and layout for me so that the JSP code stays simple. There are complaints from time to time on the struts-user mailing list about the generated markup, and when I cover the Struts2 tags more thoroughly, I will show you how to create a custom theme or to customize the markup of a single tag. The name of the tag must match a “setter” method from the action. Before launching the specified method (or execute as in this case), the fields from the form will be converted to the appropriate type and the matching “setters” will be called. When you are dealing with the Struts2 form tags, the important attributes are “key” and “name.” The “key” attribute assigns a label to the attribute so that what you put in this attribute will be used in the resulting HTML to tell the user what to fill in for the field. The “name” attribute is similar to the HTML “name” attribute in that it is the name of the parameter passed during form submission. I also used “requiredstring” because it generates a small red asterisk which indicates that the field is required. The “requiredstring” attribute does not require the user to enter the field, I will cover that when we get to validation.

While I am covering the JSP code, let’s take a look at the GuestBook_messages.jsp

GuestBook_messages.jsp

<%@ 
	page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"
%><%@
    taglib prefix="s" uri="/struts-tags"
%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>The Guest Book</title>
<s:head theme="ajax" />
</head>
<body>
<h3>The Guest Book</h3>
<p>To sign the Guest Book, <a href="<s:url action="GuestBook_input" namespace="/guestbook" />">click here</a>.
<table>
<s:iterator value="messages">
<tr><td><s:property value="guest"/> wrote when he/she visited on <s:property value="when"/></td></tr>
<tr><td><s:property value="message"/></td></tr>
</s:iterator>
</table>
</body>
</html>

In this JSP, I am making use of the “messages” Vector I made available from the GuestBookAction.getMessages() method. The Struts2 <s:iterator> tag will take any object that implements the java.util.List interface. By specifying “messages,” the tag will look on the Value Stack for an object with a getMessages method that returns an object implementing the java.util.List interface. Since the action is on the stack, it will find the method I intended and will put each of the objects in the List on the stack. Since the objects in the List are Message objects, it’s getters are available to the <s:property> tag. This makes a nice clean loop in the JSP code without reverting to a Java scriptlet. There are a few other ways of doing this, such as using displaytag (http://displaytag.sf.net) or the JSTL. The Struts2 tags integrate nicely, but they do not always provide all of the functionality that you might need. The Display tag library does a great job of taking a group of objects and creating a sortable, exportable table, so look into it if your project has these sorts of requirements.

One of the strongest, yet trickiest, features of the Struts2 framework is it’s validation facility. One of the cardinal rules of writing software is not to trust your input! You have to check to make sure that you are getting a value that will not cause your application to crash. This is true for any language on any platform. In the case of Java, when an object is not initialized, you can receive a NullPointerException, or if you are expecting a number and get a String, you might generate a NumberFormatException. When exceptions are thrown in your application, it can make your site look very ugly. Quite a bit of the traditional JSP/Servlet code I wrote in the past was devoted to making sure that I had good values before I began processing them. The problem with adding validation code is that it requires maintenance, a recompile and a reinstall of your application if the business rules change. Struts solves this problem elegantly by treating form field validation as a configuration. By creating a configuration file (using XML) and naming it appropriately and placing it in the right directory, your application will test your input before it gets to the configured method of your action. In this case, I need to create an XML file called GuestBookAction-validation.xml in the same directory as the GuestBookAction class. By putting rules in the GuestBookAction-validation.xml file, Struts2 will make sure that all of the rules pass before launching the configured method. If the input fails validation in any way, the user will be forwarded back to the “input” view and the specified error messages will be displayed.

For this project, I am going to make the “guest” and “message” fields required. If these fields are required, then won’t the errors show up the first time a user visits the “input” view? The user has not had the chance to fill them in, so won’t this create a circular dependency? Struts2 handles this situation as well. If you are visiting the “input” view for the first time, Struts2 knows to skip validation. When you try to submit the form (or when any method other than “input” is called), the validation will be launched and if it fails you will then see the error messages. Even though the processing happens in the “execute” method, Struts2 will run the validation on any method other than “input.” For this application, I want users to be able to start out by seeing the contents of the guestbook before being offered to sign it. Therefore, I created a “list” method that can be used to enter the guestbook. So that the user does not fail validation when attempting to view the guestbook for the first time, I annotated the method with @SkipValidation. Otherwise, the only entry point to the guestbook would be through the “input” method since all other methods would fail validation and forward the user to the “input” view. This is a bit tricky, and I will present a better example later when I go through a typical data CRUD example.

GuestBookAction-validation.xml ->

<!DOCTYPE validators PUBLIC 
"-//OpenSymphony Group//XWork Validator 1.0.2//EN" 
"http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">

<validators>
    <field name="guest">
        <field-validator type="requiredstring">
            <message>You have to leave your name.</message>
        </field-validator>
    </field>
    <field name="message">
        <field-validator type="requiredstring">
            <message>You have to leave a message</message>
        </field-validator>
    </field>
</validators>

The GuestBookAction-validation.xml file gives you an example of how easy it is to validate the incoming data. For each field in your input form that requires some sort of validation, there is a “field” entry. The field entries contain one or more “field-validator.” The “field-validator” is the test that Struts2 will use to decide if the input is good. Here, I am using the “requiredstring” validator which tells Struts2 that something must be present. There are other types of “field-validators” that you can use. Other helpful validators inlude “email,” “url,” and “regex” that make field validation a snap! The “field-validator”s have a “message” element that will be used by Struts2 when the test fails. So, the test itself and the error message displayed to the user are abstracted completely out of the code.

Let’s take a look at our guestbook in action. Here is a screenshot of a first visit to the guestbook (after I have added a few messages) -
First visit to the guestbook

From here, you can see the previous messages and a link to sign the guestbook. When you click the link, you will go to the input view –
Sign the guestbook

Now, if you click the submit button before you have filled out all of the fields, validation will fail –
Failed validation

After correcting your mistake, the message is submitted and you are shown the guestbook again -
Success

To use this guestbook on your site you would create a link to http://yourserver/your-app/guestbook/GuestBook_list.action. With the proper JSP code (as we have done here), Struts2 will manage the interaction flow. Obviously, this is not a production-ready application. Besides persistence, you would also have to consider escaping HTML code in the submissions to stop submitters from attempting to insert code which might attempt cross-site scripting. You would also want to consider persistence not only because the guestbook will disappear each time your app-server is restarted, but the messages Vector will grow without bounds.

Download the code for the guestbook project here.

Both of the actions in this article are still rather simple. It would be difficult to pass data around between actions using getters and setters. In the second part of this series, I will cover more advanced topics such as storing data in the visitor’s session, using and configuring interceptors and the interceptor stack, more validation techniques, and type conversions on input. Subscribe to the RSS feed to know when that article is available. Thanks for reading and leave some feedback so that I know what you think of this article.



This entry was posted on Saturday, July 7th, 2007 at 4:59 pm and is filed under Java, Struts 2. You can follow any responses to this entry through the RSS 2.0 feed.

Spread the Word

del.icio.us Digg Furl Reddit StumbleUpon Technorati


You can leave a response, or trackback from your own site.



34 responses about “Writing and Configuring Struts2 Actions (Part 1)”

  1. Edson said:

    Very good, it’s a good starting in Struts2!!

  2. anas said:

    thanx a lot for this great work :)

    found it very helpfull

  3. amar said:

    Found to be useful…Thanks for a quick tutorial on Struts2 actions.

  4. reemsky said:

    Good job !

  5. jenkins said:

    Good to see that someone is providing some information on Struts 2.

    I would be interested to know about Validation and type conversion. I am currently trying to validate an object but I can’t seem to before it gets Converted. I have played around with the order of the interceptor stack but it doesn’t seem to help. Is there any way to validate an object before type conversion?

  6. wesw said:

    jenkins –

    Post your question in the forum –

    http://www.wantii.com/forum

    that way we can all benefit from the solution if the problem gets solved.

    -Wes

  7. jenkins said:

    Will do, didn’t realize there was one.

  8. Marty said:

    Thanks, your articles are very informative and easy to follow.

  9. Ben said:

    Thanks for this – it’s been more use than the official tutorials!

  10. Santosh said:

    Thanks a ton!! I will be back with more appreciation as I put your article into building an application.

  11. Rao said:

    Very good. Looking for more articles from you on subjects like this. Thanks.

  12. BBade said:

    Excellent tutorial. So far it is the best Struts2 introduction that I have seen on the internet. Please keep publishing more when you have the time!

  13. dafydd williams said:

    This is a good tutorial but as a newbie to struts2 what I find confusing is the directory structure. Where should all these files go in respect to that. How would this fit into say the struts-blank
    structure that comes with the struts2 download. Also would you be able to supply say the guest book application as a war to download.

  14. Vasu said:

    I found this tutorial very informative for beginners like me. Thanks and keep up the good work.

  15. Fei said:

    Wonderful article, please continue doing so.

  16. Fei said:

    Sir, I came from .Net world. Thanks for this informative article.

    Is there anyway we can get rid of .action suffix?

  17. wesw said:

    Fei, changing the .action suffix is easy enough. Check here –

    http://struts.apache.org/2.0.11/docs/strutsproperties.html

    If you still have trouble, head over to the forum
    http://www.wantii.com/forum

    and we can solve it over there.

    -Wes

  18. Fei said:

    Wes-

    Thank you very much.

  19. Bizancio Spirit said:

    Incredible tutorial!

  20. devs said:

    excellent !!!

  21. rohit said:

    hey.
    thanks for the article really helpful. Although i m having some trouble.
    i cant seem to be able to pass the values that i m entering on the web page to the action class..
    can you plz help me out.
    thank you.

  22. wesw said:

    rohit, go ahead and post your question in the forum ->
    http://wantii.com/forum/index.php

    It would be easier for me to help you troubleshoot.

  23. Raj said:

    it’s a very gud tutorial for Struts developers.
    Thanx alot. keep posting this kind of topics

  24. Vandana said:

    Excellent, very clear article on Struts2 execution flow.

  25. sri said:

    very good explonation for beginners like me.

    Thanx

  26. Terigo.com Blog » Blog Archive » Best Struts2 Guide said:

    [...] came across this Struts2 example today on http://www.wantii.com who did a very good job writing an example out. I was very pleased with it! [...]

  27. Jason said:

    I agree with the others, this is one of the best struts2 guides I’ve found so far!

  28. Rupinder said:

    Excelent Work done, really appreciable for a quick start

  29. malaka said:

    ya it’s good one to start up !!!

  30. Manjunath said:

    Its very good material for the beginners of struts2

  31. praveen said:

    Hi,

    The stuff you wrote here is GOOD ! appreciate the details you have put in.

    One of the major difficulties folks will encounter , is when they need to design struts2 app for there projects i would love to know if you have any blogs on that, also usually we would integrate a struts2 app with EJB3 (since it’s my personal perception that ejb3 will rule in the coming days) . Also post a blog on how best this can be done (if there is any stuff already there , please let me know of the same) .
    But over all i loved to read u r blog.. GOOD JOB

  32. Nitin Kumar said:

    I want a running example of custom defined interceptor in struts2

  33. Santhosh Cheeran said:

    This is very usefull. I was able to solve the input fn not triggered for INPUT issue after a long while

    Action1_Input

    tiles.Action1_Input
    tiles.Action1_Input
    tiles.Action1Res

    I also addded to the form

    Form action=”Action1_Input”

    God bless you man :)

  34. Santhosh Cheeran said:

    The previous 1 didn’t print XML right .. but I split my action in to two actions

    initial action “Action1″ has only input and the form invokes an action Action1_Input and it is by putting in Form element

    form action=”Action1_Input.action”

Leave a Reply