Monday, 9 November 2009

Controlling Ajax in Tapestry

The standard way to do Ajax in Tapestry is with Zones, EventLinks and occasionally Blocks. This can result in a very simple TML file:



Hello ${name}!

Hello World!




Hello Sam


And an equally simple Java class:
@InjectComponent
private Zone helloZone;

@Property
private String name;

Object onHelloEvent(String name) {
this.name = name;
return helloZone.getBody();
}

public boolean isNamePopulated() {
return (name != null) && (name.length() > 0);
}

If that's all you need then job done (and not a line of JavaScript in sight!)

However, I'm finding that real world requirements often require greater control over the implementation details - typically this means we need to write our own JavaScript. Here's how we do it ...

First of all we modify the TML file to use a standard anchor in place of the EventLink:

Hello Sam


Now we need to tell JavaScript what to do with that link. But before we can start writing our JavaScript, we add the following to our Java class:
@Environmental
private RenderSupport renderSupport;

@Inject
private ComponentResources resources;

@Property
private String clientId;

void setupRender() {
clientId = renderSupport.allocateClientId(resources);
}

void afterRender() {
Link helloLink = resources.createEventLink("helloEvent", "Sam");
JSONArray parameters = new JSONArray();
parameters.put(clientId);
parameters.put(helloZone.getClientId());
parameters.put(helloLink.toAbsoluteURI());
renderSupport.addInit("initHello", parameters);
}

This makes use of two methods in Tapestry's rendering lifecycle - setupRender + afterRender. In setupRender we're getting unique client ID for this component. In afterRender we're passing some information to a JavaScript function via Tapestry's JSONArray object - note that one of these parameters is the URI of an EventLink we've created. For this to work, we also need to annotate the class:
@IncludeJavaScriptLibrary("myZone.js")

Finally we get to the JavaScript. What we need is a function that gets called when the page loads and accepts the three parameters we're passing in from the Java class. Here's what it looks like:
Tapestry.Initializer.initHello = function(element, zoneId, url) {
element = $(element);
$T(element).zoneId = zoneId;
element.observe("click", function(event) {
Event.stop(event);
var zoneObject = Tapestry.findZoneManager(element);
if (!zoneObject) return;
zoneObject.updateFromURL(url);
});
}

In fact, all that JavaScript was copied from the linkZone function in tapestry.js - we're doing exactly what Tapestry was doing, but now the JavaScript is under our control. :-)

So now we're in control of the JavaScript, what can we do with it? Pretty much anything we like as it turns out, but for now, let's pass the name in as a standard request parameter. First of all we modify our event handler to look like this:
@Inject
private Request request;

Object onHelloEvent() {
this.name = request.getParameter("name");
return helloZone.getBody();
}

And then we replace zoneObject.updateFromURL with a standard Prototype Ajax request:
// zoneObject.updateFromURL(url);
new Ajax.Request(url, {
method: 'get',
parameters: { "name" : "Sam" },
onException: Tapestry.ajaxFailureHandler,
onFailure: Tapestry.ajaxFailureHandler,
onSuccess : function (transport) {
zoneObject.processReply(transport.responseJSON);
}
});

Note that we are now using GET instead of POST and we have also exposed the onSuccess handler. This can prove very useful when we start implementing more advanced requirements.

Saturday, 24 October 2009

Tapestry Revisited

I've been doing a bit of Tapestry 5 at work recently. Have to say, I haven't been much of a fan in the past, but I am starting to see its merits. Yes, it's sometimes quite hard to do relatively simple things, but this is usually down to a lack of documentation more than anything. Once you've arrived at a solution, the end result is often clean and concise - it's getting there that's the issue!

It's very different to working with an MVC framework and requires you to look at server-side development in an entirely different way. For example, developing a functional component nearly always requires some knowledge of JavaScript / Prototype and I seem to frequently find myself looking through the source code for the Tapestry JavaScript library. Although I was uncomfortable with this at first (why would a Java developer need to understand JavaScript?) I'm now finding that I enjoy the challenge. In fact, the weird and wonderful world of JavaScript is turning out to be a more friendly place than I'd feared!

Friday, 25 September 2009

New Mac

Work have kindly given me a brand new MacBook Pro! It features the new 'Unibody Enclosure' and looks absolutely gorgeous - there's a video of it being made here.

Naturally, I spent some of last night making sure my Pygame efforts still worked on it. This pretty much forced me to try Python 2.6 + Pygame 1.9 (I had previously only tried Python 2.5 + Pygame 1.7) so I was quite pleased when it worked first time. Here's what it looks like:


This also hints at a couple of new features - coins + keys! Unfortunately, although you can now collect these items, you can't actually do anything with them yet.

Tuesday, 25 August 2009

Paying The Bills

A distinct lack of activity on the blogging front lately. This is because I've been (a) moving house and (b) actually enjoying myself at work! ;-)

As Tech Lead on a brand new project, I've been writing a web app using Spring MVC, Freemarker + Hibernate and I thought I would share a few thoughts. I should start by saying that it's been nowhere near as easy as Grails, GSP + GORM, but nonetheless, it's a big improvement on the 'bad old days' of Java web app development.

To be honest, although Spring MVC is both powerful and flexible, I've always found it a little fiddly in the past. However, this time I decided to use annotation-driven controllers and these were way easier to work with than the MultiActionController I've used before. All you need is something like this in your context:
<context:component-scan package="com.royale.sam.web.controller">
And then you can annotate your controller class like this:
@Controller
@RequestMapping(method = RequestMethod.GET)
public class PageController {

...

@RequestMapping(value={"/page.html", "/item.html"})
public ModelAndView pageHandler(HttpServletRequest request, HttpServletResponse response) {
...
}
}
And that's it! Using this technique you can group handler methods in one controller (much like you can in Grails) which makes it easier to reuse domain logic and leads to a well organised application. We found this was particularly useful when implementing handlers for our AJAX functionality.

Freemarker was also pretty good, although - possibly because it's application agnostic - we found that it didn't support a number of features that you take for granted when using JSP. (In fact, I would've used JSP, but requirements dictated that we used a templating language.) For example, we couldn't find an easy + reliable way to get the context path, so we ended up introducing a servlet filter that made this information (and more) available to every page in our app. We also steered clear of using taglibs in our Freemarker templates, preferring to write our own directives.

And Hibernate was Hibernate. Always trickier than you expect to set it up, but once it's done, you generally don't have to worry about it anymore.

Monday, 20 April 2009

Pixel Art

Something I am starting to realise is that pixel art is hard and animated pixel art is even harder. Not just hard, but extremely time consuming too. To give you an idea of how much work goes into these things, take a close look at the forward-facing frames for our protagonist ...

I'm telling you, the guys who did Metal Slug should get some kind of medal.

Pygame Update

I recently realised that I haven't blogged about my Python/Pygame efforts in quite a while, so I figured it was time for an update. The good news is, my lack of blogging is not reflected by a lack of development, so there's quite a few new features - so many, in fact, that it's probably best if I just post a video ...



So here we can see a couple of maps designed with my very own level editor, an animated main character, a scrolling background, the ability to seamlessly walk off one map and onto another, animated features (eg. flames), pickups (eg. coins) and movement correction (ie. note the way the little fella 'slides' off obstacles that are blocking his way).

If anyone's interested, I created this video by running gtk-recordMyDesktop against the Pygame window and converting the resulting .ogg with mencoder:
mencoder out.ogg -oac lavc -ovc lavc -lavcopts abitrate=160 -o rpg-world.avi
I have no idea what most of that means, but the quality has turned out okay!

Wednesday, 8 April 2009

Groovy comes to Google App Engine

It would seem that my reservations about Google App Engine may soon be no more. I haven't had chance to try it yet, but Google App Engine now supports Java!

Better still (for fans of Groovy like myself) work has been going on behind the scenes to ensure that Groovy is also supported - see here and here. In fact, Guillaume Laforge (creator/inventor of Groovy) has already written a Google Maps / Flickr mashup with it.

:-D