2

In my tomcat application, I have a FHIRServlet that extends RestfulServer. It registers an interceptor to be able to retrieve the original JSON that is received by the servlet (I need to sign it and return it) and a Consent resource provider that handles the received consent resource :

package com.myapp.servlet;

import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import com.myapp.fhir.resourceprovider.ConsentResourceProvider;
import com.myapp.fhir.resourceprovider.component.PreprocessedRequestInterceptor;
import com.myapp.util.Configuration;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;


@WebServlet(urlPatterns = {"/fhir/*"}, displayName = "FHIR Server")
public class FHIRServlet extends RestfulServer {

    @Override
    protected void initialize() throws ServletException {
        setFhirContext(Configuration.getInstance().getFhirContext());

        registerInterceptor(new PreprocessedRequestInterceptor());

        setResourceProviders(List.of(new ConsentResourceProvider()));
    }
}

Unfortunately, I have two issues :

  1. When I try to retrieve the id of the consent it returns null. If I parse the original JSON manually with the same FhirContext, it retrieves the id just fine.
package com.myapp.fhir.resourceprovider;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hl7.fhir.r4.model.Consent;
import com.myapp.util.Configuration;

public class ConsentResourceProvider implements IResourceProvider {
    private final Logger logger = LogManager.getLogger(ConsentResourceProvider.class);

    @Override
    public Class<Consent> getResourceType() {
        return Consent.class;
    }

    @Create
    public MethodOutcome createConsent(@ResourceParam Consent theConsent, HttpServletRequest theRequest) throws Exception {
        logger.info("Identifier: " + getConsentIDWithVersion(theConsent)); // Returns null

        FhirContext fhirContext = Configuration.getInstance().getFhirContext();

        IParser parser = fhirContext.newJsonParser().setPrettyPrint(true);

        String userConsentChangeLogToSign = getUserConsentChangeLogToSign(theRequest);

        theConsent = parser.parseResource(Consent.class, userConsentChangeLogToSign);

        logger.info("Identifier: " + getConsentIDWithVersion(theConsent)); // Returns Consent/6139f5375d04d763fa8d06aa/_history/1631194843361

        Provenance provenance = createConsentSignature(theConsent, signedUserConsentChangeLog);

        logger.debug("Signature: "+ parser.encodeResourceToString(provenance));

        MethodOutcome methodOutcome = new MethodOutcome();
        methodOutcome.setResource(provenance);

        return methodOutcome;
    }

    protected String getUserConsentChangeLogToSign(HttpServletRequest theRequest) throws InvalidRequestException {
        String originalJSONConsent = new String((byte[])theRequest.getAttribute("originalJSONConsent"), StandardCharsets.UTF_8);

        logger.info("Retrieved originalJSONConsent: "+ originalJSONConsent);

        return originalJSONConsent;
    }

    protected Provenance createConsentSignature(Consent theConsent, String signedUserConsentChangeLog) {
        Provenance provenance = new Provenance();

        String consentId = getConsentIDWithVersion(theConsent);

        logger.debug("ConsentId: "+ consentId);

        provenance.getTargetFirstRep()
            .setType("Consent")
            .setReference(consentId);

        return provenance;
    }

    protected String getConsentIDWithVersion(Consent theConsent) {
        return theConsent.getId();
    }
}
package com.myapp.fhir.resourceprovider.component;

import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Pointcut;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.rest.api.server.RequestDetails;

public class PreprocessedRequestInterceptor {
    protected Logger logger = LogManager.getLogger(PreprocessedRequestInterceptor.class);

    @Hook(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED)
    public boolean interceptRequest(HttpServletRequest theRequest, RequestDetails requestDetails) {
        if(!"Consent".equalsIgnoreCase(requestDetails.getResourceName())) {
            logger.debug("Not a Consent resource: "+ requestDetails.getResourceName());
            return true;
        }

        requestDetails.loadRequestContents();

        if(requestDetails.getRequestContentsIfLoaded() == null) {
            logger.debug("RequestContents if not loaded");
            return true;
        }

        logger.info("Set originalJSONConsent attribute to "+ new String(requestDetails.getRequestContentsIfLoaded()));

        theRequest.setAttribute("originalJSONConsent", requestDetails.getRequestContentsIfLoaded());

        return true;
    }
}

I tried disabling (not registering) this last class but it doesn't help.

  1. When I create the provenance resource with the Consent id in the target, it is truncated so that it only contains "Consent/6139f5375d04d763fa8d06aa" without the whole _history part.

Edit > There is an option for the parser:

getFhirContext().getParserOptions().setStripVersionsFromReferences(false);

That fixes the second issue.

It feels like I'm doing something wrong but I couldn't find any useful information in the documentation.

Thanks for your help.

ixM
  • 1,244
  • 14
  • 29

0 Answers0