UI Plugin Architecture
The Hippo CMS UI has a modular architecture based on Apache Wicket. A user interface plugin may add elements to the user interface, change the look and feel or change a functionality.
Each plugin has its own configuration that provides an indirection layer, used to translate plugin specific names into application/cluster-wide names. So when a plugin wants to know under what name it can find the model service, it looks up the value for the "wicket.model" key. This value might be something like " perspective.browse.model", " 935313c9-7fcb-42e8-9465-6080eb24693b" or " $#%345.*". What matters here is that the plugin that provides the service registers it under the same name.
A plugin receives a plugin context in its constructor when it is instantiated. This context can be used to interact with the framework. The following things can be achieved in this way:
- Start a new cluster
New plugins can be started by providing a cluster configuration. The cluster is under the control of the plugin that started it. When the original plugin is stopped, for example, the cluster will be stopped as well.
- Retrieve/register/unregister a service
Any object that implements IClusterable can be registered as a service. It will receive a unique service ID that can be used to register "decorator" interfaces, construct a naming scope or to retrieve the service when the plugin context is not available.
- Register/unregister a service tracker
It is possible to be notified when a service is registered under a particular name by registering a service tracker. This is useful when services may come and go in an unpredictable manner and should be used whenever references to services are stored outside of the stack.
The plugin context is private to the plugin. I.e. a plugin can use it, but should not hand out references to other plugins. The context handles the lifecycle of services and trackers. When a plugin is stopped these are unregistered so there is no need for plugins to do this.
Because this is a fairly abstract model, it is recommended to use base classes that handle the interaction with the framework.
It is sometimes desirable to dynamically instantiate plugins to extend the application in response to a user action. For example, the workflow plugin starts and stops clusters of plugins that show lists of available actions for a document.
Because the starting and stopping of a consumed service is under the control of a different plugin, in general no assumptions can be made about its availability. There are three safe ways of consuming services:
- Use IPluginContext.getService(name, clazz) to get the service whenever it is needed. No references should be stored in objects.
- Register a service tracker if storing references is necessary. When the tracker is notified of a service's disappearance, the reference should be discarded.
- Store an IServiceReference and use it to get to the service whenever it is needed. This is necessary in dialogs, where the plugin context is not directly available. You can store an IServiceReference to the plugin if it is registered as a service itself, as in the case of RenderPlugin subclasses.