Page tree
Skip to end of metadata
Go to start of metadata

The configuration of XNAT plugins is managed through an XNAT plugin class. An XNAT plugin class is written in Java and distinguished mainly by being annotated with @XnatPlugin. When XNAT starts up, it looks on its classpath, finds all of the installed plugin classes, and processes each one.1 A plugin class serves a number of functions:


  • It provides a unique identifier for the plugin that can be used to distinguish classes, properties, and other resources associated with the plugin from those provided by other plugins
  • It can indicate which packages in the plugin should be scanned for data entities. These are classes that provide a way to persist data in the database without having to write code that performs database operations directly or any SQL. You can read a basic tutorial on creating data entities, as well as how to use the NRG Entity Service Framework on which most of XNAT's data entity services are based.
  • It can identify XNAT data models and how they should be configured. XNAT data models differ from standard data entities: data models define the primary domain objects for XNAT, specifically medical imaging sessions and associated data, research projects and the subjects participating in a project, clinical instruments for evaluating subjects, and so on.
  • The @XnatPlugin annotation itself includes the Spring Framework's @Configuration annotation. This gives you the ability to configure and initialize services, create event listeners and handlers, retrieve properties from configuration files, launch REST API controllers, and so on. It's really the launch point for a complete web application development framework and allows you to fully integrate your plugin into the XNAT platform and server architecture.

Creating an @XnatPlugin Class

Creating an @XnatPlugin class starts with creating the class itself. The code below shows a basic class declaration:

Basic Java class declaration
package edu.myschool.xnat.foo.plugin;
 
public class MyXnatPlugin {
} 

This class declaration is so basic that it doesn't actually do anything. That's OK: an @XnatPlugin class can actually do a lot with very little. The next step is to make this an @XnatPlugin class by adding the annotation:

@XnatPlugin-annotation Java class
package edu.myschool.xnat.foo.plugin;
 
import org.nrg.framework.annotations.XnatPlugin;

@XnatPlugin(value = "myXnatPlugin", name = "My XNAT Plugin")
public class MyXnatPlugin {
} 

At this point, you now have an XNAT plugin class. It still doesn't do much yet, but it does a lot more than it did before that annotation was added. By virtue of having that annotation, once you build your plugin XNAT can discover this class and will load it at start-up. The plugin will also appear in the list of loaded plugins in XNAT.

Configuring XNAT Data Models

XNAT data models represent the primary domain objects in XNAT. The core set of XNAT data models includes:

  • DICOM imaging sessions with modalities including MR, PET, CT, and many many more
  • Image assessors that provide context and analysis of your imaging sessions, such as automated or manual QCs, protocol validation, FreeSurfers, and more
  • Subjects and projects that represent particular research efforts and their associated study cohorts
  • Subject assessors that evaluate and analyze subject attributes, which can include simple demographics, complex family, economic, and social histories, drug and dietary histories, mental, psychological, and neurological assessment instruments, and many more

These data models represent the instruments and experiments that compose the heart of medical imaging research and are the primary deliverable of the XNAT system.

A data model itself is defined in a type of XML called an XML schema definition or XSD. This is an XML file that defines how other XML files should be composed, which includes defining the elements, attributes on those elements, which elements are valid within parent elements, and so on. In the XNAT context, these XSDs are known as data-type schemas.

Writing an XNAT data-type schema is an entire topic unto itself. See XNAT Data Type Development for documentation on creating one.

Presuming that you have one or more data-type schemas for your plugin, you can add them to the plugin and configure them easily.

First, the schemas must be placed in the appropriate location in your project. With the Gradle build structure, the location is:

src/main/resources/schemas/namespace/schema.xsd

In most cases, namespace and schema are actually the same. For example, the main XNAT data-type schema that includes its data types for MR and PET sessions, subjects, projects, and more, is in src/main/resources/schemas/xnat/xnat.xsd.

Second, you need to declare each of your data models with the @XnatPlugin's dataModels attribute. This takes one or more @XnatDataModel annotations as a value (if more than one @XnatDataModel annotations is specified, you must surround them with the '{' and '}' delimiters and separate each with a comma).

Let's suppose that your plugin has two data models, MyImageAssessor and MySubjectAssessor. Adding these to the @XnatPlugin annotation above would result in the following:

@XnatPlugin-annotation Java class
package edu.myschool.xnat.foo.plugin;
 
import org.nrg.framework.annotations.XnatDataModel;
import org.nrg.framework.annotations.XnatPlugin;

@XnatPlugin(value = "myXnatPlugin", name = "My XNAT Plugin",
            dataModels = {@XnatDataModel(value = "myPlugin:myImageAssessor",
                                         singular = "My Image Assessor",
                                         plural = "My Image Assessors",
                                         code = "MIA"),
                          @XnatDataModel(value = "myPlugin:mySubjectAssessor",
                                         singular = "My Subject Assessor",
                                         plural = "My Subject Assessors",
                                         code = "MSA")})
public class MyXnatPlugin {
} 

In combination with the processing XNAT performs on start-up with data-type schemas that it discovers, these data-model configurations will make your new data types available for use on your XNAT system.

Adding Data Entity Packages

Data entity classes provide a convenient way to store data in the database without having to write a bunch of database transactions, SQL, and other verbose and difficult-to-manage code. These are easier to write and manage than XNAT data objects, which makes them great for implementing infrastructure services in XNAT, but don't provide the deep integration into XNAT's core data model and related services, so they're not appropriate for creating data types.

The first thing you'll notice about a data entity class is that it will be annotated with the @Entity annotation:

package edu.myschool.xnat.foo.entities;

import javax.persistence.Entity;

@Entity
public class MyEntity {
}

However, this annotation isn't enough on its own for this class to be picked up by XNAT's transaction manager. The transaction manager has to know where to look for entity classes. Our particular implementation looks within a limited list of Java packages. You can add your package that contains data entity packages to that list just by adding the package to the entityPackages attribute on your @XnatPlugin annotation:

@XnatPlugin-annotation Java class
package edu.myschool.xnat.foo.plugin;
 
import org.nrg.framework.annotations.XnatPlugin;

@XnatPlugin(value = "myXnatPlugin", name = "My XNAT Plugin",
            entityPackages = "edu.myschool.xnat.foo.entities")
public class MyXnatPlugin {
} 

Note that this isn't enough to start using your entity classes: you'll also need a corresponding service and repository object for your entity. For more on this, refer to Data Persistence with the NRG Entity Service Framework. You'll also need to tell XNAT where to find your service and repository objects, as described in the next section.

Configuring and Initializing System Services

As noted earlier, @XnatPlugin includes the Spring @Configuration annotation, which means that you can easily create and inject components into XNAT's main application context. There are two ways to do this:

  • The easiest way is by specifying packages that contain components in the  @ComponentScan annotation. Any component classes in that package or any of its subpackages will be created automatically. Component classes are defined by being annotated by a Spring @Component-based annotation, which includes but isn't limited to:
    • @Component
    • @Repository
    • @Service
    • @Controller
    • @RestController
    • @XapiRestController
    • @XnatPlugin
  • You can also create your component or service explicitly with a method within your plugin class annotated with the @Bean annotation. This gives you a greater level of control over how your object is created, since you're writing the code for it directly.

The code below demonstrates both methods of creating an object.

@XnatPlugin-annotation Java class
package edu.myschool.xnat.foo.plugin;

import edu.myschool.xnat.foo.repositories.services.MyEntityService;
import org.nrg.framework.annotations.XnatPlugin;
import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.ComponentScan;

@XnatPlugin(value = "myXnatPlugin", name = "My XNAT Plugin",
            entityPackages = "edu.myschool.xnat.foo.entities")
@ComponentScan("edu.myschool.xnat.foo.repositories")
public class MyXnatPlugin {
    @Bean
    public MyEntityService myEntityService() {
        final MyEntityService service = new MyEntityService();
        // You could do some configuration here.
        return service;
    }
}

This demonstrates a fairly common pattern, where entity classes are integrated into your plugin through the entityPackages attributes on the @XnatPlugin annotation, repositories are integrated through the @ComponentScan annotation (repositories are usually fairly simple and require no configuration), and services are constructed and injected directly (services are often integrated through component scans as well, but require configuration more often than repositories on average).

Footnotes

 

Footnotes
Ref Notes
1 This isn't completely true. XNAT actually finds property files associated with the plugins. These property files are generated at compile time by the XnatPluginAnnotationProcessor class. In practice, though, you use the annotation to define a number of attributes about your plugin and those values help XNAT load and integrate your plugin into the XNAT runtime environment.