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:
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.
The diagram below shows how Hybrid Providers work. The box in grey contains customer code, which is code that you write.
A Hybrid Providers implementation is packaged as a Java JAR file that contains several key components:
create
, read
, update
, custom operations, etc.transaction
, system history
, custom operations, etc.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;
}
}
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.
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:
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.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:
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.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());
}
}
There are 3 ways to register interceptors against a hybrid provider endpoint.
endpointInterceptors
. If this is provided, option 2 is ignored.@Interceptor
annotation. If endpointInterceptors
is missing, Smile CDR will scan the provided jar file for any beans annotated with this annotation and register them.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.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.
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.
Your Hybrid Providers can autowire an ICamelRouteEndpointSvc, which allows sending messages to a camel module route.
Example:
import ca.cdr.api.camel.ICamelRouteEndpointSvc;
/**
* Resource provider using an {@link ICamelRouteEndpointSvc} to send messages to a camel module route.
*/
public class DirectCamelEndpointLinkProvider implements IResourceProvider {
public static final String MODULE_NAME = "camel";
public static final String ROUTE_NAME = "test-route1";
public static final String HEADER_NAME = "test-header";
public static final String HEADER_VALUE = "test-header-value";
@Autowired
private ICamelRouteEndpointSvc myICamelRouteEndpointSvc;
@Read(version = true)
public Patient readPatient(@IdParam IdType theId) {
// In a real use case patient would be fetched from some repository
Patient pt = new Patient();
pt.setId(theId);
pt.addName().setFamily("Simpson").addGiven("Homer").addGiven("Jay");
Exchange exchange =
myICamelRouteEndpointSvc.send(MODULE_NAME, ROUTE_NAME, pt, Map.of(HEADER_NAME, HEADER_VALUE));
Object body = exchange.getMessage().getBody();
return (Patient) body;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
}
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
.
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:
Hybrid Providers methods have access to the underlying authenticated user session if needed. See Security Attributes for more information.
As mentioned in the Consent Service documentation, Consent Service can be enabled for Hybrid Provider modules and implemented using JavaScript or Java APIs. Note though that for JavaScript implementations, the Fhir JavaScript Execution Environment APIs is NOT available for Hybrid Provider modules.
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.
See Exceptions for details concerning exceptions and error management within Hybrid Providers classes.
A sample Hybrid Providers project is available at the following links: