Some random Grails thoughts and tipps

December 4, 2008 by Michael

Lately i’ve been rambling and ranting a lot on twitter about the Grails framework.

To my surprise, many other developers actually read this tweets and helped me out on some problems. Thanks a lot gals and guys, i really appreciate that. Me rambling isn’t meant to be personal at any time, i guess you know how easily one gets frustrated with too less time and too much stuff to do.

Anyway, here are some shortcuts that could eventually be helpful. I’m gonna add more to this list the next days:

Enabling hibernate filters in the grails session

Took me a little digging through the source code, but i came up with the following idea:

import org.springframework.transaction.support.TransactionSynchronizationManager;
 
class SecurityFilters {
  def sessionFactory
  def filters = {
    login(controller:'*', action:'*') {
      before = {
        // get your user id somewhere
        def whatsoeveruserId = 0
        def sessionHolder =  TransactionSynchronizationManager.getResource(sessionFactory);     
        sessionHolder.getSession().enableFilter("filterByOwner").setParameter("currentUserId", whatsoeveruserId);
      }
    }
  }
}

I want to have some kind of rowlevel security through a Hibernate filter. Through dependency injection i get hold of the sessionFactory and through the TA Manager, i get the current session on which i can enable my filter.

Doing this in a Grails filter, i can combine this with some kinda login mechanism and i’m good to go.

Whitelisting attributes through bindData

To me it’s a bad idea using blacklisting on data binding as i can and will forget attributes that must not be updated through a webform.

With Marc i found the following solution:

bindData(entity, params, entity.properties.collect{it.key} - ['foo', 'bar'])

That way only attributes foo and bar gets updated.

Anyway, with Grails 1.1 this won’t be necessary anymore as Graeme anonced.

Graeme was so kind to comment on this: This feature is already in 1.0.x, i just didn’t find it, have a look at the docu at The Web Layer.

bindData(entity, params,  [include:['foo', 'bar']])

Updates on 2008/12/9

Adding custom errors to a domain class

The grails reference has a handy example for adding custom errors to domain classes, have a look here. This works quite well except that all other errors from databinding are mysteriously gone.

For me, the following steps worked to update a user (change some persistent attributes and the transient attributes password and passwordConfirmation):

bindData(anwender, params, [include:['name', 'vorname', 'password', 'passwordConfirmation']])
 
if(params.password != "" && params.password == params.passwordConfirmation)
  anwender.hashPassword() // As alway, never ever store plaintext passwords ;)
else if(params.password != "") {
  anwender.validate() // IMPORTANT without that step, possible other errors from bindData vanished
  anwender.errors.rejectValue('password', 'user.anwender.passwords_doesnotmatch')
}

Afterwords, hasErrors() show all errors, i.a. non nullable fields and the like.

More thoughts

I somewhat used to hibernate and come along very well with it, even though i’m actually a SQL fan. I guess if my inside into the Spring Framework would be a little bit deeper, some areas wouldn’t be hard to understand.

On the other hand i think that Grails does a great job for J2EE based development and it should do so even more. As always, there is the law of leaky abstractions, but the whole butload of stuff that is the J2EE stack should be abstracted away.

Updates on 2009/2/6

Grails 1.1-beta3

I use hibernate validator in my domain classes (that i created outside of rails as hibernate annotated classes) and i got

java.lang.NoSuchMethodError: org.hibernate.event.PreInsertEvent.getSource()Lorg/hibernate/engine/SessionImplementor

on every insert and update. Hibernate validator 3.0.0.GA is incompatible with the Hibernate version in Grails 1.1-beta3. Problem was gone after upgrading validator to 3.1.0.GA.

Some other stuff:

  • Installed plugins are obviously gone. i.e yui plugin is still in the application folder, it needs to be reinstalled after upgrade (grails install-plugin yui)

    Ok, i see this was done on purpose: “Plugins are now stored in your USER_HOME directory. You will need to re-install your plugins or run” (from the beta2 release note). Not a good decision making this a default imho. I like having my apps pinned to specific plugins.

  • The message method for doing I18n in controllers used to be available in filters. This method seems to be gone. No solution for that so far.
  • Values not bound in a form are not null anymore but 0 in case of numeric values. Bummer! Actually my bad.
  • Some problems solved.

12 comments

  1. Graeme Rocher wrote:

    On bindData, even with 1.0.x you can do this:

    bindData(entity, params, [includes:[‘foo’,’bar’]])

    Obviously the 1.1 ways more elegant, but the above does the trick too

    Posted on December 4, 2008 at 1:36 PM | Permalink
  2. Michael wrote:

    Oh, thats cool…

    But isn’t document or is it?

    Anyway, thanks 🙂
    (although, i like my solution, it brings more Groovy knowledge to me *g*)

    Posted on December 4, 2008 at 1:44 PM | Permalink
  3. Michael wrote:

    Graeme, sorry, but that doesn’t work.

    It updates all attributes (Grails 1.0.4), just checked it.

    Posted on December 4, 2008 at 1:48 PM | Permalink
  4. Graeme Rocher wrote:

    It is documented here: http://grails.org/doc/1.1/guid.....%20Binding

    But its missing from the reference info on bindData, should probably fix that

    Posted on December 4, 2008 at 1:51 PM | Permalink
  5. Michael wrote:

    Ah, i see…

    And it doesn’t work due to a typo of yours, it must be include, not includes 😉

    But it’s indeed exactly what i needed. Really great, thanks!

    Posted on December 4, 2008 at 1:54 PM | Permalink
  6. Burt wrote:

    You can tighten up the filter code a bit by removing TransactionSynchronizationManager and using SessionFactory.getCurrentSession() (which finds the active transaction/session for you):

    sessionFactory.currentSession.enableFilter(“filterByOwner”).setParameter(“currentUserId”, whatsoeveruserId)

    Posted on December 4, 2008 at 3:12 PM | Permalink
  7. Would you mind sharing where/how you setup your hibernate filters? Good work!

    Posted on December 4, 2008 at 3:42 PM | Permalink
  8. Michael wrote:

    Burt: Sorry, but no. I tried this but as told in the javadoc:

    Obtains the current session. The definition of what exactly “current” means controlled by the CurrentSessionContext impl configured for use.

    I get a session, not the session participating in the current transaction as initiated by grails. I tried this first, the session complained “no filter without transaction”, i initiated one but it was a different than used by gorm.

    Colin: No, not at all 🙂

    I use Hibernate with Hibernate Annotations, that is my model classes are completely grails independent. I just defined my filter in the entity class like so:

    @FilterDef(
    		name="filterByOwner",
    		parameters={
    				@ParamDef(name="currentUserId" , type="long")				
    		}
    )
    @Filters({
    	@Filter(
    			name="filterByOwner", 
    			condition="(:currentUserId  = (/* some validation */))"
    	)
    })

    Turning them on in a Grails filter or interceptor works just fine.

    Posted on December 4, 2008 at 3:48 PM | Permalink
  9. Burt wrote:

    Huh, that’s weird – I’ve always used sessionFactory.currentSession. It’s true that it depends on CurrentSessionContext, but in a Spring app it’s a SpringSessionContext and in Grails it’s FlowAwareCurrentSessionContext (a subclass), so it works.

    I added this code to a filter and I’m getting the same session both ways:

    def sessionHolder = TransactionSynchronizationManager.getResource(sessionFactory)
    def sessionFromTSM = sessionHolder.session
    def sessionFromSF = sessionFactory.currentSession
    println “sessionFromTSM: ${System.identityHashCode(sessionFromTSM)}, sessionFromSF: ${System.identityHashCode(sessionFromSF)}, ${sessionFromTSM.is(sessionFromSF)}”

    Posted on December 4, 2008 at 5:02 PM | Permalink
  10. Michael wrote:

    Weird indeed…

    But my grails app also complains

    2008-12-04 16:53:24.970::INFO:  No Transaction manager found - if your webapp requires one, please configure one.
    

    and i actually don’t know how to get rid of it and it seems there is one… as sessionFactory.currentSession has no transaction and the other has…

    Also, my sessions differ: sessionFromTSM: 10412273, sessionFromSF: 27313026, false

    Posted on December 4, 2008 at 5:09 PM | Permalink
  11. Burt wrote:

    Not sure why it’s different, but the “No Transaction manager found” warning is from Jetty, complaining that there’s no server-configured TM, but that’s fine since Grails/Spring handle transactions.

    Posted on December 4, 2008 at 5:14 PM | Permalink
  12. Michael wrote:

    “but that’s fine since Grails/Spring handle transactions.”

    Thats great news to me, one thing more i can discard from my todo list.

    The other stuff… Well, weird things happens, also in Rails world 😉

    Maybe the fact that i use GrailsAnnotationConfiguration instead of the normal config class has something to do with it…?

    Posted on December 4, 2008 at 5:18 PM | Permalink
Post a Comment

Your email is never published. We need your name and email address only for verifying a legitimate comment. For more information, a copy of your saved data or a request to delete any data under this address, please send a short notice to michael@simons.ac from the address you used to comment on this entry.
By entering and submitting a comment, wether with or without name or email address, you'll agree that all data you have entered including your IP address will be checked and stored for a limited time by Automattic Inc., 60 29th Street #343, San Francisco, CA 94110-4929, USA. only for the purpose of avoiding spam. You can deny further storage of your data by sending an email to support@wordpress.com, with subject “Deletion of Data stored by Akismet”.
Required fields are marked *