Sunday, March 11, 2012

Play 2.0 and Salat (MongoDB DAO provider)


Play 2.0 using MongoDB document storage through Salat


In this post, I'll cover some points and library that ease such use case.

I won't go into deep details about Play and MongoDB, instead I'll jump straight to the MongoDB usage.

Let's talk a bit about Salat

Salat

This free library available on github here (and deployed on ivy repo, so that easily usable with sbt and Play) is able to deal with case classes and MongoDB as we'd do with JPA.

That say, case classes can be used directly for storing document by simply declaring a DAO and with the help of some annotations (not mandatory).

Grater

Salat as the notion of Grater that is responsible to the de/serialization of a case class. This through two simple functions, let have a Grater for the type T:
  • asDBObject(t:T) : returns a MongoDB representation of t
  • asObject(dbo:DBObject) : returns a T instance based on the provided MongoDB content

The latter thing to note is how to create such Grater? What do we have to do? The answer is almost nothing, in the package com.novus.salat is available the very handy method grater[Y <: AnyRef].

What that grater method does is to parse your case class provided as the method generic, and create the related Grater instance. The important thing to note at this stage is that the latter Grater is cached for further needs.

So now, you're already able to deal with you case class. What is missing is the DAO part, that will ease again your job.

SalatDAO

Salat provides another handy structure named SalatDAO. This trait defines all lifecycle operations that you might need for your domain objects.

Its usage is very simple, you just have to define an object that extends this trait by giving the Domain Specific class and Id type for your structure. The last parameter it needs is a reference to the collection.

Here is an excerpt I pulled from the salat wiki:
object UserDao extends SalatDAO[User, ObjectId](collection = DB.connection("users"))

Play 2.0

Having covered the basic of Salat, it's time to use it in our Play 2.0 application.
There is a cool wiki page, that I wished have discovered before which explain how to use salat with Play 1.2.x. I recommend you to read it, even if I'm gonna cover some of the important steps here too.

Dependencies

This step is more easy that for the previous version of Play. Because now the only two things required are:
  • add the novus repo
  • add the deps to salat
Both in the Build.scala in the project folder of your Play 2.0 app.

**Edit (on 21th June 2012)**
Before you continue, this post that explains some basics about how to deal with Salat and Play, you can now choose to simply move to this module https://github.com/novus/salat/wiki/SalatWithPlay2.
It introduced the tricks that I'll explain below, and add amazing functionalities for Model and DAO creation.
So starting at this point, it's no more mandatory to read this post... unless you're curious ;-)
**end of EDIT**

Context
Here is the main thing I have to discuss here: Salat makes intensive usage of a structure named Context which holds a reference to what have been processed along the classes and structures.

Such instance is created by default in the package object com.novus.salat.global, and the quick start of Salat recommend to import it along with salat and annotations. Don't!

Doing so will fail when using Play 2.0 in DEV mode (only) because of the cool-hot-reload-on-change feature of Play. The specific case where it will fail is the following:
If you want to keep a static reference to the Grater instance of a specific case class.


Why? Because of the (needed for sure!) graters' cache which keeps a reference to the Class instance.
But this class instance might change on bytecode refresh, moreover the fact that Play has a specific ClassLoader for that (ReloadableClassLoader).

The result is incomprehensible errors when you change your code, which errors saying the there is a mismatch between classes that you even not change yet... 

The solution (which is referred in the wiki page I told above)

Instead of importing com.novus.salat.gobal._ which only contains an implicit definition of the Context object to use in Salat core system, create a new one using the correct ClassLoader.


In the above Gist, we can see that we simply created a Context that will refer to the provided ClassLoader by the Play app itself.

That will keep enabled the class reloading, without impacting caches instances.

That's all folks!

Now you're ready to use both Play 2.0 and Salat without messing around with conversion between DSM and MongoDB and so on.
Have a gode work.

6 comments :

  1. Great post with lots of info. Thanks mate.

    ReplyDelete
    Replies
    1. Thanks, glad to see that I helped you, a bit ^^

      Delete
  2. Thank you! This problem was driving me crazy. One question, whre do I put the salatcontext.scala file?

    ReplyDelete
  3. anywhere you want,

    Let's say you're dropping it in 'package org.me.myself.and.i"

    Rather than importing 'import com.novus.salat.gobal._' do
    'import package org.me.myself.and.i.salatctx._'

    But I'll go check if something has been done in the module... otherwise it shloud and I'll do it probably ^^

    ReplyDelete
    Replies
    1. Having checked in the plugin the 'se.radley.plugin.salat' object
      you'll find the required implicit ctx.

      So using the play-module for salat should do the trick !
      https://github.com/leon/play-salat

      Delete
    2. I grabbed the latest (1.0.3) and it didn't fix the issue. From you comment I assumes that adding the manual implicit is no longer necessary - did I read it incorrectly?

      Delete