< Magazine />


Apache Wicket - Best Practices (English)

Friday, January 27, 2012 by Carsten Hufe   Tags:  apache   wicket   best   practice 

Apache Wicket became very popular in the last months and has been used in many projects. Due to the power of Wicket you can develop features very easy and fast. There are many ways to realize these features though. This article provides some cook recipes for the correct, efficient and pertinent usage of Apache Wicket.

 

This article is addressed to developers, who has already made their first experiences with Apache Wicket. Developer, who get into Wicket often have difficulties with it, because they apply the typical JSF and Struts patterns and approaches. These frameworks primarily use procedural programming methods. In contrast Wicket is strongly based on object oriented patterns. So forget the Struts and JSF patterns, otherwise you won't have fun with Wicket in the long run.

Encapsulate components correctly

A component should be self-contained. The user of a component should neither have to know nor care about its internal structure. She should just be familiar with its external interfaces and its documentation in order to be able to use it. This means in detail: Every component that extends Wicket's own Panel type (thus is a Panel itself) must provide its own HTML template. In contrast when a component extends the classes WebMarkupContainer or Form, there is no HTML template. This implicates that you should add components through composition in WebMarkupContainer or Form.

// Poor component
public class RegistrationForm extends Form<Registration> {
    public RegistrationForm(String id, IModel<Registration> regModel) {
        super(id, new CompoundPropertyModel<Registration>(regModel))
        // Wrong: RegistrationForm provides its own components
        add(new TextField("username"));
        add(new TextField("firstname"));
        add(new TextField("lastname"));
    }
}

Listing 1

 

Listing 1 is an example for a poor component. The user of the RegistrationForm must know the internal structure of the markup and component in order to use it.

public class RegistrationPage extends Page {
    public RegistrationPage(IModel<Registration> regModel) {
        Form<?> form = new RegistrationForm("form");
        form.add(new SubmitButton("register") {
            public void onSubmit() {
                 // do something               
            }
        });
        add(form);
    }
}

<html>
<body>
    <form wicket:id="form">
        <!-- These are internal structure information from RegistrationForm -->
        Username <input type="text" wicket:id="username"/>
        First name <input type="text" wicket:id="firstname"/>
        Last name <input type="text" wicket:id="lastname"/>
        <!-- Above new components from page which the user knows  -->
        <input type="submit" wicket:id="register" value="Register"/>
    </form>
</body>
</html>

Listing 2

 

Listing 2 shows the usage of the poor component in the RegistrationPage. You can see that the input fields firstname, lastname and username get used, even though these components are not added explicitly to the RegistrationPage. Avoid this, because other developers cannot directly see that the components were added in RegistrationPage class.

// Good component
public class RegistrationInputPanel extends Panel{
    public RegistrationInputPanel(String id, IModel<Registration> regModel) {
        super(id, regModel);
        IModel<Registration> compound = new CompoundPropertyModel<Registration(regmodel)
        Form<Registration> form = new Form<Registration>("form", compound);
        // Correct: Add components to Form over the instance variable
        form.add(new TextField("username"));
        form.add(new TextField("firstname"));
        form.add(new TextField("lastname"));
        add(form);
    }
}

<html>
<body>
    <wicket:panel>
    <form wicket:id="form">
        Username <input type="text" wicket:id="username"/>
        First name <input type="text" wicket:id="firstname"/>
        Last name <input type="text" wicket:id="lastname"/>
    </form>
    </wicket:panel>
</body>
</html>

Listing 3

 

Listing 3 shows a cleanly cut input component, which provides its own markup. Furthermore you can see the correct usage of a Wicket Form. The components get added by calling form.add(Component) on the instance variable. On the other hand it is allowed to add behaviours and validators over inheritance, because those do not have markup ids which must be bound.

public class RegistrationPage extends Page {
    public RegistrationPage(IModel<Registration> regModel) {
        Form<?> form = new Form("form");
        form.add(new RegistrationInputPanel("registration", regModel);
        form.add(new SubmitButton("register") {
            public void onSubmit() {
              // do something               
            }
        });
        add(form);
    }
}

<html>
<body>
    <form wicket:id="form">
        <div wicket:id="registration">
           Display the RegistrationInputPanel
        </div>
        <input type=”submit” wicket:id="register" value="Register"/>
    </form>
</body>
</html>

Listing 4

 

Listing 4 shows the usage of the RegistrationInputPanel. There is no markup of another embedded components present anymore, just markup of components, which get directly added. The RegistrationPage provides its own form, that delegates the submit to all Wicket nested forms which are contained in the component tree.

Put models and page data in fields

In contrast to Struts Wicket pages and components are no singletons, they are stateful and session-scoped. This enables us to store user-specific information within pages and components. The information should be stored in fields. This way you can access the information within a class while avoiding long method signatures only for passing this very same information around. Instances of components can exist over several requests. For example a page with a form which gets submitted and produces validation errors uses the same page instance. Furthermore the same page instance gets used when the user presses the back button of the browser and resubmits this formular again. Information which gets passed by the constructor should be assigned to fields (normally this must be models). When storing information in fields you should consider that the information is serializable, because the pages are stored in Wicket's page map. By default the page map stores the pages on the hard disk. A non serializable object leads to NullPointerExceptions and NonSerializableExceptions. Additionally big data (like binary stuff) should not be stored directly in fields, because this can cause performance losses and memory leaks while serialization and deserialization. In this case you should use the LoadableDetachableModel, which can be assigned to a field, because this provides an efficient mechanism to load and detach data.

Correct naming for Wicket IDs

Naming is for many developers a dispensable thing, but I think it is one of the major topics in software development. With the help of correct naming you identify fastly the business aspects of a software component.

Additionally good naming avoids unneccessary and bad comments.

Bad namings for Wicket-IDs are birthdateTextField, firstnameField and addressPanel. Why? The naming contains two aspects: A technical aspect "TextField" and the business aspect "birthdate". Relevant is just the business aspect because the HTML template already presents the technical aspect through <input type="text"/> and the Java code presents it by "new TextField("birthdate")". So the technical aspects are already described. Additionally the incorrect naming adds a lot of effort when you do technical refactorings, e.g. if you have to replace a TextField by a DatePicker, in result of it you have to rename the Wicket ID from birthdateTextField to birthdateDatePicker. Another reason for avoiding technical aspects in Wicket IDs is the CompoundPropertyModel. This model delegates the properties to its child components named by Wicket IDs (see listing 3). For example the TextField username calls automatically setUsername() and getUsername() on the Registration object. A naming of the setter setUsernameTextfield() would be very unpracticable here.

Avoid changes at the component tree

You should picture Wicket's component tree as a constant and fixed skeleton which gets revived when its model is filled with data like a robot without brain. Without brain the robot is not able to do anything and is just dead and a fixed skeleton. However when you fill it with data, it becomes alive and can act. There is no need of changing hardware when filling him with data. Components can decide on the state by its own by viewing on the data, e.g. on its visibility. In Wicket you should manipulate the component tree as little as possible. Consequential you should avoid calling methods like Component.replace(Component) und Component.remove(Component). Calling these methods indicates missing usage or misusage of Wicket's models. Furthermore the component trees should not conditionally constructed (see listing 5). This reduces the possibility to reuse the same instance significantly.

// typical struts
if(MySession.get().isNotLoggedIn()) {
    add(new LoginBoxPanel("login"))
}
else {
    add(new EmptyPanel("login"))   
}

Listing 5

 

Instead of constructing LoginBoxPanel conditionally, it is recommend to add the panel always and control the visibility by calling setVisibilityAllowed(boolean) within the LoginBoxPanel. So the component LoginBoxPanel is responsible for displaying itself. We move the responsibility into the same component, which executes the login. Brilliant! Business logic cleanly encapsulated. There is no decision from outside, the component handles all the logic. You can see more concrete example in "Implement visibilities of components correctly".

Implement visibilities of components correctly

Visibility of site parts is an important topic. In Wicket you control the visibility of components over the methods  isVisible() and setVisible(). These methods are within Wicket's base class Component and therefore it regards every component and page. Let's have a look to a concrete example of LoginBoxPanel. The panel just gets displayed when the user is not logged in.

// Poor implementation
LoginBoxPanel loginBox = new LoginBoxPanel("login");
loginBox.setVisible(MySession.get().isNotLoggedIn());
add(loginBox);

Listing 6

 

Listing 6 shows a poor implementation, because a decision of visibility is made while instanciating the component. Again in Wicket instances of components exist over several requests. To reuse the same instance you have to call loginBox.setVisible(false). This is very unhandy, because we always have to call setVisible() and manage the visibility. Unfortunately we cannot avoid to duplicate the states. So that means visible is equal to "not logged in". So we have two saved states, one for the business aspect "not logged in" and one for the technical aspect "visible". Both are always equal. Some while ago I preferred and recommended to override the isVisible() method, so I could avoid duplicate states. Now I cannot recommend it anymore, because it is not sure when and how often the isVisible() method is called. This can cause side effects.
To keep the two states with less effort in sync, I recommend to override the onConfigure() method and call setVisibilityAllowed(boolean). You should call setVisibilityAllowed(boolean) because isVisibilityAllowed() is final and cannot be overidden in contrast to isVisible(). Take a look at the diagram, there is an application flow with some calls. We avoid three calls through this principle and we just have to instanciate the LoginBoxPanel to provide this logic with the component.

 

Login Panel

 

public class LoginBoxPanel {
    // constructor etc
    @Override
    protected void onConfigure() {
        setVisibilityAllowed(MySession.get().isNotLoggedIn());
    }
};


Listing 7

 

In listing 7 the control over visibility has got inverted, now the LoginBoxPanel decides over the visibility by its own. When onConfigure() is getting called, the visibility state gets automatically refreshed by the interpretion of the login state. Concluding there is no out-dated information, because it is kept in sync with each component rendering. The logic is centralised in one line code and not spread application wide. Furthermore you can easily identify that the technical aspect "visible" correlates to the business aspect "logged in". The same rules can be applied to the method isEnabled(). If isEnabled() returns false the components get displayed gray, but here you have to call setEnabled() instead of setEnabledAllowed(). Forms which are within an inactive or invisible component do not get executed. Sometimes there are cases you cannot avoid to call the method setVisiblilityAllowed() from outside of onConfigure(). An example: The user presses a button to display an inlined registration form. In general you can apply the following rules: Data driven components should override onConfigure() and call setVisibilityAllowed(boolean). User triggered events call the method setVisibleAllowed(boolean) within an event like onClick(). Finally you should avoid to set the visibility within the constructor hierarchy and set it from outside. As well you can override these methods by inline implementations (see listing 8).
 

new Label("headline", headlineModel) {
 
    @Override    
    protected void onConfigure() {
     // hide headline when it contains Berlosconi
        String headline = getModelObject();
        setVisibilityAllowed(headline.startWith("Berlosconi"))
    }
}




Listing 8

 

Note: Updated at 15th November 2011. Set the visibility with setVisibiltyAllowed(boolean) in onConfigure(). Do not override isVisible(). Update was inspired by readers feedback. Thanks to you!

Always use models

Always use models! Do not pass raw objects directly to components. Instances of pages and components can exist over several requests. If you use raw objects, you cannot replace them later. An example is an entity which gets loaded at each request within a LoadableDetachableModel. The entity manager creates a new object reference, but the page would keep the obsolete instance. Always pass IModel<MyClass> in the constructor of your components (see listing 9).

public class RegistrationInputPanel extends Panel{
    // Correct: The class Registration gets wrapped by IModel
    public RegistrationInputPanel(String id, IModel<Registration> regModel) {
        // add components
    }
}

Listing 9

 

The solution in listing 9 could have every implementation in the model. Beginning with the class Model over the PropertyModel and an own implementation of LoadableDetachableModel, which loads and persists the values automatically. The model implementations get very easy to replace. You - as user - just want to know: If I call IModel<Registration>.getObject(), I will get an object of type Registration. Where the object comes from is the charge of the model implementation and the calling component. For example you can pass the model while instanciating the component. If you avoid using models, you will get the dilemma to modify the component tree sooner or later. Whereby you duplicate states  and thus produce unmaintable code. Additionally you should use models by reason of serialization. Objects which get stored in fields of pages and components get serialized and deserialized on each request. This could be inperformant in some cases.

Do not unpack models within the constructor hierarchy

Avoid unpacking models within the constructor hierarchy, that means do not call IModel.getObject() within the constructor hierarchy. As aforementioned a page instance can exist over several page requests, so you can hold an obsolete and redundant infomation. It is allowed to unpack Wicket Models at events (user actions), that are methods like onUpdate(), onClick() or onSubmit() (see listing 10).

new Form("register") {
    public void onSubmit() {
        // correct, unpack model in an event call
        Registration reg = registrationModel.getObject()
        userService.register(reg);
    }
}

Listing 10

 

An additional possibility to unpack models is through overriding methods like isVisible(), isEnabled() or onConfigure().

Pass-trough models to components of extending class

Pass-through models to the parent component. So you ensure, that at the end of every request the method IModel.detach() get called. Those method is responsible for a data cleanup. Another example: you have your own model implemented which persists the data in the detach() method. So the call of detach() is neccessary that your data gets persisted. You can see an exemplary pass-through to the super constructor at listing 11.

public class RegistrationInputPanel extends Panel{
    public RegistrationInputPanel(String id, IModel<Registration> regModel) {
        super(id, regModel)
        // add components
    }
}

Listing 11

Validators must not change any data or models

Validators just should validate. For example: a bank account form which has a BankFormValidator. This validator checks the bank data over a webservice and corrects the bank name. Nobody would expect that a validator modifies information. Such logic has to be located in Form.onSubmit() or in the event logic of a button.

Do not pass components to constructors

Do not pass components or pages to constructors of other components.

// Bad solution
public class SettingsPage extends Page {
    public SettingsPage (IModel<Settings> settingsModel, final Webpage backToPage) {
        Form<?> form = new Form("form");
        // add components
        form.add(new SubmitButton("changeSettings") {
            public void onSubmit() {
               // do something   
               setResponsePage(backToPage)           
            }
        });
        add(form);
    }
}

Listing 12

See listing 12: The SettingsPage expects in the constructor the page which should be displayed after a successful submit. This solution works, but is very bad style and unflexible. You have to know at the instanciation of SettingsPage where you want to redirect the user. This requires a predetermied order of instanciation. Better it is to order the instanciation after business logic (e.g. the order in the HTML template).  Furthermore you need an unnecessary instance of the next page, which could not be displayed. The solution is the hollywood principle again. For this you create an abstract method or a hook (listing 13).

// Good solution
public class SettingsPage extends Page {
    public SettingsPage (IModel<Settings> settingsModel) {
        Form<?> form = new Form("form");
        // add components
        form.add(new SubmitButton("changeSettings") {
            public void onSubmit() {
               // do something
               // e.g. persist data   
               onSettingsChanged()   
            }
         });
         add(form);
    }

    // Hook
    protected void onSettingsChanged() {
    }
}

// The usage of the new component
Link<Void> settings = new Link<Void>("settings") {
    public void onClick() {       
        setResponsePage(new SettingsPage(settingsModel) {
            @Override
            protected void onSettingsChanged() {
               // Referenz der aktuellen Seite
               setResponsePage(MyPage.this);
            }
        });
    }
}
add(settings);

Listing 13

 

The solution from listing 13 has more code, but is more flexible and significant. We can see there is an event onSettingsChanged() and this event is called after a successful change. Furthermore there is the possibility to execute more code just than set the back page. For example you can display messages or persist information.

Use the Wicket session only for global data

The Wicket session is your own extension from Wicket's base session. It is fully typed. There is no map structure to store information unlike the servlet session. You just should use Wicket's session for global data. Authentification is a good example for global data. The login and user information is required on nearly each page. For a blog application it would be good to know, whether the user is an author who is allowed to compose blog entries. So you are able to hide or or show links to edit a blog entry. In general you should store the whole authorization logic in Wicket's session, because it is a global thing and you would expect it there. Data of forms and flows which span over several pages should not stored in the session. This data can be passed from one page to the next over the constructor (listing 14). As a consequence of this, the models and data have a defined life cycle over the page flow.

public class MyPage extends WebPage {
    IModel<MyData> myDataModel

    public MyPage(IModel<MyData> myDataModel) {
        this.myDataModel = myDataModel;
        Link<Void> next = new Link<Void>("next") {
             public void onClick() {       
                  // do something
                  setResponsePage(new NextPage(myDataModel));
             }
        }
        add(next);
    }
}

Listing 14

 

As you see in listing 14 you should pass concrete information to the page. All models can be stored in fields, because Wicket pages are userspecific instances and no singletons in contrast to Struts. The big advantage of this approach is, that the data gets automatically cleaned up when user completes or exits the flow early. No manually cleanup anymore! This is quasi a garbage collector for your session.

Do not use factories for components

The factory pattern is an useful pattern, but unsuitable for Wicket components.

public class CmsResource {
   public Label getCmsLabel(String markupId, final String url) {
       IModel<String> fragment = new AbstractReadOnlyModel<String>() {
          @Override
          public String getObject() {
             return loadSomeContent(url);
          }
       };
       Label result = new Label(markupId, fragment);
       result.setRenderBodyOnly(true);
       result.setEscapeModelStrings(false);
       return result;
   }

   public String loadContent(String url) {
      // load some content
   }
}

// create the component within the page:
public class MyPage extends WebPage {
   @SpringBean
   CmsResource cmsResource;
   
   public MyPage() {
      add(cmsFactory.getCmsLabel("id", "http://url.to.load.from"));
   }
}

Listing 15

 

The approach in listing 15 for adding a label from the CmsFactory to a page seems to be initially ok, but brings some disadvantages. There is no possibility anymore to use inheritance. Furthermore there is no possibility to override methods like onClick(). The factory could be also a spring service which instanciates the component. A better solution is to create a CmsLabel (listing 16).

public class CmsLabel extends Label {
   @SpringBean
   CmsResource cmsResource;
   public CmsLabel(String id, IModel<String> urlModel) {
      super(id, urlModel);
      IModel<String> fragment = new AbstractReadOnlyModel<String>(){
         @Override
         public String getObject() {
            return cmsResource.loadSomeContent(urlModel.getObject());
         }
      };
      setRenderBodyOnly(true);
      setEscapeModelStrings(false);
   }
}

// create the component within a page
public class MyPage extends WebPage {
   public MyPage() {
      add(new CmsLabel("id", Model.of("http://url.to.load.from")));
   }
}

Listing 16

 

The label from listing 16 is clearly encapsulated in a component without using a factory. Now you can easily create inline implementations and override your methods or other stuff. Now someone could say "I need a factory to initialize some values in the component, e.g. a spring service.". For this you can create a implementation of IComponentInstantiationListener. This listener gets called on the super-constructor of every component. The most popular implementation of this interface is the SpringComponentInjector which injects spring beans in components when the fields are annotated with @SpringBean. You can easliy write and add your own implementation of IComponentInstantiationListener. So there is no reason using a factory anymore. More information about the instanciation listener is located in Wicket's javadoc.

Every page and component expects a test

Every page and component should have a test. The simplest test just renders the component and validates its technical correctness. For example a child component should have a matching wicket id in the markup. If the wicket id is not correctly bound - through a typo or it was just forgotten - the test will fail. An advanced test could test a form, where a backend call gets executed and validated over a mock. So you can validate your component's behaviour. This is a simple way to detect and fix technical and business logic bugs while the build process. For the test driven development approach, you can use wicket quite well. Lets say you have a page so you can validate the correctness of the page by running the unit test. If you run the unit test which fails and shows a message that the wicket id not bound, so you will avoid an unneccessary server startup, because a server startup takes longer than running a unit test. This reduces the development turnaround. A disadvantage is the difficult testing possibility of AJAX components. However the testing possibilites of Wicket are much more than in other web frameworks.

Avoid interactions with other servlet filters

Try to get as long as possible within the Wicket world. Avoid the usage of other servlet filters. For this you can use the RequestCycle and override the methods onBeginRequest() and onEndRequest(). You can apply the same to the HttpSession. The equivalent in Wicket is the WebSession. Just extend the WebSession and override the newSession()-method from the Application class. There are very few reasons to access the servlet interfaces. An example could be to read an external cookie to authentificate a user. The parts should encapsulated well and minimized. For this example you could put the handling in the Wicket session, because this is an authentification.

Cut small classes and methods

Avoid monolithic classes. Often I have seen that developers put the whole stuff into constructors. These classes are getting very unclear and chaotic, because you use inline implementations over serveral levels. It is recommended to group logical units and extract methods with a correct business naming. This enhances the clarity and the understandability of the business aspect. When a developer navigates to a component, he is not interested in the technical aspect at first, however he just need the business aspect. To retrieve technical information of a component you can navigate to the method implementation. In case of doubt you should consider to extract seperate components. Smaller components increase the chance of reuse and make the testability easier. Listing 17 shows an example of a possible structuring.

public class BlogEditPage extends WebPage {
    private IModel<Blog> blogModel;

    public BlogEditPage(IModel<Blog> blogModel) {
        super(new PageParameters());
        this.blogModel = blogModel;
        add(createBlogEditForm());
    }

    private Form<Blog> createBlogEditForm() {
        Form<Blog> form = newBlogEditForm();
        form.add(createHeadlineField());
        form.add(createContentField());
        form.add(createTagField());
        form.add(createViewRightPanel());
        form.add(createCommentRightPanel());
        form.setOutputMarkupId(true);
        return form;
    }
    
    // more methods here
}

Listing 17

The argument "Bad documentation"

Often I have heard that Wicket has a bad documentation. This argument is just particular correct. There are a lot of code samples and snippets which can be used as code templates. Furthermore there is a big community that answers complex questions in shortest time. In Wicket it is very hard to document everything, because nearly everything is extensible and replaceable. If a component is not completely suitable, you will extend or replace it. The work with Wicket is a permanent navigating through the code. For example the validators. How can I find all navigators that exists? Open the interface IValidator (Eclipse Ctrl + Shift + T) and then open the type hierachy (Crtl + T). Now we are seeing all the validators existing in Wicket and our project.

 

Type Hierachy

 

Conclusion

These advices should help you to write better and more maintainable code in Wicket. All described methodologies were already proved in a few Wicket projects. If you follow these advices, your Wicket projects will get future-proof and successful hopefully.

Links:
1. http://wicket.apache.org/ (Apache Wicket)
2. http://wicketstuff.org/wicket14/ (Wicket Examples)
3. http://en.wikipedia.org/wiki/Hollywood_Principle

 

Thanks to Daniel Bartl for the correction.

 

© 2009-2014 - www.devproof.org