26.8. Extension points defined by BASE

26.8.1. Menu: extensions
26.8.2. Toolbars
26.8.3. Edit dialogs
26.8.4. Bioassay set: Tools
26.8.5. Bioassay set: Overview plots
26.8.6. Services
26.8.7. Connection managers
26.8.8. Fileset validators
26.8.9. Logging managers
The LogManagerFactory interface
The LogManager interface
The EntityLogger interface
26.8.10. Item overview loaders
26.8.11. Item overview validation
26.8.12. Item overview information
26.8.13. Table list columns

In this section, we will give an overview of the extension points defined by BASE. Most extension points are used in the web client to add buttons and menu items, but there are a few for the core API as well.

26.8.1. Menu: extensions

Menu items can be added to the top-level Extensions menu. Actions should implement the interface: MenuItemAction

The MenuItemAction.getMenuType() provides support for MENUITEM, SUBMENU and SEPARATOR menus. Which of the other properties that are needed depend on the menu type. Read the javadoc for more information. This extension point doesn't support custom javascripts or stylesheets and the rendering is internal (eg. extensions can't provide their own renderers).

BASE ships with two action factories: FixedMenuItemFactory and PermissionMenuItemFactory. The fixed factory provides a menu that is the same for all users. The permission factory can disable or hide a menu depending on the logged in user's role-based permissions. The title, icon, etc. can have different values depending on if the menu item is disabled or enabled.

26.8.2. Toolbars

Most toolbars on all list and single-item view pages can be extended with extra buttons. Actions should implement the interface: ButtonAction. Button actions are very simple and only need to provide things like a title, tooltip, on-click script, etc. This extension point has support for custom javascript, stylesheets and renderers. The default renderer is ToolbarButtonRendererFactory.

BASE ships with two action factories: FixedButtonFactory and PermissionButtonFactory. The fixed factory provides a toolbar button that is the same for all users. The permission factory can disable or hide a buton depending on the logged in user's role-based permissions. The title, icon, etc. can have different values depending on if the menu item is disabled or enabled.

26.8.3. Edit dialogs

Most item edit dialogs can be extended with additional tabs. Actions should implement the interface: TabAction. The actions are, in principle, simple and only need to provide a title and content (HTML). The action may also provide javascripts for validation, etc. This extension point has support for custom stylesheets and javascript. Rendering is fixed and can't be overridden.

BASE ships with two action factories: FixedTabFactory and IncludeContentTabFactory. The fixed factory provides a tab with fixed content that is the same for all users and all items. This factory is not very useful in a real scenario. The other factory provides content by including the output from another resource, eg. a JSP page, a servlet, etc. The current context is stored in a request-scoped attribute under the key given by JspContext.ATTRIBUTE_KEY. A JSP or servlet should use this to hook into the current flow. Here is a code example:


// Get the JspContext that was created on the main edit page
final JspContext jspContext = (JspContext)request.getAttribute(JspContext.ATTRIBUTE_KEY);

// The current item is found in the context. NOTE! Can be null if a new item
final BasicItem item = (BasicItem)jspContext.getCurrentItem();

// Get the DbControl and SessionControl used to handle the request (do not close!)
final DbControl dc = jspContext.getDbControl();
final SessionControl sc = dc.getSessionControl();

The extra tab need to be paired with an extension that is invoked when the edit form is saved. Each edit-dialog extension point has a corresponding on-save extension point. Actions should implement the interface: OnSaveAction. This interface define three callback methods that BASE will call when saving an item. The OnSaveAction.onSave() method is called first, but not until all regular properties have been updated. If the transaction is committed the OnSaveAction.onCommit() method is also called, otherwise the OnSaveAction.onRollback() is called. The onSave() method can throw an exception that will be displayed to the user. The other callback method should not throw exceptions at all, since that may result in undefined behaviour and can be confusing for the user.

26.8.4. Bioassay set: Tools

The bioassay set listing for an experiment has a Tools column which can be extended by extensions. This extension point is similar to the toolbar extension points and actions should implement the interface: ButtonAction.

Note that the list can contain BioAssaySet, Transformation and ExtraValue items. The factory implementation need to be aware of this if it uses the JspContext.getItem() method to examine the current item.

26.8.5. Bioassay set: Overview plots

The bioassay set page has a tab Overview plots. The contents of this tab is supposed to be some kind of images that have been generated from the data in the current bioassay set. What kind of plots that can be generated typically depends on the kind of data you have. BASE ships with an extension (MAPlotFactory) that creates MA plots and Correction factor plots for 2-channel bioassays. Actions should implement the interface: OverviewPlotAction. A single action generates a sub-tab in the Overview plots tab. The sub-tab may contain one or more images. Each image is defined by a PlotGenerator which sets the size of the image and provides an URL to a servlet that generates the actual image. It is recommended that the servlet cache images since the data in a bioassay set never changes. The BASE core API provides a system-managed file cache that is suitable for this. Call Application.getStaticCache() to get a StaticCache instance. See the source code for the core PlotServlet for details of how to use the cache.

26.8.6. Services

A service is a piece of code that is loaded when the BASE web server starts up. The service is then running as long as the BASE web server is running. It is possible to manually stop and start services. This extension point is different from most others in that it doesn't affects the visible interface. Since services are loaded at startup time, this also means that the context passed to ActionFactory methods will not have any ClientContext associated with it (eg. the InvokationContext.getClientContext() always return null). There is also no meaning for extensions to specify a RendererFactory. Service actions should implement the interface: ServiceControllerAction. The interface provides start() and stop() methods for controlling the service. BASE doesn't ship with any service, but there is an FTP service available at the BASE plug-ins site: http://baseplugins.thep.lu.se/wiki/net.sf.basedb.ftp

26.8.7. Connection managers

This extension point adds support for using external files in BASE. This is a core extension point and is available independently of the web client. Actions should implement the interface: ConnectionManagerFactory.

The getDisplayName() and getDescription() methods are used in the gui when a user manually selects which connection manager to use. The supports(URI) is used when auto-selecting a connection manager based on the URI of the resource.

BASE ships with factory that supports HTTP and HTTPS file references: HttpConnectionManagerFactory. The BASE plug-ins site has an connection manager that support the Hadoop distributed file system (HDFS): http://baseplugins.thep.lu.se/wiki/net.sf.basedb.hdfs

26.8.8. Fileset validators

In those cases where files are used to store data instead of importing it to the database, BASE can use extensions to check that the supplied files are valid and also to extract metadata from the files. For example, the CelValidationAction is used to check if a file is a valid Affymetrix CEL file and to extract data headers and the number of spots from it.

Validation and metadata extraction actions should implement the ValidationAction interface. This is a core extension point and is available independently of the web client.

This extension point is a bit more complex than most other extension points. To begin with, the factory class will be called with the owner of the file set as the current item. Eg. the ClientContext.getCurrentItem() should return a FileStoreEnabled item. It is recommended that the factory performs a pre-filtering of the items to avoid calling the actual validation code on unsupported files. For example, the CelValidationFactory will check that the item is a RawBioAssay item using the Affymetrix platform.

Each file in the file set is then passed to the ValidationAction.acceptFile(FileSetMember) which may accept or reject the file. If the file is accepted it may be accepted for immediate validation or later validation. The latter option is useful in more complex scenarios were files need to be validated as a group. If the file is accepted the ValidationAction.validateAndExtractMetadata() is called, which is were the real work should happen.

The extensions for this extension point is also called when a file is replaced or removed from the file set. The calling sequence to set up the validation is more or less the same as described above, but the last step is to call ValidationAction.resetMetadata() instead of ValidationAction.validateAndExtractMetadata().

[Tip] Use the SingleFileValidationAction class.

Most validators that work on a single file at a time may find the SingleFileValidationAction class useful. Is should simplify the task of making sure that only the desired file type is validated. See the source code of the CelValidationAction class for an example.

26.8.9. Logging managers

This extension point makes it possible to detect changes that are made to item and generate log entries in real time. The core will send notifications for all item creations, updates and deletions to all registered logging managers. It is up to each implementation to decide if an event should be logged, where to log it and how much information that should be stored. The BASE core provides a logging implementation that save some information to the database which can also be viewed through the web interface.

The logging mechanism works on the data layer level and hooks into callbacks provided by Hibernate. EntityLogger:s are used to extract relevant information from Hibernate and create log entries. While it is possible to have a generic logger it is usually better to have different implementations depending on the type of entity that was changed. For example, a change in a child item should, for usability reasons, be logged as a change in the parent item. Entity loggers are created by a LogManagerFactory. All changes made in a single transaction are usually collected by a LogManager which is also created by the factory.

The LogManagerFactory interface

Each registered action factory shoulds create a LogManagerFactory that is used throughout a single transaction. If the factory is thread-safe, the same instance can be re-used for multiple requests at the same time. Here is a list of the methods the factory must implement:

public LogManager getLogManager(LogControl logControl);

Creates a log manager for a single transaction. Since a transaction is not thread-safe the log manager implementation doesn't have to be either. The factory has the possibility to create new log managers for each transaction.

public boolean isLoggable(Object entity);

Checks if changes to the given entity should be logged or not. For performance reasons, it usually makes sense to not log everything. For example, the database logger implementation only logs changes if the entity implements the LoggableData interface. The return value of this method should be consistent with getEntityLogger().

public EntityLogger getEntityLogger(LogManager logManager,
                                    Object entity);

Create or get an entity logger that knows how to log changes to the given entity. If the entity should not be logged, null can be returned. This method is called for each modified item in the transaction.

The LogManager interface

A new log manager is created for each transaction. The log manager is responsible for collecting all changes made in the transaction and store those changes in the appropriate place. The interface doesn't define any methods for this collection, since each implementation may have very different needs.

public LogControl getLogControl();

Get the log control object that was supplied by the BASE core when the transaction was started. The log controller contains methods for accessing information about the transaction, such as the logged in user, executing plug-in, etc. It can also be used to execute queries against the database to get even more information.

[Warning] Warning

Be careful about the queries that are executed by the log controller. Since all logging code is executed at flush time in callbacks from Hibernate we are not allowed to use the regular session. Instead, all queries are sent through the stateless session. The stateless session has no caching functionality which means that Hibernate will use extra queries to load associations. Our recommendation is to avoid quires that return full entities, use scalar queries instead to just load the values that are needed.

public void afterCommit(); , public void afterRollback();
Called after a successful commit or after a rollback. Note that the connection to the database has been closed at this time and it is not possible to save any more information to it at this time.

The EntityLogger interface

An entity logger is responsible for extracting the changes made to an entity and converting it to something that is useful as a log entry. In most cases, this is not very complicated, but in some cases, a change in one entity should actually be logged as a change in a different entity. For example, changes to annotations are handled by the AnnotationLogger which which log it as a change on the parent item.

public void logChanges(LogManager logManager,
                       EntityDetails details);

This method is called whenever a change has been detected in an entity. The details variable contains information about the entity and, to a certain degree, what changes that has been made.

26.8.10. Item overview loaders

The item overview functionality allow extensions to load more items in the tree structure. The extension point is a core extension point that is available independently of the web client. Actions should implement the ChildNodeLoaderAction interface. We recommend that the actions also extend the BasicItemNodeLoader since this will make it easier to handle situations with missing items, permission problems and validation. Since each registered extension is checked for each item in the overview we recommend that the action factory makes a first filtering step before an action is created. This should be relatively simple since the current item in the ClientContext is the parent node. For example:


// From the ActionFactory interface
public boolean prepareContext(InvokationContext<? super ChildNodeLoaderAction> context) 
{
   Node parentNode = (Node)context.getClientContext().getCurrentItem();
   if (parentNode != null)
   {
      if (parentNode.getItem() instanceof Ownable)
      {
         return true;
      }
   }
   return false;
}

26.8.11. Item overview validation

The item overview functionality allow extensions to add validation code to any item in the tree. There are two co-existing extension points, one for the actual validation code and one for the validation rule defintions. Both extension points are core extension points that is available independently of the web client. Validation actions should implement the NodeValidatorAction interface, which is simple the same as NodeValidator We recommend that the actions extend the NameableNodeValidator or BasicNodeValidator since this will make it easier to handle situations with missing items and permission problems. Since each registered extension is checked for each item in the overview we recommend that the action factory makes a first filtering step before an action is created. This should be relatively simple since the current item in the ClientContext is type of that should be validated. For example:


// From the ActionFactory interface
public boolean prepareContext(InvokationContext<? super NodeValidatorAction> context) 
{
   return context.getClientContext().getCurrentItem() == Item.USER;
}

Rule definition actions should implement the ValidationRuleAction. This is simply a container with some information about the validation rule, such as a title, description and a default severity level. The Validator class that ships with BASE already implements this interface. The ReflectValidationRuleActionFactory can be used to return an existing public static final rule definition as an action:


<extension
   id="validationrule.invalid-url"
   extends="net.sf.basedb.util.overview.validationrule">
   <index>4</index>
   <about>
      <name>Invalid URL</name>
      <description>Checks if an URL has a valid syntax.</description>
   </about>
   <action-factory>
      <factory-class>
         net.sf.basedb.util.overview.extensions.ReflectValidationRuleActionFactory
      </factory-class>
      <parameters>
         <field>net.sf.basedb.examples.extensions.overview.OwnerValidator.INVALID_URL</field>
      </parameters>
   </action-factory>
</extension>

26.8.12. Item overview information

The item overview functionality allow extensions to display additional information about the currently selected node in the right pane in the web interface. Each SectionAction object that is created by extensions will result in an additional section in the GUI. Note that all extension factories are called for all nodes. The ActionFactory.prepareContext() should be used to enable/disable the extension depending on the node that is selected. BASE ships with the IncludeContentSectionFactory factory implementation which allows a resourse (eg. another JSP script) to generate the content of the section. The current context is stored in a request-scoped attribute under the key given by JspContext.ATTRIBUTE_KEY. A JSP or servlet should use this to hook into the current flow. Here is a code example:


// Get the JspContext that was created on the main edit page
final JspContext jspContext = (JspContext)request.getAttribute(JspContext.ATTRIBUTE_KEY);

// The currently selected node is found in the context.
final Node node = (Node)jspContext.getCurrentItem();

// Get the DbControl and SessionControl used to handle the request (do not close!)
final DbControl dc = jspContext.getDbControl();
final SessionControl sc = dc.getSessionControl();

26.8.13. Table list columns

This extension point makes it possible to add more columns to most major list pages in the web interface. Actions should implement the ListColumnAction interface. This interface contains several methods which can be grouped into three main types:

  • Metadata methods, for example, the id and title of the column and if the column can be sorted, filtered, etc.

  • Rendering information, for example, CSS class and style information that is used for the column header.

  • Worker methods, that retrieve the value from the current item and format it for proper display. Different methods are used for the web display and for exporting to a file.

The AbstractListColumnBean class provides a bean-like implementation for all methods except the getValue() method. Extending from this class makes it easy to your own implementations. BASE ships with the PropertyPathActionFactory factory that can be used without coding if the added column can be expressed as a property path that can be handled by the Metadata class. As usual, see the example code for some examples.