Sunday 24 June 2012

Scala and Spring MVC - JSON Mapping

So, after my earlier post I've been playing with a little test rig to do RESTful requests in Scala using the Spring MVC framework. After a few issues I've got something that I actually quite like, and it was relatively painless...

The biggest issue I had was that the Jackson JSON mapping - which Spring uses by default - doesn't work well with Scala types. Case classes it outright failed on, and Scala Maps it did weird things with. Everything else was ok though. Obviously this isn't ideal though. As such, I've put together a Spring HttpMessageConverter implementation that uses the Jerkson JSON mapper - which is based on Jackson but adds Scala support into the mix. I've even written the whole thing in Scala, and was quite impressed with how (relatively) painless it was...

So what does it look like? Well - this...
1:  /**
2:   * Implementation of the Spring HttpMessageConverter to convert between Scala types and JSON using the Jerkson mapper
3:   * @tparam T The generic type of the object to convert
4:   */
5:  class MappingJerksonHttpMessageConverter[T] extends HttpMessageConverter[T] {
6:   /**The Logger to use */
7:   val LOG = Logger[this.type]
8:  
9:   /**The media types that we are able to convert */
10:   val mediaTypes = List(MediaType.APPLICATION_JSON)
11:  
12:   /**
13:    * Helper to see if we support the given media type
14:    * @param mediaType the media type to support. If None here then we return True because of a quirk in Spring
15:    * @return True if we support the media type. False if not
16:    */
17:   def isSupported(mediaType: Option[MediaType]) = {
18:    LOG.debug("Comparing requested media type " + mediaType + " against supported list")
19:    mediaType match {
20:     case Some(mediaType) => mediaTypes.contains(mediaType)
21:     case None => true
22:    }
23:   }
24:  
25:   /**
26:    * Determine if we are able to read the requested type
27:    * @param clazz the class type to read
28:    * @param mediaType the media type to read
29:    * @return True if we support the media type. False if not
30:    */
31:   def canRead(clazz: Class[_], mediaType: MediaType) = isSupported(Option(mediaType))
32:  
33:   /**
34:    * Determine if we are able to write the requested type
35:    * @param clazz the class type to write
36:    * @param mediaType the media type to write
37:    * @return True if we support the media type. False if not
38:    */
39:   def canWrite(clazz: Class[_], mediaType: MediaType) = isSupported(Option(mediaType))
40:  
41:   /**
42:    * Get the supported media types
43:    * @return the supported media types, as a Java List
44:    */
45:   def getSupportedMediaTypes = java.util.Arrays.asList(mediaTypes.toArray: _*)
46:  
47:   /**
48:    * Actually attempt to read the data in the input message into the appropriate data type
49:    * @param clazz the class that we are reading into
50:    * @param inputMessage the input message containing the data
51:    * @return the unmarshalled object
52:    */
53:   def read(clazz: Class[_ <: T], inputMessage: HttpInputMessage) = {
54:    LOG.debug("About to read message")
55:    Json.parse[T](inputMessage.getBody)(Manifest.classType(clazz))
56:   }
57:  
58:   /**
59:    * Actually attempt to write the data to the output message
60:    * @param t the value that is to be written
61:    * @param contentType the media type to write as
62:    * @param outputMessage the output message to write to
63:    */
64:   def write(t: T, contentType: MediaType, outputMessage: HttpOutputMessage) {
65:    LOG.debug("About to write message")
66:    val jsonString = Json.generate(t)
67:    val writer = new OutputStreamWriter(outputMessage.getBody)
68:    writer.write(jsonString)
69:    writer.flush()
70:    writer.close()
71:   }
72:  }
73:    

Saturday 23 June 2012

Scala and REST

I've been looking into Scala and REST recently, because I really like the idea of Scala - though to date I've not actually got on all that well with it in practice - and I really like the idea of writing webapps using the REST principles and Javascript in the client to pull it all together.

As such, I've been looking at the various frameworks that exist for writing webapps, and in particular ones that have REST support for Scala. And have come to the conclusion that the answer isn't that great. I must confess that I am rather spoilt here in that I've done a lot of work with Java frameworks (Mainly Spring) that do provide all the things I'm looking for, and I think the Scala landscape is just too new to have full featured support yet.

Frameworks

The frameworks I've looked at, and my thoughts of them are:

Play 2.0

Seems rather nice on the outside, and there's an awful lot of good press about it. However, it's one of those frameworks that seems lacking on certain features that would just make it. It isn't possible to build a WAR file to run in a standard container using Play 2.0 (Apparently that was possible in 1.0, so for some reason that's been taken out of the newer version!). It also doesn't have fantastic support for JSON marshalling. It has built in support, but only if you specify the actual mappings yourself. What I'd really prefer is to be able to just give it a class (Even better, a case class) and have it get on with it. To be fair, when you are writing JSON objects this is rather trivial, using the Json.generate method, but when you are consuming JSON this seems to be very difficult. I managed to actually achieve it, but not in a fantastic way...

Lift

Seems to be a much more fully features framework than Play, with lots of support for other nice things - the documents mention JTA and JSR 303 Validation for example. Lift also supports being packaged into a standard WAR file, and is I think the only one that I've looked at that does support this! It falls down on JSON support again though, in that it will natively consume and produce JSON using the underlying raw JSON objects - the same as Play - and it can be made to produce JSON from your own objects but doesn't seem to have any easy way to consume JSON into your own objects.

Spray

Spray I need to play with some more. I thought it was lacking in a few areas but I've just come across hints in the documentation that those areas actually are supported. Specifically all the examples I had seen imply that you need to run your own web server in the spray code, but the documents just mentioned that you can run it in a Servlet container, and tell you to go and look at the examples for how to do that.

It does still look like it expects you to do a fair bit that you shouldn't have to - one of the examples has you wrapping your controller in a handler to automatically handle GZip encoded data! Something that shouldn't need to be handled by the controllers at all, but instead by the underlying web server.

The documentation claims that it has proper JSON support too - allowing marshalling from any type and unmarshalling to any type, instead of getting raw JSON objects back that you need to work with yourself. Unfortunately the wiki pages telling you how to do those aren't written yet, so that will mean more example diving.

Blue Eyes

Blue Eyes is an intriguing one, in that it is designed to only do RESTful web services and nothing else, and has a lot of support for various things like versioning of services, path handling, and apparently JSON support (Though this is totally undocumented it seems). It even seems to have built in support for MongoDB - though why a web framework needs built in support for a data store is beyond me. It does however require you to write your own web server first, and even goes so far as to give you lots of tools to do that - including the ability to do request logging and health monitoring of your services. All things that if I ran it in my own container I wouldn't have to do!

Pinky

Pinky does support Servlet containers, but in a bizarre way. You don't write Servlets, or anything that gets called by a Servlet. Instead you write code that all gets called by the Google Guice webapp filter. It does however let you write what it calls Controllers and Servlets (seemingly interchangeably). It also claims to have out of the box support for numerous different data formats, including JSON, but is very low on documentation for those. The actual examples just feel a bit weird too - like a mash up between Java and Scala. (This is the first time I've come across pure Scala code that uses annotations, but I suspect that's because it's built on top of Guice)

Others

I'm well aware there are plenty of other frameworks to choose from - the ones I've seen mentioned include Bowler, Xitrum, Unfiltered, Finangle, Scalatra, and Play Mini, but there are loads more too that I can't name off the top of my head. However, I'm only human and looking through all of these frameworks for the One that fits what I want takes a very long time. I may get to look at some of the others in the future, and write up my thoughts on them, but to date I've had too brief a look to say anything meaningful.

Summary

When I started writing this, my conclusion was going to be that I'm going to go back to Spring MVC but writing all my code in Scala. That means the slightly weird situation of having a mixture of Java and Scala in the controllers themselves - like I just described for Pinky - but hopefully allows me to play with pure Scala code below that, and it also gives me the full power of the Spring framework to leverage, including automatic marshalling/unmarshalling of data that so many of the above frameworks lack on, and running inside a standard Servlet container.

However, my actual conclusion is a bit less solid. I think I need to look closer at Spray and Lift before I outright drop them, and maybe even Play though I have spent a fair bit of time with that and not gotten very far...


Friday 22 June 2012

New Blog

I guess its traditional to start a new blog with a post about why you're starting a new blog. Kinda circular reasoning, but ah well...

Difficult as it may be to believe, I'm starting this blog basically as an outlet for my rants, mostly about coding or related concerns. It seems I rant about these things a fair bit, and most people I know in real life don't really care for these things quite to the same degree that I do, so by ranting here instead (ok, likely as well) it can actually reach a wider audience if people who care. Plus there's the (very vague) chance there may be things in here of use to people...

I should probably also point out that this is the blog of the Ranting Coder. Not the blog where I exclusively rant about coding. Mostly, but odds are there will be bits and pieces of other stuff as well...

So - Enjoy...