Tuesday, August 10, 2010

Maven, Spring, Hibernate, Struts2, same old same old.. and what? Scala!

I recently had a project using the standard J2EE technology stack, but with an added twist of using Scala (in addition to Java). It was relatively smooth, with a few gotchas, which I will note here.

Lack of IDE support

I enjoy using vi much more than any of the Big Three IDEs for Java, so it wasn't as much of an issue for me, but my teammate was using IDEA, and he was having all sorts of problems (one reason is because one of the Scala plugins for IDEA seems to be completely busted as of this writing). I did try researching the plugins for other IDEs, and I found the Eclipse plugin to be the most sensible (unsurprising, as most of the developers of Scala seem to be using Eclipse).

If you have Scala code that depends on Java code, it's not much of a problem (as you can just compile the Java first, then compile the Scala). On the flip side, if both Scala and Java code depend on each other, you'll have trouble in the form of red squiggly underlines in Netbeans and IDEA that you simply cannot turn off.

Luckily though...

Maven support works beautifully

You won't have any trouble building your project from the command line with Maven - even if there are you have Scala and Java code with interdependencies. Here's what I added to my POM, updated for Scala 2.8.0, to make the whole thing work:

<plugin>
<groupId>org.scala-tools</groupId>
<artifactId>maven-scala-plugin</artifactId>
<version>2.12</version>
<configuration>
<args>
<arg>-deprecation</arg>
<arg>-unchecked</arg>
</args>
</configuration>
<executions>
<execution>
<id>compile</id>
<goals>
<goal>compile</goal>
</goals>
<phase>compile</phase>
</execution>
<execution>
<id>test-compile</id>
<goals>
<goal>testCompile</goal>
</goals>
<phase>test-compile</phase>
</execution>
<execution>
<id>process-resources</id>
<phase>process-resources</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>process-test-resources</id>
<phase>process-test-resources</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>


If you put your sources in src/main/scala, IDEs should be able to pick them up without a problem.

No more generating getters and setters

In Java, you'd navigate through something like 5 menu options so the IDE could generate 20 lines of boilerplate to make your class a "JavaBean". The nice folks who wrote Scala understand your pain and offer the @BeanProperty annotation:

import scala.reflect.BeanProperty
import java.io.Serializable
import java.lang.{Long => JLong}
import java.lang.{Integer => JInt}
import javax.persistence._

@Entity
@Table(name = "reviews")
class Review extends Serializable {
@BeanProperty
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
var id: JLong = _

@BeanProperty var rating: JInt = _
@BeanProperty var content: String = _
}


That's a standard Hibernate entity, written in Scala (Hibernate is none the wiser). The @BeanProperty annotation generates the getX and setX methods for you, so that Hibernate will be happy (the regular x and x_= Scala methods exist also).

Hibernate really only wants the Java classes, dammit!

Notice in the above example that Hibernate will barf if you try to make your id a scala.Long instead of java.lang.Long. The same holds for other types like Double and Float (Integer is Int in Scala, but I made the import alias just to be explicit).

Hibernate also really wants your collections to be Java collections instead of Scala collections. You can still work with Scala collections, but you'll have to import implicit conversions for them:

import scala.collection.JavaConversions._


For the performance wary, all that does is wrap the Java collections so you can interact with them in a more idiomatic way (it won't copy the collection).

Struts2

Struts seems to work quite well with Scala. Stacktraces will even show the specific line (in the Scala source) where the error occurred. The only minor gotcha I had was that when your Action classes extend ActionSupport, the SUCCESS, ERROR, etc constants are not in scope. You'll have to import them from Action directly:

import com.opensymphony.xwork2.Action._


Conclusion

Opinions on the project varied on our 2 person team. I was able to crank out much more code at a faster pace, but my coworker struggled a lot with the IDE support. On the flip side, integrating Scala into a Java project was a heck of a lot smoother than I thought.