At its heart Wicket Objects consists of a
set of Wicket Component
s and
corresponding IModel<?>
s that are used to
render entities, collections of elements and indeed individual members
of elements. Each Component
s is created by a
corresponding ComponentFactory
, with the
factory to use discovered using a chain-of-responsibility
pattern.
The Component
s created by
Wicket Objects vary in size from rendering an
entire collection of entities all the way down to a single property of
an entity. You can find the full set of built-in
Component
s by searching for implementations of
ComponentFactory
:
For example, the
CollectionContentsAsAjaxTableFactory
class is
used to render a collection of entities (eg returned from an action
invocation) as, erm, an ajax table;
public class CollectionContentsAsAjaxTableFactory extends ComponentFactoryAbstract { private static final long serialVersionUID = 1L; private static final String NAME = "styled"; public CollectionContentsAsAjaxTableFactory() { super(ComponentType.COLLECTION_OF_ENTITIES, NAME); } @Override public boolean appliesTo(IModel<?> model) { return model instanceof EntityCollectionModel; } public Component createComponent(String id, IModel<?> model) { EntityCollectionModel collectionModel = (EntityCollectionModel) model; return new CollectionContentsAsAjaxTable(id, collectionModel); } }
The selection of the ComponentFactory
is
based on two criteria: the ComponentType
, and
the IModel<?
>. Broadly speaking the
ComponentType
standardizes the
wicket:id
used in the HTML
fragment (so <div wicket:id="collectionContents"/>
would map onto the
ComponentType.COLLECTION_CONTENTS
, while the
IModel<?>
is the corresponding
information used for the rendering of that component. But there's a
semi-formal relationship between these two concepts; the
ComponentType
effectively acting as a
power-type for the subclass of IModel<?>
that is supplied.
The superclass, ComponentFactoryAbstract
,
then, takes responsibility for checking that the
ComponentType
of the model, using the value
provided in the constructor:
public abstract class ComponentFactoryAbstract implements ComponentFactory ... { ... public final boolean appliesTo(ComponentType componentType, IModel<?> model) { return componentType == getComponentType() && appliesTo(model); } protected abstract boolean appliesTo(IModel<?> model); ... }
The subclass then refines this check by overriding
appliesTo()
to also check the model;
returning true indicates that the
ComponentFactory
is able to render that model,
after which the createComponent()
method is
then called to actually create the instance. So in
CollectionContentsAsAjaxTableFactory
, its
implementation simply checks if the supplied model is an
EntityCollectionModel
. More sophisticated/less
generic ComponentFactory
might also make
additional checks; you can find some examples of these in ???.
The set of ComponentFactory
s that are
provided with Wicket Objects are specified by the
ComponentFactoryList
interface, with a default
implementation instantiated by the
WicketObjectsApplication
:
public class WicketObjectsApplication extends AuthenticatedWebApplication { ... protected ComponentFactoryList newComponentFactoryList() { return new ComponentFactoryListDefault(); } }
To add new factories, or indeed remove support for any of the
default factories, all that's needed is to create a custom subclass of
NakedObjectsApplication
and override this
method, eg:
public class MyApplication extends NakedObjectsApplication { ... protected ComponentFactoryList newComponentFactoryList() { return new MyComponentFactoryList(); } }
If you look at
ComponentFactoryListDefault
you'll see that it
is written so that it can be easily subclassed and overridden on an
as-needed basis. To use your custom subclass, then you'll need to
override the newComponentFactoryList()
method
in the WicketObjectsApplication bootstrap class:
public class WicketObjectsApplication ... { ... protected ComponentFactoryList newComponentFactoryList() { return new MyComponentFactoryList(); } }
To pick up your subclass of
WicketObjectsApplication
, just update the
web.xml
file.
As an alternative to using
ComponentFactoryList
, you can also register new
ComponentFactory
s using the JDK's own
ServiceLoader
capability.
All that is needed is for your
ComponentFactory
to be registered in a file on
the classpath call
META-INF/services/org.starobjects.wicket.ui.ComponentFactory
.
The contents of this file should be the fully qualified class name of
your ComponentFactory
implementation. And
that's it! Registering the ComponentFactory
is
done automatically just by virtue of updating the classpath. You'll
find that the custom components described in Chapter 5, Custom Components all use this technique.
Note that this technique does not allow you to remove existing
ComponentFactory
s, only add new ones. So if you
do want to exclude any of the built-in
ComponentFactory
s, then you will still need to
use the ComponentFactoryList
method.