The Extensions API is divided into two parts. A core part and a web client specific part. The core part can be found in the net.sf.basedb.util.extensions package and it's sub-packages, and consists of two sub-parts:
An ExtensionsManager
Registry
A set of interface definitions which forms the core of the Extensions API.
The interfaces defines, for example, what an Extension
ActionFactory
Let's start by looking at the extensions manager and related classes.
The BASE application is using a single manager and a single registry (handled by the
Application
base.config
. Theoretically, a single manager can handle
multiple directories, but we do not use that feature. The BASE core also
include some special files that are added with the addURI()
method. They contain definitions for the core extensions and core plug-ins
and are shipped as XML files that reside inside the BASE core JAR files.
The ExtensionsManager.scanForChanges()
method is called to initiate a check for new, updated and deleted files. The
manager uses the XmlLoader
ExtensionsManager.getFiles()
method can be used to find more information about each individual file, for example,
if it is a new or modified file, if it contains valid extension definitions and
information about the author, etc. This information is used by the installation
wizard in the web client to display a dialog were the user can select which extensions
to intall. See Figure 21.2, “Extensions and plug-ins installation wizard”.
Note that no installation or other actions take place at this stage.
The ExtensionsManager.processFiles()
method is called to actually do
something. It needs an ExtensionsFileProcessor
ExtensionsManager.processFiles()
method need to be
called multiple times with different processor implementations to perform a full
installation of an extension or plug-in. Here is a list of the various processors
currently in use in BASE.
RegisterExtensionsProcessor
Is used to register
extensions with the registry. Can be paired with different filters
depending on when it is used. At BASE startup an InstalledFilter
UnregisterExtensionsProcessor
Is used to unregister
extensions when a file has been deleted. This should always be paired with
for example, a DeletedFilter
UnregisterExtensionsProcessor
Is used to unregister
extensions when a file has been deleted. This should always be paired with
for example, a DeletedFilter
ExtractResourcesProcessor
Is used to extract files from the JAR file to a local directory. This is currently used by the web client to extract JSP files, images, etc. that are needed by web client extensions.
DeleteResourcesProcessor
The opposite of
ExtractResourcesProcessor
.
PluginInstallationProcessor
Is used to install and register plug-ins.
DisablePluginsProcessor
Is used to disable plug-ins from extensions that have been removed.
MarkAsProcessedProcessor
This is usually the final
processor that is called and reset the timestamp on all processed files
so that the next time ExtensionsManger.scanForChanges()
is called it will know what has been modified.
![]() |
Note |
---|---|
This list contains the core processors only. The web client part is using some additional processors to perform, for example, servlet registration. |
The result of the processing can be collected with a ProcessResults
The Registry
An ExtensionPoint
Action
Extension
ActionFactory
Example 28.1. The menu extensions point
The net.sf.basedb.clients.web.menu.extensions
extension point
requires MenuItemAction
MenuItemAction
:s. BASE ships with default
factory implementations, for example the FixedMenuItemFactory
Call the Registry.useExtensions()
method
to use extensions from one or several extension points. This method will
find all extensions for the given extension points. If a filter is given,
it checks if any of the extensions or extension points has been disabled.
It will then call ActionFactory.prepareContext()
for all remaining extensions. This gives the action factory a chance to
also disable the extension, for example, if the logged in user doesn't
have a required permission. The action factory may also set attributes
on the context. The attributes can be anything that the extension point
may make use of. Check the documentation for the specific extension point
for information about which attributes it supports. If there are
any renderer factories, their RendererFactory.prepareContext()
is also called. They have the same possibility of setting attributes
on the context, but can't disable an extension.
After this, an ExtensionsInvoker
ActionFactory.getActions()
has not been
called yet, so we don't know if the extensions are actually
going to generate any actions. The ActionFactory.getActions()
is not called until we have got ourselves an
ActionIterator
ExtensionsInvoker.iterate()
method and
starts to iterate. The call to ActionIterator.hasNext()
will propagate down to ActionFactory.getActions()
and the generated actions are then available with the
ActionIterator.next()
method.
The ExtensionsInvoker.renderDefault()
and ExtensionsInvoker.render()
are
just convenience methods that will make it easer to render
the actions. The first method will of course only work if the
extension point is providing a renderer factory, that can
create the default renderer.
![]() |
Be aware of multi-threading issues |
---|---|
When you are creating extensions you must be aware that multiple threads may access the same objects at the same time. In particular, any action factory or renderer factory has to be thread-safe, since only one exists for each extension. Action and renderer objects should be thread-safe if the factories re-use the same objects. |
Any errors that happen during usage of an extension is handled by an
ErrorHandler
LoggingErrorHandlerFactory
RethrowErrorHandlerFactory
ErrorHandlerFactory
The web client specific parts of the Extensions API can be found
in the net.sf.basedb.client.web.extensions package
and it's subpackages. The top-level package contains the
ExtensionsControl
ExtensionsManager
In the top-level package there are also some abstract classes that may
be useful to extend for developers creating their own extensions.
For example, we recommend that all action factories extend the AbstractJspActionFactory
JspContext
ClientContext
The sub-packages to net.sf.basedb.client.web.extensions are mostly specific to a single extension point or to a specific type of extension point. The net.sf.basedb.client.web.extensions.menu package, for example, contains classes that are/can be used for extensions adding menu items to the menu. See Section 26.8, “Extension points defined by BASE” for more information about the extension points defined by BASE.
When the Tomcat web server is starting up, the ExtensionsServlet
Initialise the extensions system by calling
ExtensionsControl.init()
. This will result in
an initial scan for installed extensions. This means that
the extension system is up an running as soon as the first user log's
in to BASE. The processing scheme is slightly different from what is done
when the core is setting up the initial manager. The major additions are:
Act as a proxy for custom servlets defined by the extensions. URL:s
ending with .servlet
has been mapped to the
ExtensionsServlet
. When a request is made it
will extract the name of the extension's JAR file from the
URL, get the corresponding ServletWrapper
Using extensions only involves calling the
ExtensionsControl.createContext()
and
ExtensionsControl.useExtensions()
methods. This
returns an ExtensionsInvoker
To render the actions it is possible to either use the
ExtensionsInvoker.iterate()
method
and generate HTML from the information in each action. Or
(the better way) is to use a renderer together with the
Render
To get information about the installed extensions,
change settings, enabled/disable extensions, performing a manual
installation, etc. use the ExtensionsControl.get()
method. This will create a permission-controlled object. All
users has read permission, administrators has write permission.
![]() |
Note |
---|---|
The permission we check for is WRITE permission on the web client item. This means it is possible to give a user permissions to manage the extension system by assigning WRITE permission to the web client entry in the database. Do this from ► . |
The XJspCompiler
.xjsp
files
which are regular JSP files with a different extension. The difference is that
the XJSP compiler include the extension's JAR file on the class path, which
means that the JSP file can use classes that would otherwise be impossible.
This feature is experimental and requires installing an extra JAR into Tomcat's lib
directory. See Section 21.1.4, “Installing the X-JSP compiler” for
more information.