Tuesday, March 15, 2011

Monido, What?

I've been working on a little project called Monido, which is simply a monitoring service for Scala. It can monitor anything from a file, a directory, a DB table, or web page. I think you get the picture.

The project, however, only ships with a single reference implementation to monitor the file system for changes. I'm using it for this very post at this moment. More on that later.

The Program

I explained in an earlier post that I write my blog entries in markdown, and store them in a git repo. I have since modified that BloggerBot script to only inject the html source in my clipboard when it runs. The command looks like:

java -jar blogbot.jar ~/notes/posts/24.md

Monido can automate this procedure:

monido ~/notes/posts -e java -jar blogbot.jar

Every time I save this post, I get:

Writing conversion to your clipboard ....
Done.

This saves me a few key strokes!

The -e argument for monido is essentially like using the pipe for a Unix system. The full file path of the changed file is passed to the terminal argument, and immediately run. Monido defines itself as a pure JVM, cross-platform solution to file monitoring.

monido ~/some/other/thing -e ls -l

Monido can spawn recursive monitors, although this practice is discouraged on using a large directory tree. Your operating system does this for you, so you should use that instead. Jnotify is a good Java wrapper around these OS events.

Monido is not just a file monitoring service, though! This is where the good part begins.

The Library

Monido's infrastructure is very flexible, being that it's comprised of two (optionally three), components. These components are:

  1. PulsingComponent
  2. MonitorComponent
  3. ListeningComponent (optional)

The explanation of these components are given on the Monido github page:

A PulsatingComponent is something that wakes the MonitorComponent to do something. A PulsatingComponent could wake it at set intervals, once a day, manually, etc. The MonitorComponent will simply monitor whatever it was told to monitor (The FileMonido reference implementation monitors the file system.) Optionally, the Monitor can notify a client of a change (or anything else really) by making use of the ListeningComponent.

Lots of moving parts that have arbitrary dependencies make it a great candidate for some DI.

Now, I will show you how to do a DBMonido, just for fun.

import com.github.philcali.monido._

// First we need to define the DBMonitor by extending the
// MonitorComponent.
trait DBMonitorComponent extends MonitorComponent {
  this: ListeningComponent[Adapter] =>
  // Let is connect via a db string
  class DBMonitor(db: String) extends SimpleMonitorDevice{
    def connect: Adapter = // connect to db
    def pulsed {
      // Making a fictional query
      val results = connect.query("SELECT COUNT(id) as count from entries")
      if(results("count") >= 9000) listener.changed(connect)
    }
  }
}

// Define Monido now
object DBMonido extends Monido with ListeningComponent[Adapter]
                               with DBMonitorComponent 
                               with PulsatingComponentImpl {

  // This object will NOT compile until dependencies are met
  val listener = new MonidoListener {
    def changed(db: Adapter) {
      // Clear records
      db.query("TRUNCATE entries")
    }
  }

  // the MonitorComponent expects a monitor value to be defined
  val monitor = new DBMonitor("jdbc:sqlite:test.db")
  // Next the PulsatingComponent expects a pulsar value to be defined
  // Sneal peak at 0.2 Scalendar syntax
  import com.github.philcali.scalendar._
  val pulsar = new Pulsar(1.day.inMillis)
}

DBMonido.start
// And we're off!

This is a pretty silly example of checking a record once a day for the change you're looking for, but now I'll show you how easy it is to configure the Monido. Say now I wanted to use a different pulsating implementation. I'll use a fictitious CronPulsing implementation that uses a cron string for its configuration.

// Re-define the DB monido
object DBMonido extends Monido with ListeningComponent[Adapter]
                               with DBMonitorComponent 
                               with CronPulsing {
  ...
  // same as before. The monitoring component worked great, so
  // leave it alone.

  // Sneak peak at the cron project
  import com.github.philcali.cron._
  val pulsar = new CronPulsar("every Sunday at midnight".cron)
}

DBMonido.start
// And we're off!

So there's your Monido. I don't know how useful it could be to everyone else, but I find myself in need of Monido's pretty often.

-- Philip

2 comments:

  1. smells like the cake pattern to me

    Got a Scala question for you. In this line:
    def connect: Adapter = // connect to db
    The capital A in adapter is throwing me off. What does this line do?

    ReplyDelete
  2. Oh this wasn't meant to be confusing, but I see how it was...

    I was making up a fake db adapter code that I was too lazy to come up with fake implementation for or say that it's fake.

    So in the above context, the method "connect" would return a db adapter defined by the db string. This fictional db Adapter object has the "query" method that interacts with a relational database.

    I wrote this post on little sleep... I should probably never do that :P

    ReplyDelete