• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Validation and copying

 
Ranch Hand
Posts: 580
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'm writing a ui framework. This framework includes validation.

I have a problem, when I edit an entity with my ui, I scrape the values of some the fields and put them in my ui components, texboxes, combos etc. The user makes some edits and presses ok. I then validate his response. So I update my entity with the dirty fields and validate it. If the entity is valid, great, however if it is invalid I've now dirtied my entity when I didn't want to. If the user then aborts the edit the app is a bad state.

So it seems I need to copy the entity before I validate it. I was hoping to avoid this, (particularly clone!) and provide as much support in my framework as possible to make it as easy as possible. However I don't see a solution other than providing an abstract copy() method at a suitable place for developers to implement. This is because the depth of the copy required could vary depending on the edit being carried out.

Hmmm...this (ie the abstract copy method) seems feasible but I wonder if there are other solutions or common approaches to this problem?

thanks, D.
 
author
Posts: 14112
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You might want to take a look at https://binding.dev.java.net/ - especially its BufferedValueModel.
 
Sheriff
Posts: 7001
6
Eclipse IDE Python C++ Debian Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'd probably think about this a bit more before jumping immediately to the idea of copying the whole entity for every "trial edit".

First you really need to get some idea of how often "good" edits are likely to occur compared with "bad" ones. If there are many more "good" ones I suggest you don't want to burden all those good ones with unneeded extra copying.

Possibilities I can think of off the top of my head include:

  • A proxy/decorator which wraps the entity and replaces the mutators with ones which sit "on top" of the actual data, and responds with either the new data (if set), or the original data when validated. If/when validation passes, run a method on the proxy to load all the new values into the entity for storing.
  • Each time you change a value in your entity from this UI, take a note of the old value, and "roll back" the stored old values if validation fails.
  • Put your validation in a third place, with a method which validates an entity and a set of updated values, without modifying the entity itself.
  • if validation fails, just throw away the entity and reload when necessary from the stored version.


  • Any of these sound likely?
     
    (instanceof Sidekick)
    Posts: 8791
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    I'm not clear on what architecture we're in. Can setField() on the entity do the validation? If there is cross-field editing too complex for that, can you make a "form" object to hold all the data for one update and validate the form before you start touching the real entity?
     
    Don Kiddick
    Ranch Hand
    Posts: 580
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator


    I'd probably think about this a bit more before jumping immediately to the idea of copying the whole entity for every "trial edit".

    First you really need to get some idea of how often "good" edits are likely to occur compared with "bad" ones. If there are many more "good" ones I suggest you don't want to burden all those good ones with unneeded extra copying.



    I can see bad edits happening reasonably often. In my domain, I can't see unnecessary copying being a performance problem, or were you alluding to other related problems?


    Any of these sound likely?



    Two problems :
    * I want the validation logic to be contained with the domain object. This is because the same logic may well be used on the server side.
    * Validating single fields is problematic. My ui may be editing a contact's attributes but the validation may depend on attributes not been edited such as the 'location'. Also there maybe interelationships between the fields, so I cannot just validate the fields in isolation.


    if validation fails, just throw away the entity and reload when necessary from the stored version.



    This is a possiblity although it doesn't seem much better than the copy approach


    If there is cross-field editing too complex for that



    Yes.


    can you make a "form" object to hold all the data for one update and validate the form before you start touching the real entity



    Thing is the cross-field validation may include fields that are not edited.

    Seems I'm a bit stuck with copying or reloading my entity. Oh well! Thanks for your replies.

    thanks,
    D.
     
    Frank Carver
    Sheriff
    Posts: 7001
    6
    Eclipse IDE Python C++ Debian Java Linux
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    From what you have just clarified, it still looks like my suggestion A proxy/decorator which wraps the entity and replaces the mutators with ones which sit "on top" of the actual data, and responds with either the new data (if set), or the original data when validated. If/when validation passes, run a method on the proxy to load all the new values into the entity for storing. might be a contender.

    In case my descrption was unintelligible, here's a (very simple) example:

    First the example interface and entity object we will be working with:



    The above entity is not particularly interesting, although it does have get/set methods and a validate method which applies multi-field validation. I hope that's representative of some of your domain code.

    Now let's look at a "wrapper" class:



    Note that the "trial" class has no validation, it uses the existing validation in MyEntity. The validation has been written in terns of the public accessor methods (good practice anyway), so it uses the overridden ones from the trial wrapper.

    So, when you need to "try" an update on an entity, start by "wrapping" it, then call whatever methods your UI would normally do. Then validate the trial. If the trial passes validation, push any updated values through to the real entity. If the trial fails validation, the original entity can remain unchanged:



    Does that make any more sense?
     
    Don Kiddick
    Ranch Hand
    Posts: 580
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Does that make any more sense?



    Yes it does and it is definitely food for thought. Although it seems like a variant on the copying strategy really.

    thanks! Tom
     
    Stan James
    (instanceof Sidekick)
    Posts: 8791
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    That's pretty neat, Frank. Have you used this or seen it in the wild? It's akin to the "copy" solution but only populates the values that are changing, which may give validation a clue about what to validate, too.
     
    Stan James
    (instanceof Sidekick)
    Posts: 8791
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Overnight this thread reminded me of my last mainframe application. It held a representation of what the user was working on in "model" space whether it was good, bad or ugly. Then this line from the OP stuck out:


    If the entity is valid, great, however if it is invalid I've now dirtied my entity when I didn't want to.


    What bad thing happens because of a dirty entity? If it's shared among users one user could mess others up. But if it belongs to one user's session, maybe it's ok. Maybe a user can hold an invalid state and work on it until it's clean or they abandon it, but they can't save an invalid state to the database.

    I'm guessing you've already been down this road and rejected it. Which leaves me curious about why.
     
    Frank Carver
    Sheriff
    Posts: 7001
    6
    Eclipse IDE Python C++ Debian Java Linux
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    Stan wrote: What bad thing happens because of a dirty entity? If it's shared among users one user could mess others up. But if it belongs to one user's session, maybe it's ok. Maybe a user can hold an invalid state and work on it until it's clean or they abandon it, but they can't save an invalid state to the database.

    Good question.

    I was guessing that somewhere in this is an ORM solution which (somehow) automatically writes back "dirty" entities to the db. This is obviously a risky position to be in with several entities loaded, some "dirty", but some of those dirty ones potentially invalid and unsuitable for writing back.

    Perhaps it might be possible to modify the entity code or the ORM code to only write these back if they are valid. It sounds trickier than writing all dirty entities back, though.
     
    Don Kiddick
    Ranch Hand
    Posts: 580
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    What bad thing happens because of a dirty entity? If it's shared among users one user could mess others up.


    Only shared by one user.


    But if it belongs to one user's session, maybe it's ok. Maybe a user can hold an invalid state and work on it until it's clean or they abandon it, but they can't save an invalid state to the database.



    I was thinking it would be difficult to abandon, because the same object could be referenced from multiple places in the application. However now I realise, that rather than abondoning the instance, I can just abandon it's state, ie. refresh the instance.

    That helps a lot! Thanks!
    D.
     
    Stan James
    (instanceof Sidekick)
    Posts: 8791
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    ... referenced from multiple places in the application ...



    That might be something to talk about, too.

    Sounds like you have some ideas on how to move forward. Keep us posted how it goes.
     
    Don Kiddick
    Ranch Hand
    Posts: 580
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    ... referenced from multiple places in the application ...
    That might be something to talk about, too



    for example the entity 'Don Kiddick' could be referenced by a table model in the 'manage contacts screen'. It also might be in a list panel in the 'employee screen' and also in the 'required attendees' box on the 'appointment screen'.

    In my implementation the various ui models (table models, list models etc) work by adapting the domain objects. Therefore each view on an entity involves holding references to that entity. When the entity changes, the ui models change too. I end up with domain objects (possibly) referenced in lots of places.

    Another approach is to scrape the display values of the domain objects to create the ui models. In this pattern a view on an entity does not neccessarily mean you hold an object reference to it.

    Does that make any sense?

    D.
     
    Stan James
    (instanceof Sidekick)
    Posts: 8791
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    That's a fun challenge. You probably don't want to update the other views of an entity until the edit passes validation and the user commits?
     
    Frank Carver
    Sheriff
    Posts: 7001
    6
    Eclipse IDE Python C++ Debian Java Linux
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator
    It's the classic two-phase transaction approach but at an object level.

    I still stand by my suggestion above of a "buffer" around the problematic objects. Each user of such a shared entity actually talks to a buffer/decorator which adds "commit" and "rollback" methods (commit pushes the buffered data through to the real object, as describd above, and all a rollback needs to do is clear the "has" flags and the client will go back to using the unchanged data in the underlying entity.

    This feels like the way a database would do it. You don't want to copy the whole db every time a transaction is started. You just want a thin "ghost" layer of changed values, only visible to the transaction involved.

    I guess the real challenge is whether between us we can come up with a general solution to this (perhaps using automatic proxying) which will simply wrap such a transaction around any object. Anyone want to try it?
    [ September 29, 2006: Message edited by: Frank Carver ]
     
    Don Kiddick
    Ranch Hand
    Posts: 580
    • Mark post as helpful
    • send pies
      Number of slices to send:
      Optional 'thank-you' note:
    • Quote
    • Report post to moderator

    Originally posted by Frank Carver:

    I guess the real challenge is whether between us we can come up with a general solution to this (perhaps using automatic proxying) which will simply wrap such a transaction around any object. Anyone want to try it?



    This would definitely be the ideal. I'm writing a framework so anything I can provide 'for free' is a bonus. I'm not convinced it's possible though.

    At a high level I see two approaches :

    1. Try to validate and if fail reload the instance in place.
    2. Copy the entity. If validation fails, throw away the copy. If it succeeds update the 'true' entity and update.

    (I'm lumping the decorator solution proposed by Frank in 2)

    #1 :
    Is performant if entities fail validation rarely.
    Is easy for me to implement a default implemention which will work in 90% of cases. This is because we mainly use hibernate and can simply ask it to refresh an object, which loads its state from the db.
    Will not work well for 'dynamic validation'. That is a text box that gives visual feedback on the 'validity' of the form as it is being updated. In this case a textbox that requires a string of minimum length 10 characters, will validate at least 10 times.

    Hmmm...that last point is a big one, cause that is something we may want to do.

    #2
    Is performant if validation fails regularly.
    Will work well for 'dynamic validation'.
    Is not easy for to provide a default implementation.

    I don't think it's possible to create a 100% effective automated copy strategy because you don't know how deep you need to copy. I 90% stragey would be good though. I see two possibilities :
    a) copy the object using serialization or other reflection based technique.
    b) autogenerate a decorator a la Frank's solution. Dynamic proxies seem out as they only work on interfaces. So that leaves cglib or some other reflection jiggery pokery.

    D.
     
    reply
      Bookmark Topic Watch Topic
    • New Topic