Customizing DICOM to XNAT metadata translation

When a DICOM study is imported as an XNAT session, some fields from the DICOM metadata are translated into the XNAT metadata format. To customize this DICOM to XNAT metadata translation, the Java code underlying the translation must be modified, and the Java library that performs this translation (dicom-xnat-mx) must be replaced by a customized version. This document provides a brief overview of this process. Note that this is an advanced topic and you will most likely have questions along the way; the XNAT discussion group is the appropriate forum.

The WU-UMinn Human Connectome Project uses a heavily customized version of dicom-xnat-mx. This is the best existing example of customized DICOM to XNAT metadata translation, and we will refer to it repeatedly in these instructions.

We assume that you have already installed (and possibly modified) a current version of XNAT, and that the XNAT webapp builder directory (commonly called XNAT_HOME) is /opt/xnat/builder. If your path to the XNAT source is different, you will need to modify the paths given below.

Step 1: Download the dicom-xnat-mx source code

The simplest way to download the metadata translation code is to clone the XNAT dicom-xnat repository:

cd /opt/xnat
hg clone https://bitbucket.org/nrg/dicom-xnat dicom-xnat-custom

Alternatively, if you expect to use Bitbucket to manage your changes, you should fork dicom-xnat to a new repository in your own account and then clone your forked repository.

Step 2: Modify Maven project files to prepare for customizations

The HCP-customized dicom-xnat is a good template for this somewhat complex step.

Modify the top-level POM (dicom-xnat/pom.xml) to set a custom version label for your dicom-xnat-mx JAR, to avoid confusing it with the stock XNAT version:

<artifactId>dicom-xnat</artifactId>
<version>1.6.3-mycustom</version>     <!-- added mycustom suffix to mark this as customized -->
<packaging>pom</packaging>

Unless you expect to make changes to the other subprojects of dicom-xnat (you probably don't), it's best to comment them out of the top-level POM; search for the <modules> element and modify it:

 <modules>
<!-- Don't build dicom-xnat-sop or dicom-xnat-util; the XNAT stock versions are good enough
    <module>dicom-xnat-sop</module>
    <module>dicom-xnat-util</module>
-->
    <module>dicom-xnat-mx</module>
  </modules>

Modify the dicom-xnat-mx subproject POM (dicom-xnat/dicom-xnat-mx/pom.xml) to use the same customized version label as the top-level POM:

    <parent>
        <artifactId>dicom-xnat</artifactId>
        <groupId>org.nrg</groupId>
        <version>1.6.3-mycustom</version>
    </parent>

Modify the dicom-xnat-mx subproject POM to use the xdat-beans JAR from your XNAT:

    <dependency>
      <groupId>org.nrg</groupId>
      <artifactId>xdat-beans</artifactId>
      <version>${xnat.version}</version>
      <scope>system</scope>
      <systemPath>${xnat.home}/plugin-resources/repository/xdat/jars/xdat-beans-${xnat.version}.jar</systemPath>
    </dependency>

This step (using xdat-beans from your XNAT) is only strictly necessary if you'll be extending the XNAT schema and defining metadata translation for those new fields, but it's a sensible thing to do even if you don't plan such extensive customization.

Note that we're using two as-yet-undefined attributes: ${xnat.version} is the version of your XNAT install, and ${xnat.home} is the pathname of your XNAT webapp builder (/opt/xnat/builder in our example). We'll define these in a Maven settings file, dicom-xnat/settings.xml

<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">
  <profiles>
    <profile>
      <id>mycustom</id>
      <properties>
        <xnat.home>/opt/xnat/builder</xnat.home>
        <xnat.version>1.6.3</xnat.version>
      </properties>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>mycustom</activeProfile>
  </activeProfiles>
</settings>

With those changes made, you should be able to build the dicom-xnat-mx JAR:

cd /opt/xnat/dicom-xnat-custom
mvn -s settings.xml -Dmaven.test.skip clean package 

(We're skipping the unit tests because they require a particular DICOM study arranged in a specific way, and I don't have a good description of that requirement yet. Send me mail if you want to set up your system to run the tests.)

If everything has been set up correctly, you should get a few pages full of Maven downloading the entire internet and otherwise doing its thing, and then:

...
[INFO] Reactor Summary:
[INFO]
[INFO] XNAT DICOM support ................................ SUCCESS [0.385s]
[INFO] DICOM metadata translation for XNAT ............... SUCCESS [24.133s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 24.644s
[INFO] Finished at: Tue Oct 01 14:14:46 CDT 2013
[INFO] Final Memory: 17M/168M
[INFO] ------------------------------------------------------------------------
bash-2.3$

Success! (No?...Send mail to the XNAT discussion group if you need help. There are a lot of ways for this to go wrong.)

Step 3: Convert XNAT to use your custom metadata translation

Now we replace the stock XNAT metadata translation with the one you've just built and make the necessary XNAT configuration changes. First, replace the jars:

cd /opt/xnat/builder/plugin-resources/repository/dcm/jars
hg rm dicom-xnat-mx-*.jar
cp /opt/xnat/dicom-xnat-custom/dicom-xnat-mx/target/dicom-xnat-mx-*.jar .
hg add dicom-xnat-mx-*.jar

Next, modify /opt/xnat/builder/plugin-resources/originals/project.xml to use your custom JAR:

        <dependency>
            <groupId>dcm</groupId>
            <artifactId>dicom-xnat-mx</artifactId>
            <version>1.6.3-mycustom</version>
            <properties>
                <war.bundle>true</war.bundle>
            </properties>
            <type>jar</type>
        </dependency>

It's a good idea to remove any traces of the old JAR from both the XNAT webapp builder and your Tomcat deployment (assumed here to be in /usr/share/tomcat6):

for h in /opt/xnat/builder/deployments /usr/share/tomcat6/webapps; do          # or wherever your Tomcat install is
  find $h -name 'dicom-xnat-mx*.jar' -print | xargs rm
done

Now you can update and redeploy:

cd /opt/xnat/builder
bin/update.sh -Ddeploy=true

Step 4: Modify your development environment for your custom metadata translation package

(Note: This step is Eclipse-oriented because that's what I use. If anyone would like to contribute a section written in IntelliJese or NetBeansian I'll be happy to include it.)

Now that XNAT is ready for you to customize the metadata translation, you'll need to prepare your development environment. First, install the m2eclipse plugin, if you don't already have it. Change your workspace Maven preferences (Preferences/Maven/User Settings) to use /opt/xnat/dicom-xnat-custom/settings.xml as your User Settings file. Import /opt/xnat/dicom-xnat-custom (and its subproject dicom-xnat-mx) as an existing Maven project.

If you also have the XNAT webapp builder open as a project in your Eclipse workspace, you'll need to modify the project classpath to use the custom version of dicom-xnat-mx. You can do this via the Eclipse project properties dialogs, but I usually just open /opt/xnat/builder/.classpath with a text editor, search for the jar name, and modify the file version by hand. If you're feeling really adventurous, you can replace the JAR reference with a reference to the dicom-xnat-mx project, but that's optional and may make it a little easier to get yourself into trouble by modifying the project but forgetting to update the JAR.

Each time you modify your custom dicom-xnat-mx, you should rebuild the jars (using the Maven build command at the end of in Step 2 above) and copy the resulting JARs from dicom-xnat-mx/target to plugin-resources/repository/dcm/jars.

Step 5: Customize the metadata translation

This can be very simple if you want to copy a DICOM attribute into a new addParam, or very complicated if you want to define new fields that require sophisticated conversion. I'll expand this section some other time, but until then, the best resource is the dicom-xnat-mx source code: dicom-xnat-mx/src/main/java/org/nrg/dcm/xnat/MRScanAttributes.java is a good place to start. The HCP custom metadata translation has many customizations and may be of particular interest if you get data from Siemens scanners, as it parses Siemens private attributes for MR, including the shadow headers.

$label.name