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 Collectioncustomers = new ArrayList ();
protected List links;
@XmlElementRef
public CollectiongetCustomers()
{
return customers;
}
public void setCustomers(Collectioncustomers)
{
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.