It’s been about half a year since development started on Firewall, my very own game, and after some private testing its time to give it a bit of an extra push. I invite anyone to help test it before it’s launched; so please do! Testing is done via TestFlight, so go to the landing page there or to the Tumblr page for more information. Please note that the game is only available for the iOS platforms as of now, but if things go well I’ll make the effort for an Android port.

GWT (Google web toolkit) is a great development toolkit for web applications. I’ve been following it for a white now (including two somehwat popular posts about drag and drop and about enums, back when those weren’t so popular..) and it keeps getting better and better. Recently, in version 2.1, they’ve added something called the RequestFactory, which is something nifty to replace GWT-RPC calls when those are needed for DTOs. In effect, it allows you to have most of your logic in the browser, and close to no code in the server side, especially if your application is mostly retrieving data and setting it nicely for display.

This framework does everything for you: serialization, proxy creation, remote call exposure, error handling, and more. And it even gets better in 2.4. But with all that automatic glory, something was missed, which is asymmetric accessors, i.e. writing a getter method without an accompanying setter method. And that’s what I’m writing about today.

Why would I want asymmetric accessors?

So why would we even want asymmetric accessors? Well, the answers lies in the concept of encapsulation, and calculated fields. Just like not all fields in an object are persistent (marked with @Transient), not all fields on the client side are “concrete” on the server side; some of them are calculated from different values. The easiest example for this would be the following:

As you can see, the field “fullname” is just a concatination of the first and last names. The implementation hides the contract of the object – perfect encapsulation. However, there cannot be a “setter” method for this field; while we could think of a simple implementation that splits the full name at the first whitespace, it’s not the case for a lot of calculated fields.

ServiceLayer and ServiceLayerDecorator

So who causes the problem in the long chain of the RequestFactory architecture? Essentially, you can find it in a few locations. First, somehow the AutoBean representing your proxy decides that it is different from what’s currently on the server. This happens in very few situations, but when it does, the second part comes in: it tells the ServiceLayer that these properties should be set for the server-side object. The ServiceLayer looks for the setter method, and when failing to find one, throws an exception and – boom, your application fails. I’m skipping a few mediation objects here; maybe one day I’ll write a full article on what happens behind the scenes there.

Luckily, the ServiceLayer is extensible and designed using the chain of responsibility design pattern. You are allowed to write plugins, or ServiceLayerDecorators, and these will work before GWT’s built-in code steps in. This way, for certain funcionality required, you can override GWT’s behavior. But if you’d look through Google’s documents about RequestFactory you won’t find how to use these decorators; but it’s not a tough task. All you need to do is extend the RequestFactoryServlet, and pass the decorators to the parent class when initiaised.

The solution

So the cleanest solution I’ve found so far is:

Annotate: create an annotation, @CalculatedField in my case, and make it available in runtime (RetentionPolicy=RUNTIME).

Decorate: create a decorator, CalculatedFieldDecorator, which overrides the setProperty method and ignores calls to it if the getter of that property is annotated with @CalculatedField.

Extend: create MyRequestFactoryServlet which extends GWT’s servlet, and pass our decorator to it

Wire: use our servlet in the web.xml file, and annotate our field in the class code.

and there you have it! You will never see the message “Could not locate setter for property” ever again!

Conlcusion

I think that either Google overlooked an important issue, or I’m missing something important here. After tweaking my code a lot, I couldn’t get around creating the proposed code, but I’m sure Google could and should make a more elegant solution. On the other hand, I think this is a perfect case for making an API that makes it easy to do most tasks people would want, and makes it possbile to do everything else. Personally, I believe this should be a guideline all API writers should follow, and I’m glad Google chose it here.

Well, I have to say that work has been a bit tiring for sure; had many stressful deadlines for a long time now and while I never stopped having ideas for the Chaotic Java and never stopped writing them down, making code samples and unit tests for them, I did never find the time to make those final touches that make a post here something to be proud of.

(at least, something I could be proud of)

At the same time, I’ve started working on a board game for the iOS platform, called Firewall. I know, I know – “iOS? It’s not in Java! Use Android!”.. But for those who know me or keep up with this blog, it won’t be much of a surprise. I thought I’d make my first post in a while about this game, in fact, both because I think it’s an interesting game and because it’s free, and I know everyone love free stuff!

The game is a turn based board game for two players. It starts off as most board games do: each player has units, each unit has its own set of rules such as move forward, get a point when taking another unit etc. However, during the game players can change the core rules of the game by placing combinations of cards they draw in each turn, changing the balance of power not by moving their units but also by changing the meaning of the units as the game progresses!

I think it’s an interesting concept, even if I may say so myself. But there’s more to be done to make this game complete. And that’s why I ask you: go to the Firewall page, and see if you can do something to help.