Recently I've been getting back in to using Play! for web development. I've built something small with it before, and it definitely is my favorite Java web framework. But there are still some things about Java that bug me. One of them is a complete lack of type inference. Static typing can be useful, but becomes a real annoyance if the compiler doesn't help you out a lot. In C# they have some basic type inference using the 'var' keyword and I have long wished Java would have something like that.
Then today I came across Project Lombok. It makes use of the AST transformations in the Java compiler to add features to Java. It can do some pretty cool stuff, and even integrates into your IDE so everything works as expected. And one of the features it has... yep, a 'val' keyword that does basic type inference. Nice! So you can do things like this:
val aList = Arrays.asList("Hello", "World"); for(val s : aList) System.out.println(s);
Behind the scenes Lombok turns the first 'val' in to "final List<String>" and the second into "final String". This is great!
One my first thoughts after seeing this was, can I get it to work with Play! Since most frameworks simple compile with javac Lombok is pretty simple to get working, you just have to have it on the compile class path. But Play! is a little different. It actually has a built in compiler (it uses the Eclipse compiler) and it compiles your code at runtime. This is great in development because it mean you and change Java code and just refresh our window and Play! will recompile the code and run the updated code. So no more restarting the server, or having to run in debug mode (which doesn't work for a lot of code changes anyway). It really makes things so much easier. But it also means in order to get Lombok to work, you have to get the Play! compiler to know about it. I couldn't find anyone saying how to do this, which is why I'm writing this.
I'll spare you the gory details of how I figured it out, because it took quite a while, but once I figured it out it's pretty simple to do.
First what you have to do it get Lombok into you Play! project. If you're using Play! 1.2, it's pretty simple (if you aren't yet using 1.2, you should really consider upgrading, lots of good stuff there). Just go to your dependencies.yml file and add Lombok. Like this:
require: - play - org.projectlombok -> lombok 0.9.3
Now this will work just fine, but if you're like me and you want the val keyword, you have to use the beta version of Lombok, which is in the maven repo. So you'll have to down load it and put it in your project. Easy enough, I put it in a jars folder under my project root and changed the dependencies.yml file to look like this:
repositories: - local: type: local artifact: "${application.path}/jars/[module]-[revision].[ext]" contains: - org.projectlombok -> *
Now we've got the jar, we just have to let Play! know about it when it compiles. I figured this out by reading what Lombok says it does to Eclipse to get it to work, and then duplicated it with Play! In order for Lombok to work, it has to be registered as a java agent, and be on the boot classpath. This can be done using JVM arguments -javaagent and -Xbootclasspath/a. The difficulty is in figuring out how to add these JVM arguments to the Play! startup process. The only way I know of is by making use of the jvm.memory config param in the applicaiton.conf file. This param just gets appended to the call to start the JVM, so you can really put any JVM arguments in there. So I added this line to my application.conf:
jvm.memory=-javaagent:lib/lombok-0.10.0-BETA2.jar -Xbootclasspath/a:lib/lombok-0.10.0-BETA2.jar
That almost does the trick. The only problem now is you'll get an error at startup saying it can't find the Eclipse compiler. That's because Lombok is now referencing it, but it's not at the right level in the classpath to be accessed. So we just need to add it to the bootclasspath, too. So now the arguments look like this:
jvm.memory=-javaagent:lib/lombok-0.10.0-BETA2.jar -Xbootclasspath/a:lib/lombok-0.10.0-BETA2.jar -Xbootclasspath/a:/opt/play/1.2/framework/lib/org.eclipse.jdt.core-3.6.0.jar
Now when you run Play! from the command line it will have Lombok available when it compiles your app. If you are using Eclipse and the eclipse launch configurations generated by Play!, you just need to add the parameters above to the VM arguments of the launch configuration and it should work fine.
Now go and use some type inference!