Lift AMQP with RabbitMQ and Scala - Tutorial and Screencast
May 22nd, 2009
Admitidally, every time i’ve ever looked at the Lift codebase in TextMate, i’ve always wondered what the hell the “lift-amqp” module was for. Well, curiosity may have killed the cat but it was not enough of deterrent to me finding out and building a sample AMQP / RabbitMQ application!
AMQP you say?
Yup, thats right. AMQP – Advanced Message Queuing Protocol to the lay’ man not familiar with the acronym. Essentially AMQP is an open standard for enterprise messaging. There are several implementations of the standard, namely, Open AMQ and RabbitMQ – the latter is newer and generally considered to be the defacto implementation. Rabbit MQ is written in ERLang and has clients for Java, .NET, Ruby etc etc.
So where does Lift fit in?
Strictly speaking, it doesn’t. It just so happens that Steve J was working on the Lift team some time ago and wrote a nifty Scala wrapper around the standard (and highly mutable) Java client implementation of Rabbit MQ. To all intense purpose, “lift-amqp” can be used in any Scala application and has no other dependencies within the Lift framework.
Hand-wavy overview
Lets get down to business… I spent quite a lot of time just looking at the source code and scala-docs for lift-amqp and for the longest while couldn’t figure out what went where. Being a fairly visual person, I’ve drawn up a diagram that explains the implementation and how you (yes, you reading this) would implement your application:

A working example?
So rather than try to post lots of pictures – I thought just putting up a screen-cast explaining the components would make more sense :-)
The client listener
So from the diagram above that explains the code layout – you can see that one of the first things you need to do is subclass AMQPDispatcher. The below is my example code that connects to the RabbitMQ broker and firstly declares a queue with the appropriate parameters. If you wondering why when the 2nd client connected in my example things didn’t explode and complain about more than one exchange existing with the same name, thats because RabbitMQ is clever enough to know that if the queue / exchange already exists then it just Noop’s the requests. Its a nice feature.
class DemonstrationSerializedAMQPDispatcher[T](
queueName: String, factory: ConnectionFactory,
host: String, port: Int) extends AMQPDispatcher[T](
factory, host, port) {
override def configure(channel: Channel) {
val ticket = channel.accessRequest("/data")
channel.exchangeDeclare(ticket, "mult", "fanout")
channel.queueDeclare(ticket, queueName)
channel.queueBind(ticket, queueName, "mult", "example.*")
channel.basicConsume(ticket, queueName, false,
new SerializedConsumer(channel, this))
}
}
So, your probably thinking that this was not the class that I created an instance of during the screencast – and you’d be right. Essentially we still need a wrapper class to mask our dispatcher; the listener proper.
The purpose of the listener is to establish a connection to the broker, pass the right credentials etc. Within the listener we create an instance of the dispatcher and start its actor. We also then just have an inner class that we use for example purposes (another actor) that prints out the messages you saw in the terminal window. In reality, this would likely be passing messages to some other, external actor which handled the business process. For a full code listing see the end of this article.
The postman…
So now that you know how the example listens for messages, lets see the posting code…
val params = new ConnectionParameters
params.setUsername("guest")
params.setPassword("guest")
params.setVirtualHost("/")
params.setRequestedHeartbeat(0)
val factory = new ConnectionFactory(params)
// Create a new instance of the string sender.
// This sender will send messages to the "mult" exchange with a
// routing key of "routeroute"
val amqp = new StringAMQPSender(
factory, "macbookpro", 5672, "mult", "example.demo"
)
amqp.start
/**
* Salute the rabbit!
*/
def salute = amqp ! AMQPMessage("hey there!")
}
Little explanation needed here – essentially the string sender is just an actor that knows how to respond to AMQPMessage…. it couldn’t be simpler! I hope this has proved helpful and a good overview of the lift-amqp module.
If you would like to download the source code, you can get it from here
XMPie Marketing Console for iPhone
May 20th, 2009
Its been a long time since I posted my original application for iPhone – iDashboard – to control XMPie uProduce on the move. I’ve been busy with a bunch of other cool stuff and in the run up to the annual XMPie Users Group meeting in Las Vegas decided to make Marketing Console for iPhone!
The application syndicates reporting and analysis information about your campaigns and lets you see the latest up-to-date charting / figures wherever you are. Currently, the application is using an internal R&D build of Marketing Console; so right now you’ll have to wait before you can monitor your own campaigns on the move!
If you have feedback, just leave a comment at the bottom of the article. Enjoy…
Running Rabbit MQ on Mac OSX - Solving {badrpc,nodedown}
May 17th, 2009
Im currently exploring Rabbit MQ and had a few issues getting up and running reliably on Mac OSX. The problem wasted so much of my own free time that I thought it would be a good idea to post about it and perhaps it might help others in the future.
The Problem
The broken boots normally as the rabbitmq user defined in the system – however, when trying to connect to it using rabbitmqctl you get the following error (repeatedly):
macbookpro:~ timperrett$ sudo rabbitmqctl status
Status of node rabbit@macbookpro ...
{badrpc,nodedown}
...done.
The Solution
After many hours dabbling, and checking, checking again, rechecking my user and permissions setup, I found that it was actually to do with the way in which ERlang networks. Essentially, I was running the broker on:
rabbit@macbookpro
However, whilst I could ping the host “macbookpro” from terminal, it appears that Rabbit MQ needed it defined in the /etc/hosts file in order to work correctly.
Both strange and annoying, perhaps this will save someone some time!
URL Rewriting with the Lift Framework
May 3rd, 2009
With my on-going effort to write more documentation and articles for Lift I’ve decided to write a walk through of Lifts dispatching and rewriting mechanisms.
Before we start this discussion, its important that you know (and understand) the importance of partial functions in scala. If your not familiar, check out this article – it should fill you in on all the particulars.
Application Boot
If your not familiar with Lift or are new, you should understand that anything of consequence that changes the application environment must in someway hook into the boot-up cycle – the default looks something like this:
class Boot {
def boot {
// where lift will look for snippets
LiftRules.addToPackages("eu.getintheloop.tutorial")
}
}
Ok, so not a great deal going on there… this is however a bare-bones lift boot class. In our case, we want to add a rewrite so that the following mappings take place:
/product/some-product-link
/** maps onto **/
./webapp/product-display.html
Its important to note that rewriting is just that, its not used for redirects or any other such activity – its 100% for URI translation and interpretation.
Adding a Rewrite
Pretty much all of application configuration within Lift is done through the LiftRules object – its the central place for configuration PFs and operation vars. So, how do we use it? Well, first add the following to the top of your Boot.scala file.
import _root_.net.liftweb.http.LiftRules
Next, add the following code below the snippet package definition:
LiftRules.rewrite.prepend(NamedPF("ProductExampleRewrite") {
case RewriteRequest(
ParsePath("product" :: product :: Nil, _, _,_), _, _) =>
RewriteResponse(
"product-display" :: Nil, Map("product" -> product)
)
})
Rewrite matching in detail
So lets step through this part by part… we already know about the LiftRules object and what its for, and one of its var properties is “rewrite” – a RuleSeq – and has the notion of both prepending values, and appending them. In this case, we prepend this rewrite rule, meaning that it will execute before any other rewrite rules previously added to the rewrite var.
List[String] is used to match the incoming path – lets take a closer look at our request object and the parameters we pass. From the scaladocs, we can see that RewriteRequest object has the following signiture:
/**
* options for RewriteRequest
*/
case class RewriteRequest(
val path : ParsePath,
val requestType : RequestType,
val httpRequest : HttpServletRequest
)
/**
* options for ParsePath
*/
case class ParsePath(
val partPath : List[String],
val suffix : String,
val absolute : Boolean,
val endSlash : Boolean
)
/**
* options for requestType object
*/
GetRequest
PostRequest
PutRequest
DeleteRequest
The first argument in the RewriteRequest is a ParsePath, this is one of the primary URI matching mechanisms and enables you to define detailed paramaters on which request to match and which to ignore. Lets take a look at some various RewriteRequest examples:
/**
* example 1.
* matches: GET /some/demo/path
*/
RewriteRequest(
ParsePath("some" :: "demo" :: "path" :: Nil, "", true, false),
GetRequest, _
)
/**
* example 2.
* matches: PUT /some/image.png
*/
RewriteRequest(
ParsePath("some" :: "image" :: Nil, "png", true, false),
PutRequest, _
)
/**
* example 3.
* matches: * /some/demo/
*/
RewriteRequest(
ParsePath("some" :: "demo" :: Nil, "", true, true), _, _
)
/**
* example 4.
* matches: GET /product/<item>/details
*/
RewriteRequest(
ParsePath("product" :: item :: "details" :: Nil, "", true, true),
GetRequest, _
)
So far we’ve seen how to configure the incoming request – to complete the picture, lets now take our request handling and couple that up with matching the response and mapping parameters so they are then available in the rest of our code via S.param.
The RewriteRequest object has several overload apply methods to keep the verbosity of your code to a minimum – namely, these overloads are:
def apply(path : ParsePath, params : Map[String, String])
def apply(path : List[String])
def apply(path : List[String], suffix : String)
def apply(path : List[String], params : Map[String, String])
We can see that this gives us a bunch of flexibility depending on our needs – straight rewrite, rewrite with params etc etc. Lets take a look at some examples:
/**
* rewrite to a html file in webapp/show.html with no params
*/
RewriteResponse("show" :: Nil)
/**
* rewrite to a html file in webapp/show.html with
* a paramater called "product" from
* a parameter placeholder called product
*/
RewriteResponse("show" :: Nil, Map("product" -> product))
/**
* rewrite to a html file in webapp/example.pdf with
* no params, but a pdf suffix
*/
RewriteResponse("example" :: Nil, "pdf")
We have now looked at both requests and response – so moving back to our original example I hope you can see how it now works. One thing we have no explored is how to access the various parameters you might configure in your snippets / other application code. The answer my friends, is simple:
S.param("product").openOr("fail over product")
By now I presume you are a rewriting guru!... that might be a bit overboard, but I hope you found this guide informative and useful.