Examples: Subscription - Smile CDR Documentation

Subscription Examples

This page contains example interceptors that can be registered with Subscription.

Example: Adding Authorization header

The following interceptor can be used to add any authorization header, like Basic Auth or Bearer token, before calling a REST hook deliverer for each resource delivery message.

/*-
 * #%L
 * Smile CDR - CDR
 * %%
 * Copyright (C) 2016 - 2026 Smile CDR, Inc.
 * %%
 * All rights reserved.
 * #L%
 */
package com.smilecdr.demo.subscription;

import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.Map;

/**
 * This class is a Hook Interceptor that is invoked immediately before REST HOOK
 * deliveries. It demonstrates a simple OAuth2 Client Credentials flow to dynamically
 * retrieve a Bearer token and inject it as an Authorization header
 */
@Interceptor
public class SubscriptionRestHookDynamicHeaderInterceptor {

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

// TODO: Replace with your OAuth2 token endpoint URL
    private static final String TOKEN_ENDPOINT = "https://your-auth-server.example.com/oauth2/token";

// TODO: Replace with your OAuth2 client_id
    private static final String CLIENT_ID = "your-client-id";

// TODO: Replace with your OAuth2 client_secret
    private static final String CLIENT_SECRET = "your-client-secret";

private final RestTemplate myRestTemplate = new RestTemplate();

/**
     * The {@link Pointcut#SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY} is invoked immediately before each
     * REST HOOK subscription delivery.
     *
     * @param theDeliveryMessage This object contains details about the subscription invocation, including
     *                           the contents of the resource that will be delivered.
     * @param theSubscription    This object contains details about the subscription, including the delivery endpoint,
     *                           headers to add, etc. A new copy of this object is created for every individual delivery
     *                           so it is fine to modify it, and this will not affect other deliveries or
     *                           subscriptions.
     * @return Return <code>true</code> if the subscription should proceed, or <code>false</code> if it
     * should be aborted.
     */
    @Hook(Pointcut.SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY)
    public boolean beforeRestHookDelivery(
            ResourceDeliveryMessage theDeliveryMessage, CanonicalSubscription theSubscription) {

try {
            final String accessToken = fetchAccessToken();
            theSubscription.addHeader("Authorization: Bearer " + accessToken);
        } catch (Exception theException) {
            ourLog.error("Failed to fetch access token from {}; aborting subscription delivery", TOKEN_ENDPOINT, theException);
            return false;
        }

return true;
    }

/**
     * Performs an OAuth2 Client Credentials grant request
     * and returns the raw access token string. This example is using
     * client credentials secret flow but it can be updated to use
     * client credentials jwt assertion flow
     */
    @SuppressWarnings("unchecked")
    protected String fetchAccessToken() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.setBasicAuth(CLIENT_ID, CLIENT_SECRET);

MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("grant_type", "client_credentials");

Map<String, Object> response = myRestTemplate.postForObject(
                TOKEN_ENDPOINT, new HttpEntity<>(body, headers), Map.class);

if (response == null || !response.containsKey("access_token")) {
            throw new RuntimeException("Token endpoint response did not contain an access_token field");
        }

return (String) response.get("access_token");
    }
}

Copy

Example: Starter Subscription interceptor for all SUBSCRIPTION_xxx pointcuts

The following example shows an interceptor that can be used as a starter Subscription interceptor, implementing a hook method for each available pointcut.

import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.util.StopWatch; import org.slf4j.Logger; import org.slf4j.LoggerFactory;

/**

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

@Hook(Pointcut.SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED) public void subscriptionAfterActiveSubscriptionRegistered(CanonicalSubscription theCanonicalSubscription) {

ourLog.info("Interceptor SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info( "Interceptor SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED - ended, execution took {}", stopWatch); } }

@Hook(Pointcut.SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_UNREGISTERED) public void subscriptionAfterActiveSubscriptionUnregistered() {

ourLog.info("Interceptor SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_UNREGISTERED - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info( "Interceptor SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_UNREGISTERED - ended, execution took {}", stopWatch); } }

@Hook(Pointcut.SUBSCRIPTION_RESOURCE_MODIFIED) public boolean subscriptionResourceModified(ResourceModifiedMessage theResourceModifiedMessage) {

ourLog.info("Interceptor SUBSCRIPTION_RESOURCE_MODIFIED - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info("Interceptor SUBSCRIPTION_RESOURCE_MODIFIED - ended, execution took {}", stopWatch); } return true; }

@Hook(Pointcut.SUBSCRIPTION_RESOURCE_DID_NOT_MATCH_ANY_SUBSCRIPTIONS) public void subscriptionResourceDidNotMatchAnySubscriptions(ResourceModifiedMessage theResourceModifiedMessage) {

ourLog.info("Interceptor SUBSCRIPTION_RESOURCE_DID_NOT_MATCH_ANY_SUBSCRIPTIONS - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info( "Interceptor SUBSCRIPTION_RESOURCE_DID_NOT_MATCH_ANY_SUBSCRIPTIONS - ended, execution took {}", stopWatch); } }

@Hook(Pointcut.SUBSCRIPTION_RESOURCE_MATCHED) public boolean subscriptionResourceMatched( CanonicalSubscription theCanonicalSubscription, ResourceDeliveryMessage theResourceDeliveryMessage, InMemoryMatchResult theInMemoryMatchResult) {

ourLog.info("Interceptor SUBSCRIPTION_RESOURCE_MATCHED - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info("Interceptor SUBSCRIPTION_RESOURCE_MATCHED - ended, execution took {}", stopWatch); } return true; }

@Hook(Pointcut.SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED) public boolean subscriptionBeforePersistedResourceChecked(ResourceModifiedMessage theResourceModifiedMessage) {

ourLog.info("Interceptor SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info( "Interceptor SUBSCRIPTION_BEFORE_PERSISTED_RESOURCE_CHECKED - ended, execution took {}", stopWatch); } return true; }

@Hook(Pointcut.SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED) public void subscriptionAfterPersistedResourceChecked(ResourceModifiedMessage theResourceModifiedMessage) {

ourLog.info("Interceptor SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info( "Interceptor SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED - ended, execution took {}", stopWatch); } }

@Hook(Pointcut.SUBSCRIPTION_BEFORE_DELIVERY) public boolean subscriptionBeforeDelivery( CanonicalSubscription theCanonicalSubscription, ResourceDeliveryMessage theResourceDeliveryMessage) {

ourLog.info("Interceptor SUBSCRIPTION_BEFORE_DELIVERY - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info("Interceptor SUBSCRIPTION_BEFORE_DELIVERY - ended, execution took {}", stopWatch); } return true; }

@Hook(Pointcut.SUBSCRIPTION_AFTER_DELIVERY) public void subscriptionAfterDelivery( CanonicalSubscription theCanonicalSubscription, ResourceDeliveryMessage theResourceDeliveryMessage) {

ourLog.info("Interceptor SUBSCRIPTION_AFTER_DELIVERY - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info("Interceptor SUBSCRIPTION_AFTER_DELIVERY - ended, execution took {}", stopWatch); } }

@Hook(Pointcut.SUBSCRIPTION_AFTER_DELIVERY_FAILED) public boolean subscriptionAfterDeliveryFailed( ResourceDeliveryMessage theResourceDeliveryMessage, Exception theException) {

ourLog.info("Interceptor SUBSCRIPTION_AFTER_DELIVERY_FAILED - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info("Interceptor SUBSCRIPTION_AFTER_DELIVERY_FAILED - ended, execution took {}", stopWatch); } return true; }

@Hook(Pointcut.SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY) public boolean subscriptionBeforeRestHookDelivery( CanonicalSubscription theCanonicalSubscription, ResourceDeliveryMessage theResourceDeliveryMessage) {

ourLog.info("Interceptor SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info("Interceptor SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY - ended, execution took {}", stopWatch); } return true; }

@Hook(Pointcut.SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY) public void subscriptionAfterRestHookDelivery( CanonicalSubscription theCanonicalSubscription, ResourceDeliveryMessage theResourceDeliveryMessage) {

ourLog.info("Interceptor SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info("Interceptor SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY - ended, execution took {}", stopWatch); } }

@Hook(Pointcut.SUBSCRIPTION_BEFORE_MESSAGE_DELIVERY) public boolean subscriptionBeforeMessageDelivery( CanonicalSubscription theCanonicalSubscription, ResourceDeliveryMessage theResourceDeliveryMessage, ResourceModifiedJsonMessage theResourceModifiedJsonMessage) {

ourLog.info("Interceptor SUBSCRIPTION_BEFORE_MESSAGE_DELIVERY - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info("Interceptor SUBSCRIPTION_BEFORE_MESSAGE_DELIVERY - ended, execution took {}", stopWatch); } return true; }

@Hook(Pointcut.SUBSCRIPTION_AFTER_MESSAGE_DELIVERY) public void subscriptionAfterMessageDelivery( CanonicalSubscription theCanonicalSubscription, ResourceDeliveryMessage theResourceDeliveryMessage) {

ourLog.info("Interceptor SUBSCRIPTION_AFTER_MESSAGE_DELIVERY - started"); StopWatch stopWatch = new StopWatch(); try { // your implementation goes here } finally { ourLog.info("Interceptor SUBSCRIPTION_AFTER_MESSAGE_DELIVERY - ended, execution took {}", stopWatch); } } }


Copy

[Examples: MDM](/content/docs/interceptors/examples_mdm.html)

Examples: Subscription

Interceptors

[Interceptors](/content/docs/interceptors/interceptors.html)
[Pointcuts](/content/docs/interceptors/pointcuts.html)
[Starter Project](/content/docs/interceptors/starter_project.html)
[Examples: FHIR Endpoints](/content/docs/interceptors/examples_fhir_endpoint.html)
[Examples: HL7v2 Endpoints](/content/docs/interceptors/examples_hl7v2_endpoint.html)
[Examples: FHIR Storage](/content/docs/interceptors/examples_fhir_storage.html)
[Examples: FHIR Gateway](/content/docs/interceptors/examples_fhir_gateway.html)
[Examples: FHIR Client](/content/docs/interceptors/examples_fhir_client.html)
[Examples: MDM](/content/docs/interceptors/examples_mdm.html)
[Examples: Subscription](/content/docs/interceptors/examples_subscription.html)
[Examples: Channel Import](/content/docs/interceptors/examples_channel_import.html)
[Examples: Cluster Manager](/content/docs/interceptors/examples_cluster_manager.html)
[Examples: App Sphere](/content/docs/interceptors/examples_app_sphere.html)
[Examples: System To System Data Exchange](/content/docs/interceptors/examples_s2s_data_exchange.html)
[2024.02.01 Migration Guide](/content/docs/interceptors/jakarta_upgrade.html)

[Examples: Channel Import](/content/docs/interceptors/examples_channel_import.html)