wood burning stoves 2.0*
The moose likes Struts and the fly likes Struts 2: Is Model Driven fundamentally flawed in most uses? Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Frameworks » Struts
Bookmark "Struts 2: Is Model Driven fundamentally flawed in most uses?" Watch "Struts 2: Is Model Driven fundamentally flawed in most uses?" New topic
Author

Struts 2: Is Model Driven fundamentally flawed in most uses?

Eric Nielsen
Ranch Hand

Joined: Dec 14, 2004
Posts: 194
I've been exploring the Model Driven pattern/interceptor in Struts 2 a lot recently and I've come to the conclusion that its fundamentally flawed for the vast majority of its normal use cases. I'm starting to dig into the source to see if its possible to fix this problem at the framework level, rather than with very hackish workarounds. Here's the specific problem:

When using Model Driven Action with visitor validation directly on the model, coupled with a JPA provided, when the validations fail, if there is any lazy-load to provide the redraw of the form, the invalid data is committed to the database as part of the flush before query synchronization.

Model Driven + Visitor Validation is probably near 100% of the ModelDrive use cases. Struts 2 + JPA Provider (or raw Hibernate) is also probably at least a majority of applications, so I don't think this is a rare/odd niche.

The error happens because:
1. Prepare Interceptor: loads an object from the database (ignoring the irrelevant first Params interceptor supporting this); thus the model object is bound to a persistence session.
2. (Second) Params Interceptor: injects (invalid) values into backing model -- model is now dirty
3. Validator interceptor: recognizes that the object is invalid (can also occur due to failed type conversion)
4. Control is passed to the INPUT flow
5. During re-render of the form, a request for a lazy-load on a collection on the object, OR a request for a getAll/etc style call on the action to support a select list triggers a flush of the persistence session to ensure that the read doesn't contain stale data -- flushes the dirty and invalid data to the DB

(Here's a link to a thread from last year describing the same problem with workarounds:
http://www.nabble.com/ModelDriven-CRUD-validation-failure-still-causes-JPA-update-td12987242.html)

None of the workaround appear to offer both correct behavior and ease of use; and in all cases its adding application logic to handle what's really a framework problem.

However I do feel that
a) validations on a model should be viewed as invariants
b) data injection from the framework to the model should be atomic
c) we need access to the original request values for form redisplay (of course with ability to cleans values for XSS reasons)

If Model Driven were to satisfy these criteria, then it wouldn't cause JPA to do the wrong thing. And would still be correct in other persistence (or lack of persistence) environments.

In part the errors comes from violating the SRP by combining the requirement of form redisplay of entered values on top of the regular business logic (including definition of valid objects) on the model object.

The solution I'm looking into:
changing Model driven to create a copy of the model object (detached from the persistence session), inject the parameters, validate, on success inject into the original, on fail leave the original alone. (Its a little annoying that all the changes have to happen to _other_ interceptors and use checks to see if the action is an instance of modeldriven, but I don't see any other approach to tweaking it)
stick some proxy/mock containing the sanitized, but nearly raw request parameters onto the value stack, onto of the model -- allowing redisplay of the entered form values (this is all very hand-wavy at the moment)


Has anyone else played with this?
[ April 30, 2008: Message edited by: Eric Nielsen ]
chad michael davis
Author
Greenhorn

Joined: Mar 01, 2006
Posts: 27
Interesting. Why do you think it's a framework problem? Why couldn't it be viewed as a problem with the common OSIV type implementation of persistence?

I've been thinking about this problem and wondering whether there couldn't some sort of rollback of property values when validation fails.

Good question.


Chad Davis<br />Co-author of Struts 2 in Action
chad michael davis
Author
Greenhorn

Joined: Mar 01, 2006
Posts: 27
Actually, the handling of type conversion error values might provide an interesting parallal. When type conversion fails, and the values can't even be set on the model object, they are still preserved on the ValueStack so that they can be written back to the form fields.

It makes sense that non-validated data shouldn't actually be set to domain objects. I think its a conflict of use space, where a single object is serving two roles: 1) the domain model object and 2) the target of Struts 2 data transfer

BTW, your link to the previous discussion didn't work, can you repost that?
Alaa Nassef
Ranch Hand

Joined: Jan 28, 2008
Posts: 467
Originally posted by chad michael davis:
BTW, your link to the previous discussion didn't work, can you repost that?


Just remove the end bracket from the link. The correct link is this


Visit my blog: http://jnassef.blogspot.com/
Eric Nielsen
Ranch Hand

Joined: Dec 14, 2004
Posts: 194
Originally posted by chad michael davis:
Interesting. Why do you think it's a framework problem? Why couldn't it be viewed as a problem with the common OSIV type implementation of persistence?

I've been thinking about this problem and wondering whether there couldn't some sort of rollback of property values when validation fails.

Good question.


Its not only a OSIV/OEMIV problem -- as I mentioned the implicit flush at the end of the filter isn't the only flush that can happen. Any lazy load can trigger this problem once an object has been dirtied -- this can happen in the action tier or the model tier without requiring OSIV.

There's been some discussion on the Struts Dev list, I need to go back and review as I got pulled away for a bit.

One possible solution I want to try out is the (Hibernate specific ... not JPA ) call of session.setReadOnly(entity,true) within the validator interceptor if validation fails. This should allow lazy loads to still work in the view tier, but not allow the object to be written back to the database. Of course that's going to require making the session available to some custom validator intercepter, which I don't really like. -- Rollback on failed validation would end up with a detached entity, and the loss of lazy load for use in the view when redisplaying the form.

I'll have to look into your comment about failed type conversion... since so far in my apps, failed type conversions have led to blank form items on redisplay... ie I'm not getting them off the value stack (probably because the model object is on top and it doesn't seem to know to look beneath on failure....


Here's the link to my thread (rather than the old thread) with some of the responses at the dev list:
http://mail-archives.apache.org/mod_mbox/struts-dev/200804.mbox/%3c20080424114307.ipiiqer2bxsswwkw@webmail.mit.edu%3e
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Struts 2: Is Model Driven fundamentally flawed in most uses?