Cristian Vrabie
Cristian Vrabie

Reputation: 4068

Serialize Function1 to database

I know it's not directly possible to serialize a function/anonymous class to the database but what are the alternatives? Do you know any useful approach to this?

To present my situation: I want to award a user "badges" based on his scores. So I have different types of badges that can be easily defined by extending this class:

class BadgeType(id:Long, name:String, detector:Function1[List[UserScore],Boolean])

The detector member is a function that walks the list of scores and return true if the User qualifies for a badge of this type.

The problem is that each time I want to add/edit/modify a badge type I need to edit the source code, recompile the whole thing and re-deploy the server. It would be much more useful if I could persist all BadgeType instances to a database. But how to do that?

The only thing that comes to mind is to have the body of the function as a script (ex: Groovy) that is evaluated at runtime.

Another approach (that does not involve a database) might be to have each badge type into a jar that I can somehow hot-deploy at runtime, which I guess is how a plugin-system might work.

What do you think?

Upvotes: 3

Views: 440

Answers (3)

Alex Cruise
Alex Cruise

Reputation: 7979

My very brief advice is that if you want this to be truly data-driven, you need to implement a rules DSL and an interpreter. The rules are what get saved to the database, and the interpreter takes a rule instance and evaluates it against some context.

But that's overkill most of the time. You're better off having a little snippet of actual Scala code that implements the rule for each badge, give them unique IDs, then store the IDs in the database.

e.g.:

trait BadgeEval extends Function1[User,Boolean] {
  def badgeId: Int
}

object Badge1234 extends BadgeEval {
  def badgeId = 1234
  def apply(user: User) = {
    user.isSufficientlyAwesome // && ...
  }
}

You can either have a big whitelist of BadgeEval instances:

val weDontNeedNoStinkingBadges = Map(
  1234 -> Badge1234,
  5678 -> Badge5678,
  // ...
}

def evaluator(id: Int): Option[BadgeEval] = weDontNeedNoStinkingBadges.get(id)

def doesUserGetBadge(user: User, id: Int) = evaluator(id).map(_(user)).getOrElse(false)

... or if you want to keep them decoupled, use reflection:

def badgeEvalClass(id: Int) = Class.forName("com.example.badge.Badge" + id + "$").asInstanceOf[Class[BadgeEval]] 

... and if you're interested in runtime pluggability, try the service provider pattern.

Upvotes: 3

Rogach
Rogach

Reputation: 27250

You can try and use Scala Continuations - they can give you the ability to serialize the computation and run it at later time or even on another machine.

Some links:

Continuations

What are Scala continuations and why use them?

Swarm - Concurrency with Scala Continuations

Upvotes: 1

Ant Kutschera
Ant Kutschera

Reputation: 6588

Serialization relates to data rather than methods. You cannot serialize functionality because it is a class file which is designed to serialize that and object serialization serializes the fields of an object.

So like Alex says, you need a rule engine.

Try this one if you want something fairly simple, which is string based, so you can serialize the rules as strings in a database or file:

http://blog.maxant.co.uk/pebble/2011/11/12/1321129560000.html

Using a DSL has the same problems unless you interpret or compile the code at runtime.

Upvotes: 0

Related Questions