Scala With RESTEasy (JAX-RS)

In the "code club" / "dojo" or whatever you want to call it that Ben (aka Johnny Coder) organized we have been learning about Scala the last few weeks. I have been puttering with Scala on my own for about a year now, and it has been fun to engage with other folk in the learning process. We have mostly been doing a mix of Project Euler and the S-99 problems as exercises. Those problems are fun, but they don't really reflect the kind of day to day programming tasks that I am usually engaged in.

To scratch that slightly more practical itch, I decided to see how well Scala fits in with other tools in the Java ecosystem. The Java ecosystem being broad, and my Scala kung fu being not that strong, all I have are some possibly interesting bits I discovered while trying to get Scala and RESTEasy working together.  I picked RESTEasy because in the middle of reading RESTful Java with JAX-RS.

I started off with an existing example project from chapter 11 in the RESTEasy book. I added the Scala plugin to the maven build, and I was off.

The first class I converted over was a JAXB annotated class com.restfully.shop.domain.Customer

I ran into a few things that caused me problems.  The first was this compiler message:

"';' expected but ',' found"


At the @XMLType annotation line.  The resolution was to switch from using the Array literal "{}" Java syntax to the Scala Array object.



I also got a JAXB runtime error during the JUnit tests:



....
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 15 counts of IllegalAnnotationExceptions
There are two properties named ....


I resolved that by using the @XmlAccessorType(XmlAccessType.FIELD) annotation.



For compatibility with the existing code that expected Java Bean style property access, I added the @BeanProperty annotation.   The annotation causes Scala to automatically generate get/set methods.



Here is the java version of Customer:



package com.restfully.shop.domain;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement(name = "customer")
@XmlType(propOrder = {"firstName", "lastName", "street", "city", "state", "zip", "country"})
public class Customer
{
private int id;
private String firstName;
private String lastName;
private String street;
private String city;
private String state;
private String zip;
private String country;

@XmlAttribute
public int getId()
{
return id;
}

public void setId(int id)
{
this.id = id;
}

@XmlElement(name = "first-name")
public String getFirstName()
{
return firstName;
}

public void setFirstName(String firstName)
{
this.firstName = firstName;
}

@XmlElement(name = "last-name")
public String getLastName()
{
return lastName;
}

public void setLastName(String lastName)
{
this.lastName = lastName;
}

@XmlElement
public String getStreet()
{
return street;
}

public void setStreet(String street)
{
this.street = street;
}

@XmlElement
public String getCity()
{
return city;
}

public void setCity(String city)
{
this.city = city;
}

@XmlElement
public String getState()
{
return state;
}

public void setState(String state)
{
this.state = state;
}

@XmlElement
public String getZip()
{
return zip;
}

public void setZip(String zip)
{
this.zip = zip;
}

@XmlElement
public String getCountry()
{
return country;
}

public void setCountry(String country)
{
this.country = country;
}

@Override
public String toString()
{
return "Customer{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", street='" + street + '\'' +
", city='" + city + '\'' +
", state='" + state + '\'' +
", zip='" + zip + '\'' +
", country='" + country + '\'' +
'}';
}
}


Here is my Scala conversion of Customer:



package com.restfully.shop.domain

import javax.xml.bind.annotation.XmlAttribute
import javax.xml.bind.annotation.XmlElement
import javax.xml.bind.annotation.XmlRootElement
import javax.xml.bind.annotation.XmlType
import javax.xml.bind.annotation.XmlAccessorType
import javax.xml.bind.annotation.XmlAccessType

import scala.reflect.BeanProperty

@XmlRootElement(name = "customer")
@XmlType(propOrder = Array("firstName", "lastName", "street", "city", "state", "zip", "country"))
@XmlAccessorType(XmlAccessType.FIELD)
class Customer {

@XmlAttribute
@BeanProperty
var id: Int = _

@XmlElement(name = "first-name")
@BeanProperty
var firstName: String = _

@XmlElement(name = "last-name")
@BeanProperty
var lastName: String = _

@XmlElement
@BeanProperty
var street: String = _

@XmlElement
@BeanProperty
var city: String = _

@XmlElement
@BeanProperty
var state: String = _

@XmlElement
@BeanProperty
var zip: String = _

@XmlElement
@BeanProperty
var country: String = _

override def toString =
"Customer{" +
"id=" + id +
", firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", street='" + street + '\'' +
", city='" + city + '\'' +
", state='" + state + '\'' +
", zip='" + zip + '\'' +
", country='" + country + '\'' +
'}'

}


The next class I converted was com.restfully.shop.domain.Customers



This conversion was very similar, but it was dealing with collections.  I found that Scala 2.8 has a new object scala.collection.JavaConversions that contains many implicit conversion for Java collections. 



Here is the Java version of Customers:



package com.restfully.shop.domain;

import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* @author Bill Burke
* @version $Revision: 1 $
*/
@XmlRootElement(name = "customers")
public class Customers
{
protected Collection customers = new ArrayList();
protected List links;

@XmlElementRef
public Collection getCustomers()
{
return customers;
}

public void setCustomers(Collection customers)
{
this.customers = customers;
}

@XmlElementRef
public List getLinks()
{
return links;
}

public void setLinks(List links)
{
this.links = links;
}

@XmlTransient
public String getNext()
{
if (links == null) return null;
for (Link link : links)
{
if ("next".equals(link.getRelationship())) return link.getHref();
}
return null;
}

@XmlTransient
public String getPrevious()
{
if (links == null) return null;
for (Link link : links)
{
if ("previous".equals(link.getRelationship())) return link.getHref();
}
return null;
}

}


Here is my Scala conversion of Customers:



package com.restfully.shop.domain

import java.util.{ArrayList => JArrayList}
import java.util.{Collection => JCollection}
import java.util.{List => JList}

import javax.xml.bind.annotation.XmlElementRef
import javax.xml.bind.annotation.XmlRootElement
import javax.xml.bind.annotation.XmlTransient
import javax.xml.bind.annotation.XmlAccessorType
import javax.xml.bind.annotation.XmlAccessType

import scala.collection.Iterable
import scala.collection.JavaConversions._
import scala.collection.mutable.Buffer

@XmlRootElement(name = "customers")
@XmlAccessorType(XmlAccessType.PROPERTY)
class Customers {

var customers = Iterable[Customer]()
var links: Buffer[Link] = Buffer()

@XmlElementRef
def getCustomers: JCollection[Customer] = customers
def setCustomers(customers: JCollection[Customer]) = {
this.customers = customers
}

@XmlElementRef
def getLinks: JList[Link] = links
def setLinks(links: JList[Link]) = {
this.links = links
}

@XmlTransient
def getNext(): String = findHref("next")

@XmlTransient
def getPrevious(): String = findHref("previous")

private def findHref(rname: String): String = {
val rel = links.filter(_.getRelationship == rname)
rel match {
case Buffer() => null
case _ => rel.head.getHref
}
}

}


That’s about as far as I got this weekend.  I guess I was actually mostly working with JAXB this time.  The same example code has JPA as was as JAX-RS, and I’m going to try and convert the entire project to Scala, and if I do, I’ll upload it to BitBucket.

GWT 2.0 ms1 with Ivy Dependency Resolution

I decided to take a stab at finding out how hard it would be to integrate Ivy with a GWT Ant build.

Generate Project

I started out by generating the GWT application with webAppCreator.

C:/Programs/gwt-2.0.0-ms1/webAppCreator com.mostlyblather.IvyGwt

Create Ivy Configuration

Next I created the ivy.xml file. After playing around with a couple of different configurations, I settled on this one. Any future libraries added to the project would be either assigned to "compile" or "runtime" configurations. The "compile" runtime would include things like 3rd party GWT libraries. The "runtime" configuration would be used for dependencies executed in the Servlet container.


ivy.xml file:


<ivy-module version="2.0">
<info organisation="com.mostlyblather" module="ivygwt"/>
<configurations>
<conf name="runtime" description="needed in servlet container"/>
<conf name="compile" description="needed for compilation (other GWT libraries)"/>
<conf name="sdk" description="GWT SDK (compile time, seperated for Eclipse GWT IDE Integration)"/>
</configurations>
<dependencies>
<dependency org="com.google.gwt" name="gwt-servlet" rev="2.0.0-ms1" conf="runtime->default"/>
<dependency org="com.google.gwt" name="gwt-user" rev="2.0.0-ms1" conf="sdk->default"/>
<dependency org="com.google.gwt" name="gwt-dev" rev="2.0.0-ms1" conf="sdk->default"/>
</dependencies>
</ivy-module>

Modify Ant Build

In order to use the Ivy resolved dependencies, several modifications to build.xml need to be made.

First an automatic ivy installation code section.


<property name="ivy.install.version" value="2.1.0" />
<condition property="ivy.home" value="${env.IVY_HOME}">
<isset property="env.IVY_HOME" />
</condition>
<property name="ivy.home" value="${user.home}/.ant" />
<property name="ivy.jar.dir" value="${ivy.home}/lib" />
<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />

<target name="download-ivy" unless="offline">
<mkdir dir="${ivy.jar.dir}"/>
<!-- download Ivy from web site so that it can be used even without any special installation -->
<get src="http://repo2.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar"
dest="${ivy.jar.file}" usetimestamp="true"/>
</target>

<target name="init-ivy" depends="download-ivy">
<!-- try to load ivy here from ivy home, in case the user has not already dropped
it into ant's lib dir (note that the latter copy will always take precedence).
We will not fail as long as local lib dir exists (it may be empty) and
ivy is in at least one of ant's lib dir or the local lib dir. -->
<path id="ivy.lib.path">
<fileset dir="${ivy.jar.dir}" includes="*.jar"/>
</path>
<taskdef resource="org/apache/ivy/ant/antlib.xml"
uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
</target>
Next, add ivy to the build.xml xml namespace.


<project name="IvyGwt" xmlns:ivy="antlib:org.apache.ivy.ant" default="build" basedir=".">



Create a new target named "resolve" to actually fetch the needed jars.

  
<target name="resolve" depends="init-ivy" description="retrieve dependencies with ivy">
<ivy:retrieve pattern="${ivy.lib.dir}/[conf]/[artifact].[ext]"/>
</target>


Additionally create a "resolve" target dependency to the "libs" target.

    
<target name="libs" depends="resolve" description="Copy libs to WEB-INF/lib">


The property definition "gwt.sdk" is no longer required so it can be removed.

The "project.class.path" property has to be changed to pick up the new Ivy provided jars.

 
<path id="project.class.path">
<pathelement location="war/WEB-INF/classes"/>
<fileset dir="lib/compile" includes="**/*.jar"/>
<fileset dir="lib/sdk" includes="**/*.jar"/>
</path>


Target "libs" similarly needs to be updated. Notice that it is only picking up the "runtime" configuration.

 
<target name="libs" depends="resolve" description="Copy libs to WEB-INF/lib">
<mkdir dir="war/WEB-INF/lib" />
<copy todir="war/WEB-INF/lib" >
<fileset dir="lib/runtime" includes="**/*.jar"/>
</copy>
</target>


The "clean" target has some additional work to do now.

 
<target name="clean" description="Cleans this project">
<delete dir="war/WEB-INF/classes" failonerror="false" />
<delete dir="war/WEB-INF/lib" failonerror="false" />
<delete dir="war/ivygwt" failonerror="false" />
<delete dir="lib" failonerror="false" />
</target>


Here is the final build.xml

 
<?xml version="1.0" encoding="utf-8" ?>
<project name="IvyGwt" xmlns:ivy="antlib:org.apache.ivy.ant" default="build" basedir=".">

<path id="project.class.path">
<pathelement location="war/WEB-INF/classes"/>
<fileset dir="lib/compile" includes="**/*.jar"/>
<fileset dir="lib/sdk" includes="**/*.jar"/>
</path>

<property name="ivy.install.version" value="2.1.0" />
<condition property="ivy.home" value="${env.IVY_HOME}">
<isset property="env.IVY_HOME" />
</condition>
<property name="ivy.home" value="${user.home}/.ant" />
<property name="ivy.jar.dir" value="${ivy.home}/lib" />
<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />

<target name="download-ivy" unless="offline">
<mkdir dir="${ivy.jar.dir}"/>
<!-- download Ivy from web site so that it can be used even without any special installation -->
<get src="http://repo2.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar"
dest="${ivy.jar.file}" usetimestamp="true"/>
</target>

<target name="init-ivy" depends="download-ivy">
<!-- try to load ivy here from ivy home, in case the user has not already dropped
it into ant's lib dir (note that the latter copy will always take precedence).
We will not fail as long as local lib dir exists (it may be empty) and
ivy is in at least one of ant's lib dir or the local lib dir. -->
<path id="ivy.lib.path">
<fileset dir="${ivy.jar.dir}" includes="*.jar"/>
</path>
<taskdef resource="org/apache/ivy/ant/antlib.xml"
uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
</target>

<target name="resolve" depends="init-ivy" description="retrieve dependencies with ivy">
<mkdir dir="lib/runtime" />
<mkdir dir="lib/compile" />
<mkdir dir="lib/sdk" />
<ivy:retrieve pattern="${ivy.lib.dir}/[conf]/[artifact].[ext]"/>
</target>

<target name="libs" depends="resolve" description="Copy libs to WEB-INF/lib">
<mkdir dir="war/WEB-INF/lib" />
<copy todir="war/WEB-INF/lib" >
<fileset dir="lib/runtime" includes="**/*.jar"/>
</copy>
</target>

<target name="javac" depends="libs" description="Compile java source">
<mkdir dir="war/WEB-INF/classes"/>
<javac srcdir="src" includes="**" encoding="utf-8"
destdir="war/WEB-INF/classes"
source="1.5" target="1.5" nowarn="true"
debug="true" debuglevel="lines,vars,source">
<classpath refid="project.class.path"/>
</javac>
<copy todir="war/WEB-INF/classes">
<fileset dir="src" excludes="**/*.java"/>
</copy>
</target>

<target name="gwtc" depends="javac" description="GWT compile to JavaScript">
<java failonerror="true" fork="true" classname="com.google.gwt.dev.Compiler">
<classpath>
<pathelement location="src"/>
<path refid="project.class.path"/>
</classpath>
<!-- add jvmarg -Xss16M or similar if you see a StackOverflowError -->
<jvmarg value="-Xmx256M"/>
<!-- Additional arguments like -style PRETTY or -logLevel DEBUG -->
<arg value="com.mostlyblather.IvyGwt"/>
</java>
</target>

<target name="hosted" depends="javac" description="Run hosted mode">
<java failonerror="true" fork="true" classname="com.google.gwt.dev.HostedMode">
<classpath>
<pathelement location="src"/>
<path refid="project.class.path"/>
</classpath>
<jvmarg value="-Xmx256M"/>
<arg value="-startupUrl"/>
<arg value="IvyGwt.html"/>
<!-- Additional arguments like -style PRETTY or -logLevel DEBUG -->
<arg value="com.mostlyblather.IvyGwt"/>
</java>
</target>

<target name="build" depends="gwtc" description="Build this project" />

<target name="war" depends="build" description="Create a war file">
<zip destfile="IvyGwt.war" basedir="war"/>
</target>

<target name="clean" description="Cleans this project">
<delete dir="war/WEB-INF/classes" failonerror="false" />
<delete dir="war/WEB-INF/lib" failonerror="false" />
<delete dir="war/ivygwt" failonerror="false" />
<delete dir="lib" failonerror="false" />
</target>

</project>

Quick install instructions for latest Mercurial (1.3.1) on Ubuntu (9.04 Jaunty Jackalope)


sudo apt-get install build-essential gcc python-dev python-setuptools
sudo easy_install -U mercurial