Life Codecs Ruminations. Reflections. Refractions. Code.

December 9, 2012

Oracle JDK on Debian

Filed under: how to,software dev — Tags: , — Kamal Advani @ 21:56

Just came across JavaPackage in Debian. This allows you to cleanly replace OpenJDK with a complying alternative systemwide, the Debian Way. I was only interested in the Oracle JDK, and that worked a treat, not sure about other JDKs. In essence:

  1. Install the JavaPackage package using apt[itude]
  2. Download the .tar.gz JDK package from Oracle
  3. Run make-jpkg, installed as part of JavaPackage, against it (not as root); this produces a Debian package of the tarball
  4. Install the Debian package using dpkg
  5. Finally, use update-java-alternatives to update various Java-related symlinks systemwide. You can choose to only update a subset (e.g. just use the Oracle JDK for the browser plugins, but nothing else), or go the whole hog — all JDK binaries will then be from the Oracle JDK.

The wiki link has examples and details.

PS. I really miss saying “Sun JDK”.

April 5, 2010

Generics and Method Selection: A Backwards Compatibility Gotcha

Filed under: software dev — Tags: , , , — Kamal Advani @ 22:45

If you managed to read through the rather long post title… let’s move on to a much longer post.

I recently came across an interesting bug at work which was rather eye-opening on just how nasty backwards-compatibility (which prompted type erasure!) can be. Credits are due to my friend and colleague Marcus Hasslinger who reasoned through the issue with me from a backwards compatibility viewpoint.

Some background: we use Java 5 and EJB 3, we also make use of annotated methods that are inspected by custom EJB 3 interceptor(s). A sample EJB might implementation might be:

1
2
3
4
5
6
7
8
9
10
11
@Local
public interface Foo {
    void doSomething();
}

@Stateless
@Interceptors({TransactionHandlerInterceptor.class})
public class FooServiceImpl implements Foo {
    @RollbackWhen(...)
    public void doSomething() { /* ... */ }    
}

In the above, the interceptor (TransactionHandlerInterceptor) is an around-invoke type interceptor which intercepts doSomething(), reads the annotation on it (RollbackWhen) and decides some rollback logic.

The defect was that in a particular EJB, it would not detect the method as having this annotation, and thus the necessary rollback logic was never executed. Thus begins our saga. Rather than going through work artifacts (which I probably can’t show anyway) and bore you with our business domain, I’ll create an essentially similar example using the classic ‘Shape’ set of objects (I know.. I know…).

This particular EJB had the following structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// package foo
// Domain objects
public interface Shape {
}

public class Circle implements Shape {
}

// Services
public interface ShapeService<T extends Shape> {
    void draw(T shape);
}

@Local
public interface CircleService extends ShapeService<Circle> {
}

@Stateless
@Interceptors({TransactionHandlerInterceptor.class})
public class CircleServiceImpl implements CircleService {
    @Override
    @RollbackWhen(...)
    public void draw(final Circle shape) {
        System.out.println("Drawing circle");
    }
}

// Sample dumb, non-EJB client
public class Client {
    public static void main(final String[] args) {
        final CircleService circleService = new CircleServiceImpl();
        circleService.draw(new Circle());
    }
}

The issue was that whenever the interceptor intercepted CircleServiceImpl.draw(), it would not detect that it was annotated with @RollbackWhen. So I printed out the actual method.toString() output from within the interceptor – this turned out to be:

1
public void draw(Shape)

instead of the expected:

1
public void draw(Circle)

Obviously draw(Shape) is not annotated with @RollbackWhen, draw(Circle) is. Hmm. So where exactly does draw(Shape) come from though, we only ever implemented draw(Circle) in CircleServiceImpl. It’s time to look at type-erasure in action under the hood using ‘javap’ – a useful little bytecode disassembler that comes as part of the stock JDK. The following output was obtained by running ‘javap -c <classfiles…>’ (without the .class).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// javap -c Shape Circle ShapeService CircleService CircleServiceImpl Client
Compiled from "Shape.java"
public interface foo.Shape{
}

Compiled from "Circle.java"
public class foo.Circle extends java.lang.Object implements foo.Shape{
public foo.Circle();
  Code:
   0:   aload_0
   1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   4:   return

}

Compiled from "ShapeService.java"
public interface foo.ShapeService{
public abstract void draw(foo.Shape);

}

Compiled from "CircleService.java"
public interface foo.CircleService extends foo.ShapeService{
}

Compiled from "CircleServiceImpl.java"
public class foo.CircleServiceImpl extends java.lang.Object implements foo.CircleService{
public foo.CircleServiceImpl();
  Code:
   0:   aload_0
   1:   invokespecial   #10; //Method java/lang/Object."<init>":()V
   4:   return

public void draw(foo.Circle);
  Code:
   0:   getstatic       #18; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #24; //String Drawing circle
   5:   invokevirtual   #26; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

public void draw(foo.Shape);
  Code:
   0:   aload_0
   1:   aload_1
   2:   checkcast       #35; //class foo/Circle
   5:   invokevirtual   #37; //Method draw:(Lfoo/Circle;)V
   8:   return

}

Compiled from "Client.java"
public class foo.Client extends java.lang.Object{
public foo.Client();
  Code:
   0:   aload_0
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   new     #16; //class foo/CircleServiceImpl
   3:   dup
   4:   invokespecial   #18; //Method foo/CircleServiceImpl."<init>":()V
   7:   astore_1
   8:   aload_1
   9:   new     #19; //class foo/Circle
   12:  dup
   13:  invokespecial   #21; //Method foo/Circle."<init>":()V
   16:  invokeinterface #22,  2; //InterfaceMethod foo/CircleService.draw:(Lfoo/Shape;)V
   21:  return

Let’s start with the simple cases: Shape and Circle – Shape is a simple interface, all fine and dandy. Circle is a class implementing Shape, we can see the default constructor generated since we didn’t provide one of our own.

Next we look at ShapeService – a bit more interesting – it has an abstract method draw(Shape). Generics information is erased, there’s no ‘T’ anywhere. T is replaced by its upperbound, i.e. Shape. Thus the generated method too is draw(Shape). So far so good.

Moving on to CircleService – once more, the parameterisation is nowhere to be seen, there’s therefore no notion of Circle in CircleService, it simply extends ShapeService, and thus inherits the same method signature: draw(Shape). This is what got me – it of course, needs to be this way due to type erasure. You get the illusion of full type-safety, as if a new method draw(Circle) is automatically generated in CircleService – since after all, compile-time code completion, etc. in your IDE works just dandy on a reference of type CircleService – CircleService.draw(Circle) – never does it show CircleService.draw(Shape)!

Let’s move on further – things are getting more interesting. CircleServiceImpl has 2 overloaded methods, draw(Circle) which is our code converted to bytecode, and a compiler generated draw(Shape) – which casts (or at least does a cast-check – haven’t read the bytecode description) to ensure the supplied Shape is indeed a Circle, and then delegates on to our draw(Circle). Think about this for a moment – by itself, it’s just madness – but from a backwards compatibility viewpoint — 2 methods need to be there, our own draw(Circle) which as you can see, did not override any method in the interface as such(!), and draw(Shape) the compiler generated one – which is needed to satisfy the type-erased interfaces we implemented!

One final piece of the puzzle — client code: once more we see a default constructor is generated since we didn’t create one. And then the main() method which in fact calls draw(Shape), not draw(Circle). This is once more correct in a backwards-compatible-whacky way — as we saw above CircleService only ever had draw(Shape) — since we used the CircleService reference, that’s the method we got. And thus the implementation class needs to generate draw(Shape) and delegate to our draw(Circle).

In as far as our defect goes — this is exactly why it did not detect the annotation — it was intercepting the client-invoked, compiler-generated draw(Shape) which in turn invoked draw(Circle). draw(Circle) is of course an internal invocation, not accessed via the EJB proxy anymore, and hence never intercepted again.

Ah backwards compatibility.

That aside, there are a few observations to be made:

  • A generics call in the above fashion will always incur an additional cast check and method call, not a big deal for most code, but within a loop, etc. best to measure it. On the bright side, the checkcast is a single JVM instruction. Another optimisation – if you can even call it that! – is to explicitly declare a method draw(Circle) in CircleService – then it is generated as per normal, and client code goes directly to CircleServiceImpl.draw(Circle) – at this time, this is our workaround for the defect. I’ll leave the discussion of where the annotation should belong for another time (i.e. implementation class or interface – this is more a design principle/responsibility issue)
  • It’s interesting that specifically from an EJB 3 interception and transactional semantics viewpoint – this is quite nasty since you do not intercept a method that you think is part of your EJB interface, nor does the transactional semantics get applied (apart from the default ‘required’ of course)! I wonder if it might be better, if in an EJB 3 class, the generated draw(Shape) method did not do a plain invocation of draw(Circle), but accessed it via an internal EJB proxy reference (circleServiceEjbRef.draw(Circle)) – this would ensure proper transactional semantics and interception. Wait – the generated method in fact should not enforce any transaction type either — it should just be (if I recall correctly) using ‘supports’ semantics. As it stands, if draw(Circle) were annotated with a transaction attribute other than ‘required’ – this would not be picked up at all since it was a direct invocation, not via the EJB transactional proxy! Nasty, nasty! It could lead to some very obscure bugs!

Hmm.. I never remember reading about this in any generics book :P. Anyway, if you got this far, hope it was an informative read.

February 10, 2010

New Code: Groovy JSON Decoder/Parser

Filed under: software dev — Tags: , , , , — Kamal Advani @ 22:42

Finally got around to uploading this. Have not had time to make a proper public repository, doubt I will have time, so as usual, sources and tests are there to light the way!

August 17, 2009

Groovy Closure and Its Execution Context

Filed under: software dev — Tags: , , , , — Kamal Advani @ 03:17

I’ve been messing with Groovy – truly – the name aside – it is groovy, and I am impressed.

At some point I’ll say more about it, or dump out various code snippets I’ve been experimenting with to learn it, but for now I wanted to ensure I have this snippet recorded for reference – it is on closures and their context in Groovy. It took me a while to get it. It is always interesting how closures are defined within the context (no pun intended) of the JVM, where functions are not first-class types.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*
 * For a closure: basically local vars are the ones passed in {x,y,z -> blah}, everything
 * else is picked up and searched for from the execution context. Internally generated as
 * innerclasses with a supplied context consisting of 'this', owner, and delegate (the
 * internal Groovy closure base type is: groovy.lang.Closure -- see its Javadoc, this is
 * what closure blocks get translated to).
 *
 * Method/property resolution order for unqualified methods within a closure is in the
 * following order (and defined by the following context vars available to the closure):
 *
 * 1. this -- execution context - object to which the closure is first bound, e.g. script
 *    or class where defined, NOT the closure instance itself!
 *    
 * 2. owner -- usually equal to this, except when a closure (itself implemented as an
 *    innerclass) then creates an inner closure, that inner closure's 'this' is the outer
 *    closure instance. So that's one way to get access to a closure itself - create a
 *    subclosure and mess with it's 'this'!
 *    
 * 3. delegate -- usually set to owner, but can be modified (others are fixed), for
 *    metaprogramming purposes
 *
 * For better illustration check the following snippet
 */

enclosingInstance = this
outerclosure = {
    // by default, 'this' is always execution context (object enclosing the closure), never this
    // closure instance (which is implemented as groovy.lang.Closure (.doCall))
    assert this == enclosingInstance
    // And so is owner
    assert owner == this
    // this.owner throws a MissingPropertyException, since the outer script has no 'owner' property,
    // and 'this' is the outer script, not the closure instance!
    // But if the closure (outerclosure) creates another closure like so...
    innerclosure = { return this; }
    // The inner closure's owner is the outer closure instance!
    assert innerclosure.owner == outerclosure
    // However the inner closure's 'this' (returned) is still the initial enclosing instance, not the
    // outer closure.
    // Not quite sure if this is intuitive, hmm.
    assert innerclosure() == enclosingInstance
}
// run it!
outerclosure()
// check out it's superclass -- groovy.lang.Closure
println "superclass type: "+outerclosure.getClass().superclass
// done.

Type that out into some file say Foo.groovy and run it (yes you don’t need a static main() method in an enclosing public class with the same filename). The example is focussed on context, it does not provide samples of parameterised closures, or even parameterless ones (technically the closure above has one default parameter called ‘it’ which is null unless otherwise set in the call to the closure :P).

April 26, 2009

Spring 2.5 + EJB 3 Integration

Filed under: software dev — Tags: , , — Kamal Advani @ 01:49

Yes, yet another post on the topic so talked about on the Web (like a year ago or so anyway!). This is mostly for my benefit and summary though – haven’t even written code to test it myself, but here goes. Spent a few hours exploring integration alternatives just now, basically EJB 3 DI is great, but a glaring thing that’s missing is injecting POJOs into it, which Spring does quite well – sure in both cases the “POJOs” are managed by containers, but hey, nothing’s perfect, yet. To summarise, here are the 2 major integration scenarios and solutions:

  1. To inject Spring Beans into EJBs (and bootstrap Spring app context(s) from within EJBs!) is simple enough, we use:

    Spring Docs, Chapter 18, EJB 3 Integration

    This actually handles bootstrapping (read up the doc on how it does it), which is tricky in EJB modules, since there’s no standard EJB context listener mechanism as there is for web containers. Many app server vendors do have EJB module lifecycle hooks/interfaces you can implement – but obviously they’re proprietary and make your app less portable, so I’d rather avoid them if I can, I still don’t see why this feature isn’t part of the spec… clearly there are use cases, and clearly since there are non-standard implementations, it’s feasible to do and define behavior for.

  2. The other way around is a bit tricky, i.e. we now need EJB container-managed objects (e.g. entity managers, EBJs, resources, etc.) injected into Spring Beans in the XML file. In this case those resources need to be registered in the JNDI ENC (still figuring out EJB context vs. global) and in the Spring XML a JNDI lookup is done for the name it was registered with, and then injected to the relevant bean via a bean-ref. A ‘lookup-then-inject’ Spring XML config example can be seen here (note dataSource JNDI look-up and inject):

    Spring and JNDI (Tomcat or Jetty)

    Registering the container-managed objects in the JNDI can be done via EJB 3 interceptors, for example, a use case is having Spring-managed DAO beans, but EJB-managed Entities/Persistence Units: let the interceptor have an annotation

    @PersistenceUnit(unitName="bar", name=”barEntityManagerFactory”)

    – which receives the container-injected persistence unit and then registers it into the JNDI registry keyed by the name “barEntityManagerFactory”. The Spring XML config will need a bean created through a JNDI look-up of “barEntityManager”, and inject that in to the DAO Spring-managed bean.

    Lots of repetition up there! (Note I said it was for my benefit :P).

Now I can finally watch some Naruto!

April 25, 2009

JBoss 5.0.x (and 5.1 Beta?) and JPA Persistence Units

Filed under: software dev — Tags: , , — Kamal Advani @ 05:50

After much messing around, I finally have my sample EJB 3 app working, but on Glassfish!! I started off with JBoss, and may still go back to it once they decide on better handling of persistence units, and not reload/load it regardless! Too complicated for me to explain, check out these links which summarise it in depth (they speak about Seam, but refer to the same issue I’m babbling about):

http://relation.to/Bloggers/JBossAS5AndGlassFishSupportAddedToSeamgen
http://code.google.com/p/seaminaction/wiki/DeployingToJBossAS5

Basically I have a common domain-model jar which I wish to treat as a persistence unit used by both my EJB and WAR in the same EAR.

And after hours of tweaking and messing, this is my successful test output:

1
2
3
4
5
6
7
8
9
10
Creating...
Data Value=JustCreated!!

Retrieving...
Data Value=JustCreated!!

Updating...
Updated Data Value=JustUpdated!!

Deleting...

Ah, the joys of development. Actually that’s a pretty decent test case, it’s a servlet that’s got an EJB3 instance injected, which in turn has an EntityManager instance obtained via the JNDI name of a configured JTA XA MySQL datasource injected. What a mouthful!

February 24, 2009

Delegates in Java – Introducing delegates4j

Filed under: software dev — Tags: , , , — Kamal Advani @ 10:29

Background:

For a while now I’ve wanted a delegate-like (C# delegates – think of them as basically type-safe function pointers) feature to use in Java (and don’t mention closures or dynamic languages please, I know it will make my whole post obsolete :P), and I tried several attempts to simulate them, but didn’t quite get it right. I learnt about funky uses of the proxy pattern from my ex-team lead (very neat person, hey Steve!) in various projects. Then Java 5 came along…

No wait, before that, one day as I was messing around with cglib (I call it an AOP enabling tech through proxies – go proxy!), I noticed that they provided a dynamic delegate creator, in the cglib sources, see: net.sf.cglib.reflect.MethodDelegate – quoting from Apache Avalon‘s Delegate class, a delegate is defined as an interface with a single method. Note that Avalon has been marked as a closed project – but I don’t think it had anything to do with the delegate definition, phew.

delegates4j is Born:

Anyway, while that was neat (and backward compatible with pre-Java 5 stuff, hmph), it required a method name as a string, not a very big deal, but not quite what I wanted either, I wanted something a little more declarative, and something perhaps simpler, and a little more typesafe; and not requiring an external class enhancer (yes, maybe I just wanted to reinvent the wheel and not use cglib to do it). Annotations to the rescue! So! Keeping in mind that a delegate is an interface with a single method, let’s look at what I have (might want to paste this into an editor if it’s too annoying to read):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package net.namingcrisis.delegates4j.core;

import static net.namingcrisis.delegates4j.core.DelegateFactory.cdg;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class DelegateFactoryBasicTest {
    // Basic case, also a sampling of how the API is used.

    @Before
    public void setUp() throws Exception {
    }

    // Delegate type interface, only a single method.
    // Think of it as a function pointer in disguise
    // (C# of course supports delegates natively)
    private static interface IStringProcessor {
        String process(String s);
    }

    // A strategy class with different string processing routines
    // Does NOT extend StringProcessor, nor do the method names have to match,
    // this is the point.
    private static class GString { /*
                                     * It's a joke here, but GTK actually has a
                                     * gstring type
                                     */

        private static final String UPPER_CASE = "upper case";
        private static final String PRAYER = "prayers";
        private static final String EMPTY = "nada";

        @Implements(clazz = IStringProcessor.class, mode = UPPER_CASE)
        public String toUpperCase(String s) {
            return s.toUpperCase();
        }

        @Implements(clazz = IStringProcessor.class, mode = PRAYER)
        public String toPrayer(String s) {
            return "Hallelujah! " + s;
        }

        @Implements(clazz = IStringProcessor.class, mode = EMPTY)
        public String toEmptyString(String s) {
            return ""; // :P
        }
    }

    // A test client of StringProcessor, assume it to be a simple visitor
    // Returns a new array of processed strings
    public String[] printStrings(final String[] strings, IStringProcessor proc) {
        final String[] res = new String[strings.length];
        for (int i = 0; i < strings.length; i++)
            res[i] = proc.process(strings[i]);
        return res;
    }

    @Test
    public void testCreateDelegate() {
        final String[] strings = new String[] { "you", "and", "i" };
        final GString gs = new GString();

        // Dynamically create delegate instance, cdg is a static import
        // meaning "create delegate", deliberately keeping the name
        // short and unintrusive.
        // cdg(interfacetype, mode, implementationInstance)
        final IStringProcessor upperCaseProcessor = cdg(IStringProcessor.class,
                GString.UPPER_CASE, gs);
        String[] res = printStrings(strings, upperCaseProcessor);
        Assert.assertTrue(res.length == strings.length);
        for (int i = 0; i < strings.length; i++)
            Assert.assertEquals(res[i], gs.toUpperCase(strings[i]));

        final IStringProcessor prayerProcessor = cdg(IStringProcessor.class,
                GString.PRAYER, gs);
        res = printStrings(strings, prayerProcessor);
        Assert.assertTrue(res.length == strings.length);
        for (int i = 0; i < strings.length; i++)
            Assert.assertEquals(res[i], gs.toPrayer(strings[i]));

        final IStringProcessor emptyProcessor = cdg(IStringProcessor.class,
                GString.EMPTY, gs);
        res = printStrings(strings, emptyProcessor);
        Assert.assertTrue(res.length == strings.length);
        for (int i = 0; i < strings.length; i++)
            Assert.assertEquals(res[i], gs.toEmptyString(strings[i]));
    }
}

A couple of things (noted in comments):

  • The implementation class did NOT implement the delegate’s interface (or the delegate rather, “delegate interface” is redundant going by the above definition!), it merely provided compatible methods, whose names did not have to match.
  • The use of mode, is of course a string, and perhaps not very typesafe either, but it is exclusively there to support the scenario where multiple similar methods implement different behaviour, allowing you to have several strategies in a class, very handy for quick things. Probably not a good idea for larger reusable strategies. If you didn’t have it, just set it as an empty string (at the moment it’ll NPE-out otherwise :P)
  • This test doesn’t illustrate it, but to keep things single and fast (method resolution is done at runtime after all), it matches declared methods only, nothing inherited is usable – an interface that extends another but has no method will error out as a delegate type, a class with a superclass annotated method overridden but not annotated, also errors out. (The class bit is redundant, annotations are not inherited anyway, so).
  • Finally – also not illustrated by this test, delegates4j supports Java 5 covariance, where the return type of the implementor can be narrower than that of the delegate type.

Hopefully, it is simple enough to use – my initial attempts had multiple steps, but they tried to do more too. An improvement to this is possibly somehow caching resolved methods for reuse keyed by the annotation detail for example, in practice I don’t know if this is worth it yet.

The project is available as Maven downloadable jar (licensed under Apache License v2.0) in my repository, including sources, and tests to better illustrate usage/limitations (and of course, test that it works!). Set the dependency as follows: groupId = net.namingcrisis, artifactId = delegates4j, version = 0.5.

For the curious, under the hood, the only real magic is the use of the JDK Dynamic Proxy to implement the delegate and pass on the request to the supplied instance, and reflection for method resolution – the source is there anyway :-). If you do use it, hate it, try it out, etc. drop me a line anyway :-).

– Kamal

February 6, 2009

Wickedt!

Filed under: software dev — Tags: , , , , , , , — Kamal Advani @ 16:48

The title means “Wicket is Wicked!” in German (I know, lame, still kinda funny though :P). More seriously I just finished Parts 1 and 2 of Wicket in Action. I found the book to be very well-written with one exception: the analogies to the lasagna preparation made me very hungry – I am currently fasting, ahem. Apart from that, it certainly explained the framework well enough to enable to me to write this post, so kudos to the authors and Manning. Here’s my ‘review’ (if I may be so bold) of the book/framework (note that this is based on fairly limited usage writing toy apps, so caveat emptor):

I fully read and tried out examples in parts 1 and 2. I love the framework, it truly brings an OO component-driven model to web programming. As the authors aptly pointed out, Wicket solves the impedance mismatch problem between the stateless nature of HTTP and stateful object model, and Wicket solves it well! The impedance mismatch issue is analogous to the one we have between OOP and RDBMSes which of course ORM solutions like Hibernate solve. Some pros and cons:

Pros:

  • Familiar OO UI programming model, especially for people who’ve written thick client apps in toolkits like Java Swing.
  • Encourages typesafe – almost transparent – state maintenance using objects rather than direct manipulation of the Servlet HttpSession.
  • Excellent abstractions make learning logical and intuitive, for example a Page represents a top-level container, similar to a Frame object, to which you can add child components.
  • Minimal configuration gets you started. Pretty much just web.xml to configure your wicket app, the other plumbing can be done using Maven 2, definitely check out the bonus chapter 15 online very early on to configure your IDE so you can play with the examples.
  • Makes wizard creation – a fairly common and very tedious task in web programming, God the pain… – a breeze, not because it has specialised classes or declarative ways of doing it, but because state maintenance is easy, passing one page instance to the next page instance is straightforward (Page1.java: page1 = this; … setResponsePage(new Page2(page1, …)).
  • Previewability – allows HTML pages to be viewed with dummy data that will be removed at runtime. The authors admit however, if a page has a lot of components, it makes it more tedious to preview.
  • A lot of thought has gone into packaging new components, complete with their own resources such as CSS files, images, and Javascript. This extensibility does allow a lot of reuse of components easily, very much in line with OO principles.
  • I did not mess with this a lot – but Wicket seems to have decent Ajax support, including transparent fallback to non-Ajax behaviour if it is disabled on the browser. The authors point out that Wicket takes the view that Ajax is optional, and assuming you use the correct Ajax+Fallback component, no code changes are needed to accomodate both Ajax and non-Ajax behaviour. Chapter 10 of the book which I hardly read (part 3) goes in more depth about using Ajax.
  • Due to the stateful nature – makes checkbox and multi-selection handling a breeze, woohoo!!
  • A lot of built-in validators for common validations – having said that, these days I tend to prefer having validations (== business rules) done via my models, so this doesn’t buy me a lot – in fact it would make me put business rules in the web tier. Ideally we could annotate models and use some form of code generation (I hear screeches of pain…) for both the models and the client side for better responsiveness, of course some validations just can’t be done on the client side, so still thinking on this.

Cons/Gotchas:

  • There’s a lot of reliance on Java object serialization – to achieve transparent state maintenance, Wicket serializes the manipulated markup and code at the end of each request. This means any models you have used in the session must also be serializable. The good news is the authors spend time talking about this, and ways to get around it, using detachable models (I’d rather call them model wrappers for they wrap your actual model object) for example. This is also something to take note of when considering performance issues.
  • For people used to request-response based frameworks like Struts, the programming model will take a little bit of getting around – although I got around it pretty quickly, it seriously is way easier and more logical, especially to back-end programmers. As with any framework, there are new concepts to pick up here and there.
  • To bind models to web forms, strings are used in the form of property expressions (e.g. foo.getAddress().getStreet() -> “foo.address.street”), this obviously will not be picked up when your models get refactored. Hopefully smarter or Wicket-aware IDE plugins will help in this area. This plague is common to many frameworks… (e.g. in Hibernate: HQL and even when using the Criteria API).
  • In spite of adherence to the DRY principle, it does not generate HTML for you, and markup (which are tagged with “wicket:id” attributes referenced in the code) and code must remain in synch or Wicket will throw an error when accessing that page. This is good and bad, the nice bit is it does allow web designers to maintain the look and feel for you. Wicket IDs, Code Hierarchy, and Markup Hierarchy synchronisation is another place where Wicket-aware IDE plugins would be useful.

I only skimmed through Part 4, but it definitely has a lot of useful advice on integrating Wicket with with other toolkits and middleware such as Spring and Hibernate, and on internationalization (i18n) and localization (l10n) (still trying to figure what the latter’s scope is exactly…), something that’s missing in many books – while it’s not strictly necessary thoeretically, practically no framework works in isolation these days except perhaps if you get full-stack frameworks like Grails or JBoss Seam that Ruby on Rails popularised; dunno, I still kinda like cherry picking my parts, but I haven’t explored these so I may change my mind.

Hmm, I wonder how it compares to GWT – something else to try out when I have time, for now, time to check out EJB 3.

– Kamal

Powered by WordPress