On this page:

5.13Interceptors

 

The HAPI FHIR Server Interceptor Framework is a mechanism for modifying the behavior of the server in a variety of ways.

As described in the documentation, special classes called Interceptors can be used to:

  • Pre-process or mask data that is being returned to the client as a result of a FHIR search, read, etc.
  • Add additional business checks before processing a write operation
  • Modify data that was submitted by a client prior to saving it

5.13.1Coding and Deploying Interceptors

 

HAPI FHIR interceptors should be packaged as a simple JAR file containing only the classes defined by the interceptor (i.e. library dependencies such as Spring or Commons-Lang should not be included in your JAR).

The interceptor class (or classes) that you have defined should then be set on the interceptor_bean_types property within the FHIR Storage module configuration.

For example, the following property sets a custom interceptor from within the configuration property file:

module.persistence.config.interceptor_bean_types=com.example.demo.ExampleAttributeEnhancingInterceptor

Interceptor classes have access to any of the standard libraries available within Smile CDR. This includes Spring Framework, Apache HTTPClient, Gson, Jackson, Woodstox, and others.

5.13.2Starter Project

 

The cdr-persistence-interceptordemoproject contains a complete example Maven-based Java project to define an interceptor that can be imported into Smile CDR. It is available for download at the following links:

5.13.3Example: Attribute Enhancement

 

The following example shows an interceptor that can be used to enhance resources that are submitted for update by a client. In this example, for every time that a Patient resource is created or updated, we are calling a REST EMPI service to fetch an EUID (an enterprise identifier for a Patient). We are then adding this EUID as an additional identifier on the list of patient identfiers.

/**
 * This example interceptor fetches a set of attributes from a third-party
 * REST service and uses them to enhance the resource being submitted.
 */
@SuppressWarnings("unchecked")
public class ExampleAttributeEnhancingInterceptor extends ServerOperationInterceptorAdapter {

	private static final Logger ourLog = LoggerFactory.getLogger(ExampleAttributeEnhancingInterceptor.class);

	/**
	 * We are going to treat creates and updates the same way in this example, so the
	 * create method redirects to the update method.
	 */
	@Override
	public void resourcePreCreate(RequestDetails theRequest, IBaseResource theResource) {
		resourcePreUpdate(theRequest, null, theResource);
	}

	/**
	 * For this example we are overriding the <code>resourcePreUpdate</code>
	 * method, which will be called when a resource is being updated. There
	 * are many other methods that can be overridden, but this one is
	 * appropriate for the desired functionality.
	 *
	 * @param theRequest     Contains details about the incoming request. You may wish to cast this
	 *                       object to a {@link ca.uhn.fhir.rest.server.servlet.ServletRequestDetails},
	 *                       which is always possible when running interceptors inside Smile CDR.
	 * @param theOldResource The previous version of the resource, before this update was
	 *                       requested by the client.
	 * @param theNewResource The new version of the resource as submitted by the client. This
	 *                       object can be modified.
	 */
	@Override
	public void resourcePreUpdate(RequestDetails theRequest, IBaseResource theOldResource, IBaseResource theNewResource) {

		// This interceptor only cares about Patient resources
		if (!(theNewResource instanceof Patient)) {
			return;
		}

		Patient newResource = (Patient) theNewResource;

		/*
		 * We want to know the primary identifier, which is the first instance of
		 * Patient.identifier where identifer.system = "http://example.com/mrn". If the
		 * client has submitted a resourece that does not have such an identifier, this
		 * is an error and we will throw a PreconditionFailedException (HTTP 412)
		 */
		String primaryIdentifier = newResource
			.getIdentifier()
			.stream()
			.filter(t -> t.getSystem().equals("http://example.com/mrn"))
			.map(t -> t.getValue())
			.findFirst()
			.orElseThrow(() -> new PreconditionFailedException("No MRN supplied with request"));

		// We're going to make a web service call to a simple web service that
		// returns a JSON response. There are lots of ways of doing this, but
		// in our example we'll make a call using the Spring RestTemplate
		RestTemplate restTempate = new RestTemplate();
		restTempate.setMessageConverters(Collections.singletonList(new GsonHttpMessageConverter()));
		HashMap<String, Object> response = restTempate.getForObject("http://localhost:9999/empiquery?mrn=" + primaryIdentifier, HashMap.class);

		// We'll log the response. Generally this isn't a good idea in a real
		// production system but it's handy for debugging at first.
		ourLog.info("Response: {}", response);

		// Grab the existing EUID (identifier with a system of "http://example.com/euid")
		// and create one if it doesn't exist
		Identifier euidIdentifier = newResource
			.getIdentifier()
			.stream()
			.filter(t -> t.getSystem().equals("http://example.com/euid"))
			.findFirst()
			.orElseGet(() -> newResource.addIdentifier());

		// Set the identifier value using the web service response
		euidIdentifier.setSystem("http://example.com/euid");
		euidIdentifier.setValue((String) response.get("euid"));
	}
}