Apache Wicket gets a weird rep in Java circles. People love the HTML friendliness but get spooked by the word stateful. If you have been shipping apps on Tomcat or Jetty and someone told you Wicket will flood the session, stick around. From the trenches, Wicket components are stateful but manageable. You can keep memory tight, keep pages bookmarkable, and still enjoy a clean separation of markup and code.
Problem framing
Right now everyone is split across Spring MVC, JSF, Play and friends. Ajax is table stakes, mobile traffic is climbing, and ops keeps asking for smaller session footprints. Wicket scares folks because it stores page state so it can do component oriented magic. The fear is fair: if you throw domain objects into models and let page versioning run wild, memory will drift. The happy path is simple though. Choose stateless where it fits. Keep models lightweight. Turn off versioning when you do not need it. Use providers for big lists. The result is a calm codebase with friendly HTML and no surprise GC storms.
Three case walkthrough
Case 1: A stateless search page
Public pages like search and product detail should be bookmarkable and stateless. Wicket does that out of the box with bookmarkable pages, PageParameters and StatelessForm.
public class SearchPage extends WebPage {
private final IModel<String> q = Model.of("");
public SearchPage(PageParameters params) {
String fromUrl = params.get("q").toString("");
q.setObject(fromUrl);
add(buildForm());
}
public SearchPage() {
add(buildForm());
}
private Component buildForm() {
StatelessForm<Void> form = new StatelessForm<>("form") {
protected void onSubmit() {
PageParameters p = new PageParameters();
p.add("q", q.getObject());
setResponsePage(SearchPage.class, p);
}
};
form.add(new TextField<>("q", q));
return form;
}
}This page renders the same URL for the same query, plays nice with caches, and stores no heavy data in the session.
Case 2: A multi step wizard without bloating the session
Wizards are stateful by nature. Keep the footprint small with lightweight DTOs, turn off page versioning if you do not need back button history, and load entities lazily with a LoadableDetachableModel.
public class EditCustomerPage extends WebPage {
public EditCustomerPage(final Long id) {
IModel<Customer> customer = new LoadableDetachableModel<>() {
protected Customer load() { return customerService.get(id); }
};
setVersioned(false); // keep only the current page
Form<Customer> form = new Form<>("form", new CompoundPropertyModel<>(customer)) {
protected void onSubmit() {
customerService.save(getModelObject());
info("Saved");
}
};
form.add(new TextField<>("name"));
form.add(new TextField<>("email"));
add(form);
}
}The model detaches after each request, so only an id stays in memory. No giant object graphs in the session.
Case 3: A big list that stays quick
Do not stash a thousand rows in a panel. Let Wicket stream items with IDataProvider and DataView. It only loads the page slice you need.
public class OrdersPage extends WebPage {
public OrdersPage() {
IDataProvider<Order> provider = new IDataProvider<>() {
public Iterator<? extends Order> iterator(long first, long count) {
return orderRepo.page(first, count).iterator();
}
public long size() { return orderRepo.count(); }
public IModel<Order> model(Order o) {
return new LoadableDetachableModel<>() {
private final Long id = o.getId();
protected Order load() { return orderRepo.get(id); }
};
}
public void detach() { }
};
DataView<Order> view = new DataView<>("rows", provider, 25) {
protected void populateItem(Item<Order> item) {
Order o = item.getModelObject();
item.add(new Label("id", o.getId()));
item.add(new Label("total", o.getTotal()));
}
};
add(view);
}
}Memory stays flat, paging is smooth, and markup remains clean for your CSS and your designer.
Objections and replies
- Wicket will blow up my session. Only if you let it. Keep models lean, prefer providers, and tune settings. You can cap per session page bytes and back pages. Look at
getStoreSettings()andgetPageSettings()in yourWebApplication. - I lose control of HTML. Wicket markup is plain HTML with
wicket:id. You keep semantic tags. Your designer can work in a normal editor. Components bind without messing with the structure. - State breaks bookmarks. Use bookmarkable pages with
PageParametersfor search, detail and filter views. Mix stateless and stateful pages in the same app.
Action oriented close
Want sane state with Apache Wicket? Use this short list and ship.
- Favor stateless for entry points like home, login, search and detail. Use
StatelessFormandPageParameters. - Control page versioning. Call
setVersioned(false)on pages that do not need back button history. - Keep models tiny. Wrap entities in
LoadableDetachableModel. Pass ids not graphs. - Stream big data. Use
IDataProviderwithDataVieworDataTable. No giant lists in memory. - Tune the store. In your app init, set limits:
getStoreSettings().setMaxSizePerSession(Bytes.kilobytes(512)); getPageSettings().setVersionPagesByDefault(false); - Measure. Turn on Wicket debug bars in dev, log page sizes, and watch session bytes in your container.
Wicket components are stateful by design, which is why they feel nice to code. Keep the state where it helps you, trim it where it does not, and you get a tidy Java web framework with clear HTML and steady memory. That is a deal I will take any day.