20 November 2011

Central exception handling in Scala

Have you ever seen this in Java code?
public class AssignResponseImpl extends ServletSupport {
public AssignResponse assign(Ident ident, int low) {
try {
return getService().assign(ident, low);
} catch (Exception e) {
logger.debug("caught Exception", e);
return new AssignResponse("ERROR", e.getMessage());
This sort of java code always irritates me. I've got about 20 of these classes. They are there to glue the Axis servlets to my services.
The reason I don't like them is you can't factor them. And because you can't factor them, it's difficult to create (and maintain) standard behaviour.
Like always logging the error. And always returning the exception message in the correct place. Let's have a look at another:
public SearchResponse search(Ident ident, Criteria criteria) {
try {
return getService().search(ident, criteria);
} catch (Exception e) {
return new SearchResponse("ERROR", e.getMessage());
Notice the subtle change in behaviour? No? It's in the logger. These methods are in two different unrelated services. But they are very similar, and require the
same error handling.
Now, SearchResponse and AssignResponse share a common superclass, Response. So in the case of an exception, only the class differs, the fields
we're filling in are those in Response. This doesn't really help in Java, we have to cut and paste the try catch, with only the name of the class changing.
We also have to add
in the extra constructor into the subclasses, and all they do is fill in the fields in the superclass. But in Scala, we can use
two tricks: manifests and first class functions.

Manifests allow you access to information about classes which you wouldn't normally have available in Java, in this case the
return type of the service method (A):
abstract class ServletSupport[A <: Response] {
protected def exception(fn: => A)(implicit m: Manifest[A]): A = {
try {
} catch {
case e => {
logger.debug("caught Exception", e)
// create new instance of A
val t = m.erasure.newInstance().asInstanceOf[A]
In our service endpoint superclass, we've defined exception, which takes as parameters a function (taking no parameters and returning
A, the return type of our target method), and an implicit Manifest parameter. Importantly, this is added by the Scala compiler, so we don't have to add
the parameter manually.
In this case, we're asking for extra information about A.

Our exception method calls the passed in function, and if there isn't an exception thrown, then it returns the value returned by the function. If there is
an exception, then the return value is a new instance of A, with the response code and message filled in. We know that we can call setResponseCode and setMessage on an A
because in the class definition, we're setting the type bounds of A, it has to extend Response. OK, so what is our calling code like now?
class AssignResponseImpl extends ServletSupport[AssignResponse] {
def assign(ident: Ident, low: Int) = exception {
getService().assign(ident, low)
Now, we have standard error handling between web services; all I have to do is add a exception {} round the delegated call. In this case, we can use {} rather than ():
this means it looks like it's part of the language. And this is all type safe, I only have to specify the name of the response class once in the definition.

07 November 2011

How to inherit static methods in Scala

In our project we're using Apache Axis soap web services. With Axis, you have to define your pojos in a certain way. Not only do you need your getters and setters, you have to define a static method so that the axis libraries can find out type information to create the wsdl and static methods to serialize and deserialize to and from XML. These must be static methods, so are very hard to factor out in Java. We need to duplicate them for each POJO class.
public class WebServiceObject {
    private Integer id;

public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
// Type metadata public static TypeDesc getTypeDesc() { TypeDesc typeDesc = new TypeDesc(WebServiceObject.class, true); typeDesc.setXmlType(new QName("to", "WebServiceObject")); SoapHelper.addTypeDesc(typeDesc, "id", "int", true); return typeDesc; }
public static Serializer getSerializer(String mechType, Class<?> javaType, QName xmlType) { return new BeanSerializer(javaType, xmlType, typeDesc); }
public static Deserializer getDeserializer(String mechType, Class<?> javaType, QName xmlType) { return new BeanDeserializer(javaType, xmlType, typeDesc); } }
As you can see, there is a lot of boilerplate here. Some of the POJOs are large (66 attributes), so there is a *lot* of boilerplate. Is there anything we can do about this? Let's see. The first thing we can use is @BeanProperty, to get rid of the getters and setters. This annotation, which we can apply to a field, generates a getter and setter for that field.
class WebServiceObject {
  @BeanProperty var id: Integer = _
So already this is a lot better. But what about the static methods? We can apply a trick here. If we define a method in a companion object, then the class gets a static forwarder for that method. So I can define a getTypeDesc in the companion object, and then java can call it using the normal static way, i.e: WebServiceObject.getTypeDesc(). And we can use inheritance with companion objects. Yay!
So this is the entire code:
class WebServiceObject {
  @BeanProperty var id: Integer = _

object WebServiceObject extends SoapSerializer { val typeDesc = { val typeDesc = createTypeDesc(classOf[WebServiceObject], "to", "WebServiceObject") addTypeDesc(typeDesc, "id", "int", true) } }
trait SoapSerializer { val typeDesc: TypeDesc
def getTypeDesc() = typeDesc
def getSerializer(mechType: String, javaType: Class[_], xmlType: QName) = new BeanSerializer(javaType, xmlType, typeDesc)
def getDeserializer(mechType: String, javaType: Class[_], xmlType: QName) = new BeanDeserializer(javaType, xmlType, typeDesc) }
So our pojo has gone from 26 java lines to 9 Scala lines. Our 66 attribute POJO was 698 lines but is now 154.
There is more we could do, but this is enough for the minute. Our code is pretty well factored, and the amount of duplication is now acceptable. More importantly, you can read the code :-)