Smile CDR v2022.11.PRE
On this page:

18.0Hybrid Providers

 

Hybrid Providers allow you to use data where it resides in your organization as FHIR resources, without having to make a copy of the data in the FHIR database. Hybrid providers work with data stored in external databases, services, etc., and expose this data as FHIR while retaining all the flexibility and support for other elements in Smile CDR.

Hybrid Providers can be implemented both as a full or partial facade for your internal systems.

This capability in Smile CDR is equivalent to the Plain Server capability in HAPI FHIR, and uses the same API. The same Resource Provider classes and APIs that are created with HAPI FHIR are also used to create Hybrid Provider classes for Smile CDR.

Hybrid Providers have a number of enhancements over the HAPI FHIR Plain Server:

  • Access and updates to data are logged in the Smile CDR Audit Trail.
  • Authorization decisions (i.e. whether to allow a given FHIR request to proceed for a given user) are controlled by the Smile CDR security framework.
  • Management and monitoring capabilities are provided by the platform.

Use Cases

The classic use case for Hybrid Providers is use within an institution where an existing system stores a large amount of useful data. This might be an enterprise repository, a scheduling system, a billing system, etc.

If there is a desire to build new applications that connect to the existing system using modern data standards, Hybrid Providers can act as a bridge to allow this.

In this scenario, Hybrid Providers are created that define code to implement various FHIR operations. This code communicates directly with the existing system's database or legacy APIs, and maps responses to FHIR Resources as appropriate.

18.0.1Architecture

 

The diagram below shows how Hybrid Providers work. The box in grey contains customer code, which is code that you write.

Hybrid Providers Architecture

A Hybrid Providers implementation is packaged as a Java JAR file that contains several key components:

  • Resource Provider classes, which are HAPI FHIR classes used to implement type- and instance-level FHIR operations such as create, read, update, custom operations, etc.
  • Plain Provider classes, which are HAPI FHIR classes used to implement system-level FHIR operations such as transaction, system history, custom operations, etc.
  • A Spring Context Config class, which is a Spring Framework class used to instantiate and configure the providers.

18.0.2Resource Providers

 

Resource provider classes implement type- and instance- level FHIR operations for a specific FHIR resource type. For example, if your server supports the Patient resource type and you want to support the read and create operations for this resource type, you would typically do this in a Resource Provider class named PatientResourceProvider (note: the class name is just a suggestion; it can be anything you choose).

Resource provider classes must implement the IResourceProvider interface, and contain methods annotated with appropriate HAPI FHIR method annotations such as @Read, @Create, @Operation, etc. A complete reference of available FHIR operation API annotations can be found in the HAPI FHIR documentation.

A very basic example is shown below:

public class PatientResourceProvider implements IResourceProvider {

	/**
	 * The getResourceType method comes from IResourceProvider, and
	 * must be overridden to indicate what type of resource this
	 * resource provider supplies.
	 */
	@Override
	public Class<Patient> getResourceType() {
		return Patient.class;
	}

    /**
     * This method implements the FHIR "read" operation for the
     * Patient resource type.
     */
	@Read(version = true)
	public Patient readPatient(@IdParam IdType theId) {
	    // In a real example we would fetch this from somewhere, but
	    // we'll just create an object and pretend we fetched it.
	    Patient pt = new Patient();
	    pt.setId(theId);
	    pt.addName()
	        .setFamily("Simpson")
	        .addGiven("Homer")
	        .addGiven("Jay");
	    return pt;
	}
}

18.0.3CapabilityStatement

 

The CapabilityStatement for a Hybrid Providers REST Endpoint is generated based on the resource providers and their annotations as written in the hybrid providers project.

Static CapabilityStatements work for all FHIR Endpoint modules, including Hybrid Providers REST Endpoint modules.

18.0.4The Spring Context Config Class

 

This mandatory class is a Spring Framework Annotation-based Application Context Config class. It is characterized by having the @Configuration annotation on the class itself, as well as having one or more non-static factory methods annotated with the @Bean method, which create instances of your providers (as well as creating any other utility classes you might need, such as database pools, HTTP clients, etc.).

This class must instantiate at least two beans:

  • The resourceProviders bean method should return a List<IResourceProvider>, and contains a list of all Resource Provider classes to be used by the Hybrid Providers Endpoint module.
  • The plainProviders bean method should return a List<Object>, and contains a list of all Plain Providers.

This class may also optionally instantiate the following beans:

  • A bean named healthChecks that returns a map of names to health checks. Any health-checks added here will be automatically run with the frequency determined by module.clustermgr.config.stats.heartbeat_persist_frequency_ms (default once every 15 seconds). Server health-checks are available on the Admin Json endpoint, e.g. http://localhost:9000/runtime-status/node-statuses/health-checks. Smile CDR uses the Dropwizard Metrics library to manage health checks. The values of the healthChecks map need to extend com.codahale.metrics.health.HealthCheck. See the Example Project below for a working example of a Hybrid Provider Health Check.
  • A bean named endpointInterceptors that returns a list of interceptor instances. Any interceptors listed here will be automatically registered to the endpoint. This list will be used as authoritative if provided.

The following example shows a Spring Context Config class that creates two resource providers, one plain provider and one health check.

import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import org.springframework.context.annotation.Bean;

@Configuration
public class TestServerAppCtx {

    /**
     * This bean is a list of Resource Provider classes, each one
     * of which implements FHIR operations for a specific resource
     * type.
     */
    @Bean(name = "resourceProviders")
    public List<IResourceProvider> resourceProviders() {
        List<IResourceProvider> retVal = new ArrayList<>();
        retVal.add(new PatientResourceProvider());
        retVal.add(new ObservationResourceProvider());
        return retVal;
    }

    /**
     * This bean is a list of Plain Provider classes, each one
     * of which implements FHIR operations for system-level
     * FHIR operations.
     */
    @Bean(name = "plainProviders")
    public List<Object> plainProviders() {
        List<Object> retVal = new ArrayList<>();
        retVal.add(new SystemProvider());
        return retVal;
    }

    /**
     * This bean is a named list of HealthChecks that will be registered with the server.
     */

    @Bean(name = "healthChecks")
    public Map<String, HealthCheck> healthChecks() {
        Map<String, HealthCheck> retVal = new HashMap<>();
        // Name the health check and class whatever makes sense in your environment.  For example:
        // retVal.put("Lab system X", new LabSystemXHealthCheck());
        // retVal.put("Payment system Y", new PaymentSystemYHealthCheck());
        retVal.put("Remote System", new RemoteSystemHealthCheck());
        return retVal;
    }

    /**
     * This bean is a named list of interceptors that will be registered with the server.
     */
    @Bean(name = "endpointInterceptors")
    public List<Object> endpointInterceptors() {
        return Collections.singletonList(new LoggingInterceptor());
    }
    
}

18.0.5Interceptor Loading

 

There are 3 ways to register interceptors against a hybrid provider endpoint.

  1. Define a bean in your Spring Context Config class called endpointInterceptors. If this is provided, option 2 is ignored.
  2. Annotate the interceptors in your jar file with the @Interceptor annotation. If endpointInterceptors is missing, Smile CDR will scan the provided jar file for any beans annotated with this annotation and register them.
  3. Regardless of success of the previous two methods, you may always fill in the interceptor_bean_types field. This will read and construct individual beans from your jar file. These will be added to any interceptors already registered in options 1 and 2.
Only options 1 and 2 will allow your interceptors to resolve beans defined in your custom Spring Context Config class. Interceptors loaded via `interceptor_bean_types` will not be able to resolve or wire any bean dependencies defined in your context.

18.0.6Building Your Hybrid Providers

 

This section outlines considerations when buiding your Hybrid Provider classes.

See Library Support for details concerning libraries available on the classpath where your Hybrid Provider classes are loaded.

18.0.7Paging

 

If your Hybrid Provider endpoint requires paging, you can supply a paging provider in the Spring Context Config Class. A simple in-memory paging provider for a single server can be added to the Spring Context Config Class using the hapi-fhir FifoMemoryPagingProvider class as follows.

@Bean
IPagingProvider pagingProvider() {
   FifoMemoryPagingProvider retVal = new FifoMemoryPagingProvider(10);
   retVal.setDefaultPageSize(10);
   retVal.setMaximumPageSize(100);
   return retVal;
}

For more complex paging providers (e.g. where search results are persisted to a database and shared across multiple servers in a cluster), you would need to create your own implementation of the IPagingProvider interface.

See HAPI FHIR paging documentation for more details.

Packaging Your Providers

The Spring Context Config class and your Provider classes must all be packaged up in a normal Java JAR file. No third party libraries should be packaged with your code.

If you are using Apache Maven as your build system, this just means you should use a normal project with a packaging of jar.

18.0.8Deploying Your Hybrid Providers

 

Once you have created a JAR with your Hybrid Providers in it, this JAR should be placed in the customerlib/ directory of the Smile CDR installation, and Smile CDR should be restarted. You will then want to create a new module configuration as follows:

  1. Log into the Web Admin Console.
  2. Navigate to the Module Config section ( Config -> Module Config ).
  3. Create a new module of type Hybrid Providers Endpoint.
  4. Give the module a sensible ID.
  5. Under Listener Port, enter a port that the server will listen on.
  6. Under Spring Context Config Class, enter the fully qualified class name for your Spring Context Config Class.

18.0.9Accessing Security Attributes

 

Hybrid Providers methods have access to the underlying authenticated user session if needed. See Security Attributes for more information.

18.0.10Accessing Dao (Data Access Object)

 

Hybrid Providers can use a registry to get resource Dao, and operate directly on resources stored in the FHIR repository.

There are some caveats using resource Dao in your Hybrid Providers, and it needs to be carefully tested.

It is not the best practice to use Dao to access data, as they are highly tied to Hapi-FHIR / Smile CDR version.

The interfaces and/or behaviour can change quite a lot between versions, so there should be no expectation on compatibility between versions used.

It is much safer to use a FHIR client for long term use to retrieve/send data from/to a FHIR repository (IGenericClient created from FhirContext.newRestfulGenericClient()) as the interfaces and/or behaviour are more guaranteed by the FHIR specification.

But in some specific cases, Dao can be used.

Also, using Dao could allow users to access data that might not be permitted normally, depending on the user permissions. So it needs to be planned carefully.

We provided code examples in the sample Hybrid Providers project, to demonstrate how to use DaoRegistry to get resource Dao. Look for PractitionerProvider classes.

18.0.11Implementing Custom Operations

 

You can create your own custom operations on either Resource Provider or System Provider. These operations must return a FHIR resource.

public class PractitionerProvider implements IResourceProvider {
    // ...
    @Operation(name = "exportToCSV")
    public Binary exportPractitionersToBinaryCSV() {
        // ...
        // load your Practitioner resources here
        // ...
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (CSVWriter csvWriter = new CSVWriter(byteArrayOutputStream)) {
           // ...
           // write the appropriate data in CSV format
            csvWriter.line(/*...*/);
        }
        // ...
        Binary retVal = new Binary();
        retVal.setId(UUID.randomUUID().toString());
        retVal.setContentType("text/plain;charset=utf-8");
        retVal.getDataElement().setValue(byteArrayOutputStream.toByteArray());
        return retVal;
    }
}

The above sample code would handle POST {hybrid_provier_endpoint}/Practitioner/$exportToCSV calls, and would return a Binary resource with Base64 encoded data, containing the CSV lines.

18.0.12Exceptions

 

See Exceptions for details concerning exceptions and error management within Hybrid Providers classes.

18.0.13Example Project

 

A sample Hybrid Providers project is available at the following links: