Skip to content
Eduardo Pinho edited this page Jan 19, 2017 · 15 revisions

Developing Dicoogle Plugins

In order to integrate additional features over Dicoogle, you may create your own PluginSet. A PluginSet is a composition of plugins developed with the intent of supporting a given functionality. There are 5 particular types of plugins:

  • Storage plugins are responsible for storing and retrieving data. A basic implementation would keep files in the local file system, but Dicoogle can be extended to support remote storage with plugins of this type.
  • Indexer plugins implement index generation. A fully deployed instance of Dicoogle should have at least one DICOM indexer.
  • Query plugins provide a means of querying the indexed data. Often a query provider is coupled with a particular indexer, and are bundled together in the plugin set.
  • Jetty Service plugins support the attachment of Eclipse jetty servlets, so as to host new web services in Dicoogle.
  • Web Service plugins contain a Restlet server resource that can be attached to Dicoogle, also for hosting web services.
  • Web User Interface Plugins, unlike other kinds of plugins, are developed in JavaScript and provide new UI components that are automatically loaded into Dicoogle's web application. For further details, see the Dicoogle Web Core documentation.

Graphical plugins, which were based on Java Swing components, are currently obsolete. For extending the user interface, please develop web plugins instead.

Frequently Asked Questions

Here is a list of tasks frequently performed when developing plugins for Dicoogle.

How do plugins access the platform?

Interactions with the core platform are made via the PlatformInterface. This is the top-level API of Dicoogle that is exposed to other plugins.

In order to obtain this platform interface, plugins (or the plugin set) need to implement the interface PlatformCommunicatorInterface. The method setPlatformProxy declared therein behaves like a callback, which will be called by the platform shortly after the plugin is loaded. Usually, plugins can simply pass the argument into an attribute for future use:

public class MyQueryPlugin 
        implements QueryInterface, PlatformCommunicatorInterface
    private DicooglePlatformInterface platform;

    // ... other content

    void setPlatformProxy(DicooglePlatformInterface platform) {
        this.platform = platform;
    }

How do I query for DICOM meta-data?

There should be at least one DIM content provider in a deployed instance of Dicoogle. Let us assume that the plugin is named "lucene". First retrieve the appropriate QueryInterface, then call the query method with the intended query. For DICOM meta-data providers, the query should follow the Apache Lucene query language.

QueryInterface provider = this.platform.getQueryPlugin("lucene");
Iterable<SearchResult> results = provider.query("Modality:CT AND AXIAL");
for (SearchResult res: results) {
   // use results
}

The outcome is a sequence of search results, which is possibly lazy. You should not traverse the outcome more than once. In order to manipulate the list further, please save the results into a list such as ArrayList.

At the moment, plugins that rely on DICOM content are recommended to support a configurable DIM query source, rather than hard-coding "lucene" as the provider.

How do I access files in storage?

Dicoogle provides an abstraction for accessing files from any kind of data source. Instead of using standard Java I/O APIs, plugins should retrieve the appropriate storage interface (StorageInterface class). Once with the intended storage, the method at can be used to obtain a sequence of all files at the given location.

URI uri = ...;
StorageInterface store = this.platform.getStorageForSchema(uri);
Iterable<StorageInputStream> files = store.at(uri);

StorageInputStream is an abstraction for files in a storage (like a blob of data, not necessarily in the file system), from which a raw input stream can be retrieved.

What if I want to retrieve a file by SOPInstanceUID?

First query the DIM provider for the file with that UID, then retrieve the URI from the search result:

QueryInterface dimProvider = this.platform.getQueryProviderByName("lucene", true);
Iterator<SearchResult>> results = dimProvider.("SOPInstanceUID:1.2.3.4").iterator();
if (results.hasNext()) {
    SearchResult res = results.next();
    URI uri = res.getURI();
    Iterable<StorageInputStream> files = this.platform.getStorageForSchema(uri).at(uri);
    // use files
} else {
    // no such file
}

How do I read and write settings?

All plugins and plugin sets implement the method setSettings, which is also similar to a callback. The platform will call this method with a configuration holder after instantiation.

A typical implementation of this method should save the configuration holder to an attribute and check that the settings are ok. Fetching the actual settings currently yields an Apache Commons 1.x XmlConfiguration object (a user guide can be read here). The method may also write missing fields with default values.

@Override
public void setSettings(ConfigurationHolder configurationHolder) {
    this.settings = configurationHolder;

    XmlConfiguration configuration = this.settings.getConfiguration();

    try {
        // required field, will throw if missing
        String uid = configuration.getString("service-uid");
    } catch (RuntimeException ex) {
        logger.warn("Failed to configure plugin: required fields are missing!", ex);
    }

    // optional field, default is 1
    int numResources = configuration.getInt("num-resources", 1);
    configuration.setProperty("num-resources", numResources); // write field

    try {
        configuration.save();
    } catch (ConfigurationException ex) {
        logger.warn("Failed to save configurations!", ex);
    }
    this.uid = uid;
    this.numResources = numResources;
}

Also note that, in the latest version of Dicoogle, the plugin will be disabled if this method throws an unchecked exception.

Clone this wiki locally