Sunday, September 27, 2009

Introducing Maven Trap - the honey pot for Maven malcontents

For far too many years, Maven 2 has dominated my builds, and I've sat idly by, taking what they've given, meekly replying, "Thank you sir, can I have some more?" I've watched my builds break apart on unmodified code or just hang, I've nearly drown fighting the torrent of jumbled output trying to find the lone test failure, and I've lost my sanity wading through huge XML build files trying to understand why my build is downloading the internet.

Sure, there have been moments of resistance, like when I created a fork of Maven to add features like parallel downloading of artifacts, but the effort in maintaining a fork is too high when, honestly, I'd rather be doing just about anything than hacking build systems. Still, the revolutionary fire never died, despite not having a sustainable outlet.

Like the scrappy fighter that keeps bouncing back after a huge left hook, I'm back, but this time, looking for victory by treating Maven like the unpredictable wild animal it can be. Instead of forking Maven, a fellow Atlassian Sam Le Berrigaud and I have put it in its cage by wrapping the executable with an interceptor framework, allowing modules to control how Maven gets executed and what happens after it gives up the ghost.

Maven Trap is this executable interception framework and three modules:
  • Console Colorization - Pages of white or green text is hard to read and makes it easy for critical errors to slip by. This module transforms output into ANSI colorized text to make it easier to see errors and other key events.

  • Always Offline - A build system should not connect the network unless you explicitly give it permission. This module inverts the meaning of the "-o" flag to make the default offline, only to be enabled via the "-o" flag.

  • YAML POM - While XML is great for programs, it sucks for humans. This module automatically converts your XML pom.xml file into a YAML-formatted pom.yml for either read-only or editable usage.
Since Maven Trap treats Maven as an opaque black box, what it can do is limited, however, it provides low-risk integration points that can be extended fairly easily and should work with most any version of Maven. Each module can be enabled by setting a unique environment variable, giving you full control over which features you want without modifying your Maven installation.

No announcement is complete without a screenshot, so here is all three modules enabled on simple project:



We use this in the Atlassian Plugin SDK, as we include our own customized installation of Maven to try to reduce the Maven hassles that our development community continually faces. If you too are fed up with how Maven is always accessing the internet, produces heaps of unmanageable output, or uses obtuse XML configuration, give Maven Trap a go, or better yet, join GitHub and fork it for full control.

Thursday, September 24, 2009

Better server-side JavaScript unit testing

As part of a project aiming at a server and client-side tag library based Rhino, I thought it might be nice to write my unit tests in JavaScript. A quick Google search later and I find the non-uniquely named JsUnit project, which not only implements *unit in JavaScript, but has Ant and Maven 2 support. However, quite soon, I hit some problems, mainly around very little error reporting and feedback. Few things are as annoying as knowing there is a problem, but having no idea where.

A quick fork later and I now have JsUnit 1.3-db. So far, this fork adds:
* Callstack reporting for errors and failures
* Text test result output, not just XML
* File and line numbers in all error messages
* Upgraded Rhino

Previously, if you hit an error, you'd get this:
[INFO] [jsunit2:jsunit-test {execution: test}]
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
Then, you'd have to dig into the target/surefire-reports/TEST-MyTestCase.xml to find this:
<testcase name="TestFieldTest.testRender" time="0.006">
<error message="ReferenceError: "bar" is not defined." type=""/>
</testcase>
Not helpful. Now, you get this:
[INFO] [jsunit2:jsunit-test {execution: test}]
TestRunner (1 test cases available)
> Starting test suite "TextFieldTest"
- Running test 1: "TextFieldTest.testRender"
ERROR in TextFieldTest.testRender: ReferenceError: "bar" is not defined. (TextField.js:7)

Stack trace:
1: TextField.js:7
2: TextFieldTest.js:6 (TextFieldTest_testRender)
3: JsUnit.js:827 (TestCase_runTest)
4: JsUnit.js:808 (TestCase_runBare)
5: JsUnit.js:386
6: JsUnit.js:409 (TestResult_runProtected)
7: JsUnit.js:390 (TestResult_run)
8: JsUnit.js:797 (TestCase_run)
9: JsUnit.js:1033 (TestSuite_runTest)
10: JsUnit.js:1014 (TestSuite_run)
11: JsUnit.js:2614 (EmbeddedTextTestRunner_run)
12: Components:4
< Completed test suite "TextFieldTest"
1 error, 0 failures.
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
For fellow Maven folks, you can find the releases at my repo:

http://twdata-m2-repository.googlecode.com/svn/de/berlios/jsunit/jsunit-maven2-plugin

Saturday, September 5, 2009

GWT for JavaScript Junkies

The most appealing thing about GWT for me isn't the fact that I, as a Java developer, don't have to write JavaScript anymore (hey, I like JavaScript!), but the single development experience. Since everything is in Java, you can have common libraries, coding patterns, frameworks, and editor behavior, drastically simplifying the development experience. My second favorite aspect of GWT is its component library. For far too long, we Java developers have treated HTML as just text to output, which is like treating programming as a series of ones and zeros - surely we can work at a higher level than that.

Therefore, I set out to explore what it would be like to have a GWT-like development experience for JavaScript developers, only instead of Java everywhere, you write JavaScript. Also, I wanted the bulk of the UI to be built on the server-side to minimize XHR calls and improve accessibility. To implement the server-side component library, I ripped of as much of Apache Wicket as I could, while keeping it 100% stateless (no session usage or complicated state sharing schemes). I'm not sure how to package this yet (new framework, Struts 2 plugin, require OSGi, or whatever), but before I go too far down the road of making it a proper framework, I thought I'd get some feedback on the basic design.

This is what the code of a simple application looks like:
/controllers/comment.js
/templates/comment/comment.html

comment.html

The comment.html template is a simple HTML page with "jsid" attribute markers to determine where the components are rendered. Absolutely no logic is allowed in a template:
<html>
<head>
// import jquery and json
</head>
<body>
<form jsid="commentForm" action="#">
<input jsid="title" type="text">
<input jsid="body" type="text">
<input jsid="submit" value="Submit" type="submit">
</form>
<script type="text/javascript" jsid="proxies"></script>
<body>
</html>

comment.js

Where it gets interesting is in comments.js. This follows the basic design of Ruby on Rails controllers with some interesting twists:
page({
comment : function(params) {
this.commentForm = new Form();
this.commentForm.add(new TextField("title"));

var body = new TextField("body", {
value : "Great feature",
label : "Body",
description : "The body of the comment"
});
this.commentForm.add(body);
this.commentForm.add(new Submit("submit"));

this.commentForm.onsubmit = function() {
alert('calling comment');
saveComment({
title : jQuery('#title').val(),
body : jQuery('#body').val()
}, function() {
alert("Comment saved");
});
return false;
}.toString();

this.proxies = new Proxies(['saveComment']);
},
saveComment : function(comment) {
print("comment title: " + comment.title + " body: " + comment.body);
this.result = "success";
}
});
Ok, starting from the top. You pass into the "page" function a map of methods to be used as controller methods, although there is a mix of HTML and JSON-producing methods: "comment" returns a comment page, while "saveComment" is a JSON-invokable method.

comment()

Since this is the method that will render HTML, we will build up our component tree. Instance variables like 'commentForm' are the first to be matched against comment.html, with subcomponents matching accordingly as the template processor works its way through the HTML. There are four components used in this page:
  1. "Form" - represents an HTML form
  2. "TextField" - represents an HTML text input field
  3. "Submit" - represents a submit button
  4. "Proxies" - generates client-side proxy methods that call the corresponding server-side ones
The main advantage of building your components in server-side code and not in a template is the much greater options you have for presentation logic. In this example, I just use hard-coded strings, but you can see how these could just as easily be initialized using service calls.

Nothing eye-opening so far...until you get down to the onsubmit() method. What we are doing is creating a method that will be executed on the client-side in our server-side code, using the nifty toString() feature on functions to get the source. We could have just as easily wrote it in a big string, but this way we get better editor support and fail-fast parser failure reporting. This also serves to help "blur" the line between server and client-side code ala GWT.

Finally, we create the "Proxies" component, passing the server-side function names that we want to be able call from the client. My implementation uses JQuery and JSON to stringify objects, but those details are irrelevant here. The point is we are able to call "saveComment()" on the client-side as if it was on the server-side, with all the json parsing and construction happening behind the scenes. Since both sides are JavaScript, you don't have the immedience mismatch you do in other languages.

saveComment

The "saveComment()" function is a remotely executable function, meant to receive and return JSON, although you aren't limited to it. JSON in the request body is generally a good idea since it limits your vulnerability to cross-domain request forgery. Of course, this technique also means your form only works with JavaScript turned on, which may not be an option. The "comment" argument is the serialized object passed from the client, and we return a serialized instance of our controller, which is why we set the result by setting the instance variable.

This basically completes the tutorial. One other thing to note is the components are also written in JavaScript, and since that means Rhino, you get all the E4X niceities in constructing HTML. This means even the HTML is syntaxally processed when the script is loaded.

Next steps

My next step is to build a real application using this little framework and see how it scales as the feature demands pile on. Thanks to Rhino, I can keep developing the backend of this application in Java with all the libraries and frameworks I depend on. The consistent use of JavaScript for the presentation layer hopefully means less code, more reuse, and more productivity.