Matt RaibleMatt Raible is a Web Developer and Java Champion. Connect with him on LinkedIn.

The Angular Mini-Book The Angular Mini-Book is a guide to getting started with Angular. You'll learn how to develop a bare-bones application, test it, and deploy it. Then you'll move on to adding Bootstrap, Angular Material, continuous integration, and authentication.

Spring Boot is a popular framework for building REST APIs. You'll learn how to integrate Angular with Spring Boot and use security best practices like HTTPS and a content security policy.

For book updates, follow @angular_book on Twitter.

The JHipster Mini-Book The JHipster Mini-Book is a guide to getting started with hip technologies today: Angular, Bootstrap, and Spring Boot. All of these frameworks are wrapped up in an easy-to-use project called JHipster.

This book shows you how to build an app with JHipster, and guides you through the plethora of tools, techniques and options you can use. Furthermore, it explains the UI and API building blocks so you understand the underpinnings of your great application.

For book updates, follow @jhipster-book on Twitter.

10+ YEARS


Over 10 years ago, I wrote my first blog post. Since then, I've authored books, had kids, traveled the world, found Trish and blogged about it all.

ActionForms: Struts' bastard child

Folks that rag on Struts seem to point to ActionForms as one of its major design flaws. I've been slightly frustrated with ActionForms this week, but overall, I think they're a good thing. It's possible my ActionForm affection is misguided. The major reasons I like them is because I believe they allow me to do stuff that is not possible in other web frameworks. I definitely could be wrong though, so I'm hoping the other framework authors/users will speak up and say "My framework does that!" Specifically, I'm talking to the WebWork, Tapestry, JSF and Spring folks.

I do wish that I could throw my POJOs up to my UI, so I hope the following things are possible with the WTJS frameworks. It would simplify things if I didn't need to transform POJOs -> ActionForms (particularly with Hibernate).

  • Validation and re-displaying the user's entered values. I love Struts' Validator. It's great how I can generate the validation.xml file with XDoclet and have a "required" struts.validator tag right next to a hibernate not-null="true" tag. Two questions:

    1. Can any of the WTJS frameworks re-display the user's entered values? Specifically, back into the input fields where the user entered them? I think this is important for useability.

    2. Do any of them have the ability to generate client and server-side validation, or at least declaratively write it in XML?

    I'd love to find a way to hack the Validator to allow you to define validation rules for a POJO and then use an Interceptor to validate it. I don't like how Spring requires you to write YAJC (Yet Another Java Class) to do validation.
  • Handling checkboxes. The basic reason for the reset() method in ActionForms is to handle checkboxes. Since unchecked checkboxes don't send a value - there needs to be a way to set a boolean back to null. I'm sure all of the WTJS frameworks support checkbox handling, I just want to make sure - and frankly - I'd like to learn a little more about how each framework handles it.

I guess there's only two reasons I like ActionForms - the major one being the ability to specify (and generate) my client and server-side validation in XML. If I don't find this same slick feature in the other frameworks, I might have to do a bit of hacking to do the Interceptor with Validator thing - but hopefully I won't need to go there.

Posted in Java at Feb 04 2004, 08:31:13 PM MST 25 Comments
Comments:

The Echo framework can pretty easily handle #1, redisplaying the users entered values in the same input fields where the user entered them is pretty easy: pull the field from the session and if not null, set it to the userInputField.

However, currently there is no Validation / Interceptor architecture like Struts. Unfortunately you'd have to hack that type of validation into your application. Considering Echo is based on Swing's Interfaces, it's going to inherit Swing's validation framework, which basically means that validation will occur in the actionListener method when a user clicks a button...

However, I can understand the power and need to externalize that kind of thing into the XML files... One person has suggested the use of commons-validation but no one has reported success either way. It really needs some sort of declarative framework though...

On a side note, I'm thinking that XDoclet is something that I seriously need to learn. :)

thx

Posted by mnickel on February 04, 2004 at 11:23 PM MST #

WebWork2 provides most of the features you're asking for.

1. Can WebWork display the original values back to the user?

Yes, you have the capability of doing this, but this isn't the default behavior. The default behavior is to highlight the field and ask the user to fill in the correct data. You can relatively easily modify the template that the ui tag uses so that it populates the field with the data the user entered.

2. Does WebWork have an XML validation framework?

Again, the answer is yes. Here's a small snippet that should give you a sense of what the validation looks like:

<validators>
    <field name="bar">
        <field-validator type="required">
            <message>You must enter a value for bar.</message>
        </field-validator>
        <field-validator type="int">
            <param name="min">6</param>
            <param name="max">10</param>
            <message>bar must be between ${min} and ${max}, current value is ${bar}.</message>
        </field-validator>
    </field>
</validators>

Posted by Matt Ho on February 05, 2004 at 12:36 AM MST #

You might consider checking out WebWork as it does let you use your pojo's without having to write wrapper Objects like ActionForm. Suppose you had the following POJO.
public class Pojo {
   private Integer id;
   private Integer[] checkboxFields;
   private String username;
   private Date date;
   // assume the usual accessors
}
WebWork has built in type conversion that will automatically convert string fields into the types you're requesting. For example, the following could be used to populate the Pojo date field with the Date representation for 2/4/2004.
<input type="text" name="date" value="2/4/2004">
Like Struts, WebWork also has a set of UI tags. Finally, you can then take your POJO and stuff it directly into Hibernate. Here's a simplified snippet that shows how to do that. Notice that PojoAction does not need to extend any parent class.
public class PojoAction implements Action, ModelDriven {
    private Pojo model = new Pojo() ;

    // ModelDriven implementation
    public Object getModel() { return model; }

    // Action implementation
    public String execute() {
        Session session = // fetch hibernate session;
        session.save(model);
        // close hibernate session here
        return "success";
    }
}
We could also define validation using XML to ensure that the model is valid before the execute method is called.

Posted by Matt Ho on February 05, 2004 at 12:50 AM MST #

Thanks Matt. But does WebWork's validation engine support <em>client-side</em> validation as well?

Posted by Matt Raible on February 05, 2004 at 04:23 AM MST #

Hi Matt, I am probably missing something (I am new to struts), but I have noticed that you do not use DynaForms for AppFuse. They seem to be very convenient, it there a reason. Thanks.

Posted by Redmapper on February 05, 2004 at 07:17 AM MST #

Redmapper - the reason I don't use DynaForms is simple: They were created to make it easier to create forms - by using XML instead of creating a Java class. The ActionForms in AppFuse are generated from POJOs (using XDoclet) - so this is even easier than declaring them in XML.

I simply use one XDoclet to flag the POJO for Form generation. I can also use @struts.validator tags on the POJO to generate the validation.xml file. Furthermore, it's much easier to use an IDE with concrete ActionForms, than using casting and get("propertyname) that you have to do with DynaActionForms.

Posted by Matt Raible on February 05, 2004 at 08:32 AM MST #

A Question for Matt Ho, or any other web workers that stop by. If I have a date field in my POJO how gracefully does WebWork2 handle values that aren't in the proper date format? Does it capture the error and redisplay to the user, or do we get 500 errors and ugly stack traces? Also can the improper date value that was entered be redisplayed in the page so that the user can correct his error (I'm guessing not since the value never got into the date property since it wasn't a valid date)?

Posted by Scott Mitchell on February 05, 2004 at 11:54 AM MST #

I just wanted to point out that JSF can re-display input values and associate user input controls directly with POJOs. You can, however, just as easily have an ActionForm-type class if you want (it doesn't need to subclass anything, though). Also, a JSF Action can be any method on any available object, as long as it has the proper signature. As far as validation goes, JSF doesn't handle it in an external XML file. You can attach validators to specific input controls, or you can handle validation in any method that has the proper signature. It's possible, however, through the Struts-Faces integration library, to use Struts' validation, but that requires use of ActionForms. JSF's real strength is the fact that the user interface is represented on the server by a tree of components. So an input control has an actual server-side representation that can be manipulated declaratively or through code. And with the Struts-Faces integration library, you can take advantage of the component model in conjunction with Struts features.

Posted by Kito D. Mann on February 05, 2004 at 12:07 PM MST #

POJO's are ready for Struts, right out of the box. Only thing you have to do is nest one level. That's it!. ActionForm with a "rootForm" property or something, and everything under that is completely decoupled from the Form system. You don't have to use the nested tags or anything (but they are damn good at it :).... <bean:write name="myFormBean" property="rootForm.helloThere" /> ...or you could expose the nested bean as a new reference. Either way, my apps are very quick to decouple POJO's an pass them around like something really easy to pass around.

Posted by Arron Bates on February 05, 2004 at 12:19 PM MST #

I put some answers on my blog... .http://jroller.com/page/jcarreira/20040205#webwork2_answers

Posted by Jason Carreira on February 05, 2004 at 06:37 PM MST #

Do I sound like a broken record yet? When are you going to just quit asking questions and try out Tapestry? You can build it from CVS HEAD easily or just grab a 3.0 binary (currently beta, but way stable). Drop in the WorkBench WAR file and see for yourself. It does all that you ask about here. I would wager a good bet that no other Java framework is as tight and slick with JavaScript as Tapestry - it is integral. Again, just fire up the WorkBench sample application and click around and see the types of interactions it allows, and then estimate how much work it would be to emulate that with Struts.... oh, and then have a look at the Tapestry source code for it. Then kick yourself for continuing down the Struts road. :))

Posted by Erik Hatcher on February 05, 2004 at 07:51 PM MST #

Scott, The default behavior of WebWork would be to leave the date field as null if the value the user inputed is not valid. Using the validation framework, you could set the date field as a required field in which case the user would be sent back to input page and be notified that the date field was improper. The field itself would be blank. So, no 500 errors, no ugly stack traces. Just behavior that you'd expect.

Posted by Matt Ho on February 06, 2004 at 01:14 AM MST #

A little late, but here are my thoughts

I think the real advantages of separate ActionForms are:
-The ActionForm can be easily shared between multiple Actions
-Enables the Action to be a singleton

Validation, etc, are all possible in other frameworks. Some simple spring examples just write directly to business objects, which is usually wrong if any errors could be generated. This causes data entered by the user to be lost.
-Paul

Posted by PSK on February 07, 2004 at 10:33 PM MST #

Why do you want you Actions to be singletons? That's very limiting...

Posted by Jason Carreira on February 08, 2004 at 11:39 PM MST #

Well, its a good question.. I'd know if I have a good answer, but I'll try:

I think it centers around performance. Actions in struts are used, not just for handling form input, but also to enable flowcontrol to be kept in the struts-config.xml file. Since actions are singletons, there's no overhead of allocation or a pool lookup.

But.. I really agree with you, I think the benefits of singletons do not out-weigh their drawbacks. I'm never in an environment where the overhead of object allocation will make or break me. But I respect their desicion just the same...

I'm really exicted about picocontainer + webwork2 myself. ;)

Posted by PSK on February 09, 2004 at 05:29 PM MST #

Spring stores user-entered values as "rejected values" if they can't be bound to command/form object fields. This means that an "xxx" entered for a java.util.Date will result in a marked error but still show "xxx" as value to modify for the user. I believe this is the single most intuitive behavior; showing the field as empty can be quite nasty if the user just got one letter wrong... (throwing an exception is inacceptable anyway).

Conversion between user-entered Strings and form object fields is done via registered PropertyEditors in Spring. The same PropertyEditors will also be used for displaying the form field values, converting back from the form object field type to a String. A set of standard editors is pre-registered; custom PropertyEditors can be registered in your command/form controller's "initBinder" method.

The recommended Spring strategy is to implement validation logic in a separate object, but note that this Validator is a business object that's not tied to web usage by any means! You write it for domain objects, not for web form objects. Alternatively, you can also implement validation logic in an "onBindAndValidate" method in your command/form controller class - no separate class necessary then, accepting the tradeoff that the logic is bound to the web then.

Regarding action/form objects: Spring controllers are servlet-style shared instances like Struts actions, but the command/form objects used are plain POJOs (typically existing domain objects) with either request or session scope. I do believe that shared controller instances have value, but I strongly object to having form objects that derive from an ActionForm base class.

For my taste, WebWork combines too many object roles in the action: It is controller, command/form object, validation errors holder, etc. Spring separates those object roles into individual objects. And as far as I can see, WebWork doesn't support form objects with session scope; it always binds to a new instance for each request. What about editing a Hibernate-persistent object that stores collection snapshots and possibly a version field? We need to bind to the *same instance* on re-submit here, for efficiency and detection of optimistic locking failures.

Posted by Juergen Hoeller on February 10, 2004 at 04:24 AM MST #

In my point of view, it only makes sense to instantiate new actions on each request if you bind request parameters to its bean properties (like WebWork does). If your action is just a workflow controller, binding request parameters to a separate command/form object, the action can easily be implemented as thread-safe singleton (analogous to a shared business service); just the command/form object needs to have request (or session) scope then.

Posted by Juergen Hoeller on February 10, 2004 at 04:27 AM MST #

I recently had the opportunity to take a stab at solving the ActionForm problem on a large project with multiple development teams. The solution we came up with worked very well, and saved everybody a bunch of work (except my framework development team, of course ;-). It would take more time than I have right now to give a complete overview, but in a nutshell, here's what we did: First we created a project-specific abstract base class that extends ActionForm, and implements the DynaBean interface. We added a Map to our form class to cache UI string values (both inbound and outbound), and implemented the DynaBean get() and set() methods to transparently access its values. We overrode the RequestProcessor such that prior to forwarding to the JSP, it invokes a method on the form that populates the Map of string values by recursively descending the object graph that begins at the form. Conversions are done on bean properties by instances of Formatter classes. Our framework provides a default set of bindings of Java types to Formatter classes, so that this stuff happens automagically, unless the developer specifies something other than the default (very rare in our application since we have custom value classes for money, percentages, etc., but quite easy to do, either through a Java API or XML). The Map that is built models the structure of the object graph, so that references in the JSP look exactly like they would normally (e.g., <bean:write name="foo" property="bean.nestedBean.someProperty"/>). When Struts (using beanutils) binds inbound values, the beanutils code sees that the form is a DynaBean and invokes it's get() method to get the initial value in the keypath. We designed our implementation to lie about the underlying data types (telling beanutils that they're all Strings or arrays of Strings) to avoid ConvertUtils conversions. We then added a bit of code to our RequestUtils processValidate() method to invoke a method on our form class that binds the values from the Map to the real objects. Our framework again finds the approriate Formatter classes by introspecting the property types (don't worry, we cache all this stuff, so the overhead is minimal). Any time a conversion error is encountered, the framework automatically adds an ActionError with an appropriate error message. If there are no conversion errors, the form calls the regular validation code. This design allows us to use POJOs in our ActionForms, and automatically handles conversion errors and formatting. You can actually do an even simpler version of this with beanutils 1.7, by subclassing BeanUtilsBean and PropertyUtilsBean to allow values to be diverted to the Map of strings (DynaBean's a pretty big interface, so there's a lot more you have to do to use that approach, though it's arguably a more robust way to do it). On the other hand, if you don't *have* to use Struts (a lot of projects mandate it), then by all means look at Tapestry. I haven't worked with it myself, but I have used WebObjects (which takes a similar approach) quite a bit, and it's one tenth the work/ten times the flexibility of doing stuff with *any* JSP-based framework.

Posted by Jonathan Lehr on February 11, 2004 at 06:21 PM MST #

Oops, I'm reposting my previous comment -- I forgot to insert paragraph tags, and it look pretty unreadable without them. :-) I recently had the opportunity to take a stab at solving the ActionForm problem on a large project with multiple development teams. The solution we came up with worked very well, and saved everybody a bunch of work (except my framework development team, of course ;-). It would take more time than I have right now to give a complete overview, but in a nutshell, here's what we did:

First we created a project-specific abstract base class that extends ActionForm, and implements the DynaBean interface. We added a Map to our form class to cache UI string values (both inbound and outbound), and implemented the DynaBean get() and set() methods to transparently access its values. We overrode the RequestProcessor such that prior to forwarding to the JSP, it invokes a method on the form that populates the Map of string values by recursively descending the object graph that begins at the form. Conversions are done on bean properties by instances of Formatter classes. Our framework provides a default set of bindings of Java types to Formatter classes, so that this stuff happens automagically, unless the developer specifies something other than the default (very rare in our application since we have custom value classes for money, percentages, etc., but quite easy to do, either through a Java API or XML).

The Map that is built models the structure of the object graph, so that references in the JSP look exactly like they would normally (e.g., <bean:write name="foo" property="bean.nestedBean.someProperty"/>). When Struts (using beanutils) binds inbound values, the beanutils code sees that the form is a DynaBean and invokes it's get() method to get the initial value in the keypath. We designed our implementation to lie about the underlying data types (telling beanutils that they're all Strings or arrays of Strings) to avoid ConvertUtils conversions.

We then added a bit of code to our RequestUtils processValidate() method to invoke a method on our form class that binds the values from the Map to the real objects. Our framework again finds the approriate Formatter classes by introspecting the property types (don't worry, we cache all this stuff, so the overhead is minimal). Any time a conversion error is encountered, the framework automatically adds an ActionError with an appropriate error message. If there are no conversion errors, the form calls the regular validation code.

This design allows us to use POJOs in our ActionForms, and automatically handles conversion errors and formatting. You can actually do an even simpler version of this with beanutils 1.7, by subclassing BeanUtilsBean and PropertyUtilsBean to allow values to be diverted to the Map of strings (DynaBean's a pretty big interface, so there's a lot more you have to do to use that approach, though it's arguably a more robust way to do it).

On the other hand, if you don't *have* to use Struts (a lot of projects mandate it), then by all means look at Tapestry. I haven't worked with it myself, but I have used WebObjects (which takes a similar approach) quite a bit, and it's one tenth the work/ten times the flexibility of doing stuff with *any* JSP-based framework.

Posted by Jonathan Lehr on February 11, 2004 at 06:24 PM MST #

Jonathan Lehr, have you advise to struts community your solution of a problem ? I think it is good enought to include it in next version of struts. Is your patch to struts free for download ?

Posted by sub4x on February 24, 2004 at 02:18 PM MST #

Being frustrated with some of the Struts limitations AND being very charmed with the nice architecture of Maverick, I created an extension project for Maverick - as Maverick itself is quite basic - some eight months ago. By now it's stable and public, and I think it solves some of the issues mentioned in this discussion. It would be great if you took a look at Baritus. Do any of you people have real-world experience with Tapestry? It looks really nice, but one of my concerns would be that it might be more difficult for page-oriented developers to work with.

Posted by Eelco Hillenius on April 09, 2004 at 04:27 PM MDT #

>A Question for Matt Ho, or any other web workers that stop by. If I have a date field >in my POJO how gracefully does WebWork2 handle values that aren't in the proper >date format? Does it capture the error and redisplay to the user, or do we get 500 >errors and ugly stack traces? Also can the improper date value that was entered be >redisplayed in the page so that the user can correct his error (I'm guessing not since >the value never got into the date property since it wasn't a valid date)? this is a bug in framework but it can be resolved as below... <#if parameters.name?exists> <#assign oldvalue="stack.findValue(parameters.name)" value="${oldValue}"/> </#if> insert above code in your ftl. in case of validation failure or conversion error. old value be in place with error message. Sudhir Nimavat

Posted by Sudhir Nimavat on January 23, 2007 at 03:53 AM MST #

<#if parameters.name?exists> <#assign oldvalue="stack.findValue(parameters.name)" value="${oldValue}"/> </#if> insert above code in your ftl. in case of validation failure or conversion error. old value be in place with error message

Posted by 203.88.149.218 on January 23, 2007 at 03:55 AM MST #

<#if parameters.name?exists> <#assign oldvalue="stack.findValue(parameters.name)" value="${oldValue}"/> </#if> insert above code in your ftl. in case of validation failure or conversion error. old value be in place with error message. Sudhir Nimavat

Posted by sudhir nimavat on January 23, 2007 at 03:56 AM MST #

dsafkhadsf

Posted by 122.169.244.87 on August 19, 2008 at 12:04 AM MDT #

Post a Comment:
  • HTML Syntax: Allowed