4.2. Component Factories

4.2.1. ComponentFactory interface

At its heart Wicket Objects consists of a set of Wicket Components and corresponding IModel<?>s that are used to render entities, collections of elements and indeed individual members of elements. Each Components is created by a corresponding ComponentFactory, with the factory to use discovered using a chain-of-responsibility pattern.

The Components 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 Components 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 ???.

4.2.2. Registering ComponentFactorys using ComponentFactoryList

The set of ComponentFactorys 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.

4.2.3. Registering ComponentFactorys using META-INF Services

As an alternative to using ComponentFactoryList, you can also register new ComponentFactorys 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 ComponentFactorys, only add new ones. So if you do want to exclude any of the built-in ComponentFactorys, then you will still need to use the ComponentFactoryList method.