So, I've been playing about with some ideas for writing this REST framework I just mentioned this morning. And I've been playing with doing it in Scala, because - well - I like Scala even if I don't always get on with it. And I've been working on doing it with Annotations, in the same way that Spring MVC works, because I quite like that and it's nice and clean and easy to understand. It's kind of like the DSL techniques that I've seen done elsewhere, but without introducing any magic into the system that makes it difficult to understand how it works - it's simply just classes and methods that have annotations on them. And it also happens to make it easy to extend for other uses - you just add more annotations and it just works. This makes the Swagger integration trivial to add on later.
It's worth putting a quick note in first - the documentation on this is all marked very clearly with an Experimental tag, so it's no surprise that it's not rock solid yet. I'm sure that in a release or two it will be fantastic, but it's just not there yet.
However, Annotations are far from a first class citizen in Scala. I'm not sure they even count as second-class citizens from what I've been seeing. Actually writing an annotation in Scala is easy. You simply write a class that extends from StaticAnnotation and it just works. (Not quite the whole story, but you can work out the rest yourself I'm sure). That's where Easy ends though.
Actually introspecting an object at runtime to get the annotations on it is possible, but it's not trivial. It took me a fair bit of hacking away to get at the following:
This works, and the annotations variable contains a list of all the annotations on the class. However - and I don't understand this yet - it doesn't always work. I wrote this in a unit test, and I found that the unit test passed about 90% of the time, without actually changing anything at all! That's scary, and has totally put me off of using Scala annotations for now.
Instead, I've just re-written my annotation in Java, and using it is as trivial as using any Java annotation even when I'm annotating a Scala class. The actual use of a Java annotation is virtually identical to using a Scala annotation - though the Scala annotations do offer more flexibility in that they are full-on classes and so I assume they can contain their own behaviours if you want. I'm not sure where that would ever be useful though to be honest...
Showing posts with label scala. Show all posts
Showing posts with label scala. Show all posts
Sunday, 29 September 2013
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...
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.
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.
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...
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...
Subscribe to:
Posts (Atom)