Use delegation to write map/filter in Java

The problem

In Java, imagine you have a list of User objects, each encapsulates the user’s id, first name, last name and age. Then you want to call a web service UserService.deleteUsersByIds(List<Integer> userIds) to delete the users from your data store. It doesn’t sound too hard, does it? All you need to do is to transform you List<User> to List<Integer>. So you go ahead and write the following code:

List<Integer> ids = new ArrayList<Integer>(users.size());
for (User user : users) {
  ids.append(user.getId());
}

Then you go ahead and use your ids list, and everything is fine and dandy.

However, two minutes later, you find yourself having to provide another API method with a list of user’s names in String. So, again, you exercise your CSC101 skill:

List<String> names = new ArrayList<String>(users.size());
for (User user : users) {
  names.append(new StringBuilder(user.getFirstName()).append(" ").append("user.getLastName()));
}

Now, something else comes along and you need to write a piece of code that returns a list of names that belong to people who are under 21 years of age in the list…You get the idea. Well, things get boring pretty quickly.

As it turns out, these are two very important functions in functional programming: map and filter.

  • map(coll, f) “loops” over the collection, calls the function f on each element, and add the return of the f(element) to the return collection.
  • filter(coll, f) “loops” over the collection, calls f(element), and only add element to the return list when f(element) returns true

Use delegation for generic-ity

Now we take our first step in designing our generic map function:

<FromType, ToType> List<ToType> map(ArrayList<FromType> list) {
  List<ToType> retval = new ArrayList<ToType>(list.size());
  for (FromType item : list) {
    [...]
  }
  return retval;
}

What we left out in the above code snippet is how the input is mapped to the output. This is where delegates come in. Unfortunately, Java doesn’t have the language-level delegate. We need to design an interface for this delegate.

interface MapDelegate<FromType, ToType> {
  ToType map(FromType obj);
}

The delegate is parameterized (to provide more type safety) with FromType and ToType. FromType is the type of the objects in the original list, and ToType is the type of objects in the mapped list. Now we need to change our method signature to incorporate the delegate.

<FromType, ToType> List<ToType> map(ArrayList<FromType> list, MapDelegate<FromType, ToType> mapDelegate) {
  List<ToType> retval = new ArrayList<ToType>(list.size());
  for (FromType item : list) {
    retval.add(mapDelegate.map(item));
  }
  return retval;
}

Now the client code will look like this:

List<User> users = getUserListFromSomeWhere();
List<String> ids = map(users, new MapDelegate<User,String>() {
  public String map(User obj) {
    return new StringBuilder(user.getFirstName()).append(" ").append("user.getLastName()).toString();
  }
});

Similarly, we can write a filter function:

<T> List<T> filter(List<T> list, FilterDelegate<T> filterDelegate) {
  List<T> retval = new ArrayList<T>(list.size());
  for (T item : list) {
    if (filterDelegate.filter(item)
      retval.add(item);
  return retval;
}
interface FilterDelegate<T> {
  boolean filter(T item);
}

What about return value creation?

Use delegation, we can separate the parts of an algorithm in terms of their interfaces and leave the implementation to the caller. However, given the above filter and map methods, what if I don’t want the return type to be ArrayList? What if I want a LinkedList or a HashSet? Doesn’t the statement

  List<T> retval = new ArrayList<T>(list.size());

an implementation by itself?

Absolutely! For more flexibility, the “new” statement in the implementation body has to be delegated out as well. We introduce a ReturnDelegate interface:

interface ReturnDelegate<R extends Collection<?>> {
  R createReturnCollection();
}

and plug in the return delegate to the map method:

<FromType, ToType, R extends Collection<?>> R map(Collection<FromType> coll, MapDelegate<FromType, ToType> mapDelegate, ReturnDelegate<R> returnDelegate) {
  R retval = returnDelegate.createReturnCollection();
  for (FromType item : list) {
    retval.add(mapDelegate.map(item));
  }
  return retval;
}

Now the actual implementation has been completely separated. I know you can probably achieve flexibility without return delegate with the use of reflection, but on some systems (like GWT, which is what I’m working on and what this code is originally designed for), reflection is off limits.

Solving a GWT deferred binding mystery

This morning at work, I was running our GWT application with some deferred binding logic in it, and all of a sudden I got this ridiculous message:

[ERROR] Class 'mycompany.rebind.HistoryResourceGenerator' must derive from 'com.google.gwt.core.ext.Generator'
[ERROR] Failure while parsing XML
com.google.gwt.core.ext.UnableToCompleteException: (see previous log entries)
at com.google.gwt.dev.cfg.ModuleDefSchema$ObjAttrCvt.convertToArg(ModuleDefSchema.java:729)
at com.google.gwt.dev.util.xml.HandlerArgs.convertToArg(HandlerArgs.java:64)
at com.google.gwt.dev.util.xml.HandlerMethod.invokeBegin(HandlerMethod.java:214)
at com.google.gwt.dev.util.xml.ReflectiveParser$Impl.startElement(ReflectiveParser.java:257)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:501)

It was running fine yesterday when I left work, and now it tells me that my generator isn’t a subclass of the GWT Generator??? Quickly pulled the source, and it’s as clear as day that my generator is properly written. Then what gives?

Searching for an explanation, I pulled the GWT’s source code, and opened com.google.gwt.dev.cfg.ModuleDefSchema and here’s the method in question:

    public Object convertToArg(Schema schema, int lineNumber, String elemName,
        String attrName, String attrValue) throws UnableToCompleteException {

      Object found = singletonsByName.get(attrValue);
      if (found != null) {
        // Found in the cache.
        //
        return found;
      }

      Class<?> foundClass = null;
      try {
        // Load the class.
        //
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        foundClass = cl.loadClass(attrValue);
        Class<? extends T> clazz = foundClass.asSubclass(fReqdSuperclass);

        T object = clazz.newInstance();
        singletonsByName.put(attrValue, object);
        return object;
      } catch (ClassCastException e) {
        Messages.INVALID_CLASS_DERIVATION.log(logger, foundClass,
            fReqdSuperclass, null);
        throw new UnableToCompleteException();
      } catch (ClassNotFoundException e) {
        Messages.UNABLE_TO_LOAD_CLASS.log(logger, attrValue, e);
        throw new UnableToCompleteException();
      } catch (InstantiationException e) {
        Messages.UNABLE_TO_CREATE_OBJECT.log(logger, attrValue, e);
        throw new UnableToCompleteException();
      } catch (IllegalAccessException e) {
        Messages.UNABLE_TO_CREATE_OBJECT.log(logger, attrValue, e);
        throw new UnableToCompleteException();
      }
    }
  }

Because the error message was from Messages.INVALID_CLASS_DERIVATION, I put a breakpoint on line 22:

Messages.INVALID_CLASS_DERIVATION.log(logger, foundClass, fReqdSuperclass, null);

and started my app in debug mode. The breakpoint hits, and I noticed that the exception object is reporting ClassCastException from JSONNull to JSONObject. I quickly clicked: The error is from the class that fetches some resource over the net and plugged in to the generator. Following this lead, I found that the server resource the class was fetching wasn’t available any more, which caused the fetcher class to fail, and consequently, the ClassCastException is propagated to the generator and eventually ModuleDefSchema.

The mysterious error message, however, has nothing to do with the real problem. I spent a lot of time thinking that my Generator is wrong, because that’s what the compiler says, but in fact, it’s not. I think the confusion can be avoided if the GWT compiler can log the actual exception object, instead of interpreting the exception for the user. That’s what they’re doing in the try/catch clause in the method shown above: the exception object “e” is not used in the case of ClassCastException…

Well, the take-home message is: don’t always trust the GWT compiler message. Also, adding log more logging to your generator classes that can be indicative to where the actual problem is.

Integrating SmartGWT with ASP.NET JSON web service

Many web API authors are using third party libraries like JayRock to convert ASP.NET web service method return values to JSON.  ASP.NET does have the ability to return JSON objects for web methods.  It’s not a very well-known feature (I guess) as there are only a few places mentioning it. http://encosia.com/2008/03/27/using-jquery-to-consume-aspnet-json-web-services/ 

For example, suppose the web method we are calling is /ItemService.asmx/GetList, and it’s expecting a parameter “uid”. For JSON web service to work, we have to set the request content type to “application/json” and provide the request parameter as a JSON object and send it through HTTP POST data ({uid:’12345′}).

In the current project I’m working on, I’m hooking up SmartGWT’s Data source to use JSON web service of ASP.NET. SmartGWT’s data source allows OperationBinding to be set for each of the four CRUD operations. For ItemDS, my fetch operation looks like the following:

    OperationBinding createFetchOperation() {

        final OperationBinding retval = new OperationBinding(DSOperationType.FETCH, Urls.ISSUE_FETCH);
        retval.setDataFormat(DSDataFormat.JSON);

        retval.setDataProtocol(DSProtocol.POSTPARAMS);

        retval.setRecordXPath("/result");

        return retval;

    }

Since we’re doing a bunch of customized stuff, we’re bound to override transformRequest() method in the DataSource class:

class ItemDS extends RestDataSource {
    /* data source setup */
    @Override
    protected void transformRequest(DSRequest req) {
        // TODO
    }
}

We need to do two things in transformRequest:
1) set request header to “application/json; charset=utf-8”
2) set the request payload to be the JSON object that contains parameters to the web service
So here we go:

/* inside transformRequest() */
dsRequest.setContentType("application/json; charset=utf-8");
final JavaScriptObject params = JSOHelper.createObject();
JSOHelper.setAttribute(params, "uid", "12345");
req.setData(params);
return req.getData();

Does that work? A simple test proved that it didn’t. Because remember we set data protocol on the fetch operation to “POSTPARAMS”? It turns out that even though req.data is sent via POST body, it’s URL form-encoded, not JSON encoded as you would expect since we already set data format to JSON. A bit digging on the SmartGWT forum turned out that data format setting only affects the parsing of the return value, not the outbound parameters.

Okay…let me try again…This time, I’m setting DSProtocol to POSTMESSAGE, as this seems to be a more logical choice (as the parameters are going to be sent out as POST message/body). Does it work? I wish…From the DevConsole, it turns out that SmartGWT’s RPC Manager sends out “[Object]” in POST body…Whaaat? Looking at SmartGWT’s API, it doesn’t allow setting request data as a simple string. It has to be a JavaScriptObject instance. I digged inside SmartGWT’s source code, setting the attribute ‘data’ directly to the JSON object that I want to use, but GWT shell complains it cannot cast from java.lang.String to JavaScriptObject…Sighhhh

It starts to get frustrating. This evening, as I was going through some JavaScript stuff, it dawns to me that the notion “[Object]” seems to be the string representation of a generic JavaScript object. (i.e., the return of an object’s toString() method). Looking at JavaScriptObject class in GWT confirms this. So, what if I just override toString() method and just return a JSON object?

This is like an epiphany! I went over some articles on how to do GWT JSNI and quickly come up with this:

    /* Modify OperationBinding, set to use POSTMESSAGE protocol */
    setDataProtocol(DSProtocol.POSTPARAMS);

    /* in ItemDS */
    static native void setValue(JavaScriptObject jso, String val) /*-{
        jso.toString = function() {
            return val;
        }
    }-*/;
    /* in transformRequest */
    /* ... setup content type ... */
    final JavaScriptObject data = JSOHelper.createObject();
    setValue(data, "{uid:'12345'}");
    req.setData(data);
    return data;

A voila!!! The DevConsole shows that RPC manager sends out {uid:12345} as the HTTP POST data and I got right what I want.