Dear fellow wicket users,

Following is a call for your opinion and a vote on how Wicket should behave 
with regards to post-constructing components in 1.5.  The final solution that 
is chosen will affect the way you construct your components and your component 
hierarchies.



First, some background on what onInitialize is and why you should be using it.

THE PROBLEM

Historically, we've generally used our component constructors to build our 
component's hierarchy.  We call add() to put new components in our custom 
component from our component's constructor:

public class MyPanel extends Panel {

    private final IModel<String> name = Model.of();

    public MyPanel(String id) {

        add(new TextField<String>("name", name));
    }
}

This can cause problems, however, when initializing a component requires 
knowledge of the component's markup, or requires that the component's path in 
the hierarchy is known (during construction, the component is not yet added to 
any parent component).


THE SOLUTION

onInitialize was introduced to Component.  This gives us a place to initialize 
our component's hierarchy after our component has been added to its parent.  As 
a result, our component has a path to its page, making markup, parent and 
sibling components accessible.


THE BAD

The downside of initializing your component hierarchy in onInitialize as 
opposed to from the constructor, are:

1. You need to move all your component hierarchy initialization code into a 
separate method.  Usually, this is trivial work (cut/paste), but it is work 
that needs to be done nonetheless.
2. You cannot put your components in final instance fields anymore.
3. You should never do anything with components from custom methods.  You 
should only ever reference your components directly from overridden wicket 
methods, such as onConfigure, onBeforeRender, onSubmit, etc.

A little more on #3:

If you add custom methods to your component, eg.

void makeRequired() {
    nameField.setRequired(true);
}

You allow things like: new MyPanel().makeRequired();
That would throw a NullPointerException, since nameField is no longer 
initialized in the constructor, but much later, before the component first gets 
rendered.

I would argue, though, that any custom methods that touch components are to be 
avoided at all cost.  Component behaviors should never be touched except from 
wicket lifecycle methods (you probably want onConfigure) or right after you 
construct it:  add(new TextField<String>("name", name).setRequired(true));  If 
you need a custom method such as makeRequired, it shouldn't touch components, 
it should set state that gets used by onConfigure:

void makeRequired() {

    this.required = true;
}

void onConfigure() {

    setRequired(required);
}

(Obviously, the example is rather silly..)



Fast-forward to https://issues.apache.org/jira/browse/WICKET-3218, it seems 
onInitialize has a problem.

THE PROBLEM

With regard to pages, if you add a component to a page from the constructor, 
onInitialize is invoked immediately.  As a result, onInitialize code is ran 
even though your instance hasn't been fully constructed yet.  This is bad.

The real issue here is that we can combine component hierarchy initialization 
with constructors and onInitialize in a single class.  However, doing so causes 
dangerous situations.  We really should do only one, not both at the same time.


THE SOLUTIONS

This is where we need your vote.  Currently, two proposed solutions exist:

1. Make onInitialize final for all Pages.  As a result, onInitialize will still 
get called when the page is constructed and components are added, but you won't 
be able to use it, avoiding the risk of you using it in a manner that requires 
your instance to be constructed.

2. Make adding components from the constructor illegal.  This would have to be 
by means of a runtime exception thrown whenever you try to add components to 
the hierarchy from the constructor.


THE GOOD

1. This will be least painful for existing code.  In all components, you can 
still mix constructor an onInitialize use, in pages, you can't use 
onInitialize, but in all likelihood, the largest amount of existing wicket code 
out there still uses constructors to create component hierarchies.

2. The safer onInitialize will always and consistently be used for any 
component hierarchy construction.  This also requires the good code habits that 
you shouldn't be doing anything with your components outside of wicket 
lifecycle methods.


THE BAD

1. Mixing constructor and onInitialize usage, whether it's within one class or 
between different classes in your type and component hierarchy, is and will 
always be dodgy.  You really shouldn't do it.  Also, to future-proof your code, 
you should really already be moving your initialization code into onInitialize. 
 Additionally, this introduces an API inconsistency: For pages, you cannot use 
onInitialize, for everything else, you should.  That's not very intuitive.  
Additionally, it makes refactoring pages into panels and back a real pain 
(we've had to do this quite often already).

2. Checking for add() usage with a runtime exception is not very neat.  You'll 
only notice your mistake at runtime (albeit, very soon.  And during your 
migration to 1.5, you should expect more trouble than just this).  Probably the 
biggest down-side is that a lot of people will need to move code around, from 
constructors into onInitialize (it can be argued that you really should be 
doing this anyway).


REFERENCES

The original discussion on the onInitialize issue with Pages:
http://mail-archives.apache.org/mod_mbox/wicket-dev/201012.mbox/%[email protected]%3E

The JIRA issue:
https://issues.apache.org/jira/browse/WICKET-3218

Please refer to these if you're not entirely sure about the arguments of either 
side.

Cast your vote for the future shape of wicket's component hierarchy 
initialization API.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to