How to Call a WebService URL via Java Code for FHIR Resources and alternative approaches

Tags: #<Tag:0x00007f60051ce7f8>

I am looking for an example of how I can call a webservice URL specifically in this case from the FHIR module, from a Scheduled Task - I need the output so that I can process it

I am thinking, how can I:

  1. Build the url from OpenMRS constants - I do not have access to a request object so where can I find the host, port and scheme (http vs https)
  2. Get an authenticated user which will probably be the daemon user
  3. Call the url and get the results

Maybe differently is there a way I can call the WebService classes and pass the parameters as we do Reporting Service

1 Like

Hello @ssmusoke , this looks close to what i did here in the hapi fhir .

You can use an HTTP client like Apache , and since this will run with in the server , you will have these values constant
port :8080 ,
host :localhost
scheme: http .
so the base url is http://127.0.0.1:8080" .
we also hard coded the base url .

note that a change in the port and scheme usually comes either through using a proxy to the external enviroment or through port forwarding ,but internally those are usually constant.

Infact , even if you were doing this from a controller where you could have acess to a request object , and you picked the host, port and scheme from the request , you would have errors in cases where you port forward (say incase of using docker) or in the case where theres a proxy between the server and the external enviroment. it will mean the actual port and scheme exposed to the external enviroment is different from the actual port and host with in the server enviroment .

At least for the FHIR2 version, something like this would work (please excuse the really hacky means of getting a reference to the ApplicationContext):

public class SampleTask extends AbstractTask {

	@Override
	public void execute() {
		ServiceContext serviceContext = (ServiceContext) ReflectionUtils.getField(ReflectionUtils.findField(Context.class, "serviceContext"), Context.class);
		
		FhirPatientService patientService = serviceContext.getApplicationContext().getBean(FhirPatientService.class);
		IParser parser = FhirContext.forR4().newJsonParser();
		parser.encodeResourceToString(patientService.get("Some UUID"));
	}
}

This will get the FHIR-formatted version of an OpenMRS patient and convert it into a JSON string. You can get an XML version by replacing newJsonParser() with newXmlParser().

Things are a little more complicated if you need more than one resource at a time, which is usually done via a search operation. The FHIR2 service classes generally have a searchFor...() method, but the parameters vary quite a bit and need to be encoded using the appropriate HAPI classes, so it’s quite a bit more fragile. But, you could, for instance, get all the patients named “Adam” like this:

IBundleProvider bundleProvider = patientService
	.searchForPatients(null, new StringAndListParam().addAnd(new StringParam("Adam")), null, null, null, null,
			null, null, null, null, null, null, null, null, null, null);

List<Patient> patients = bundleProvider.getResources(0, Integer.MAX_VALUE).stream().filter(r -> r instanceof Patient)
	.map(r -> (Patient) r).collect(Collectors.toList());

and convert them to JSON or XML following a similar pattern.


I believe things are similar with the old FHIR module, except the services are actually normal OpenMRS services and can be gotten with Context.getService(...).

1 Like

@mozzy Thanks we are looking to call this within OpenMRS

@ibacher This is what we actually thought of doing, but were not sure of how to achieve it within Tasks

1 Like

Thanks @ibacher ,

ideally its not a very good practice as per say to use a client from with in the server , since you can have acces to the service API directly.

in our use case, the request url is very dynamic ,the request params could be adjusted , so it was more natural to handle the request using a client. And its seems not the case here ,

@ibacher Looks like the FHIRService classes cannot be loaded like the regular OpenMRS services like AdministrationService, PersonService etc as the hacky approach gives an error

Could not access method: Class org.springframework.util.ReflectionUtils can not access a member of class org.openmrs.api.context.Context with modifiers “private static”

PS: I have also seen that the services interfaces do not extend the OpenmrsService interface too, any reason why? Is this a non-destructive PR that can be made to simplify loading then in other modules

@ssmusoke You might need more ceremony to get the ApplicationContext:

	@Override
	public void execute() {
		try {
			Field serviceContextField = Context.class.getDeclaredField("serviceContext");
			serviceContextField.setAccessible(true);
			try {
				ApplicationContext applicationContext = ((ServiceContext) serviceContextField.get(null))
						.getApplicationContext();

				FhirPatientService patientService = applicationContext.getBean(FhirPatientService.class);
				IParser parser = FhirContext.forR4().newJsonParser();
				parser.encodeResourceToString(patientService.get("Some UUID"));
			}
			finally {
				serviceContextField.setAccessible(false);
			}
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

These services are intended to have a slightly different lifecycle from standard OpenMRS services. See this not yet merged commit.

1 Like

Awesome let us try this out…

Why would the FHIR Services will have a different lifecycle from the OpenMRS Services?

So the real point of the FHIR services is to provide an API for a HAPI Plain Server instance, which is basically a fancy Servlet and I was having some difficulties with the differences between the standard module lifecycle and the lifecycle of that servlet.

@ibacher this process takes a lot of time. wondering what could be the cause. bundleProvider.getResources(0, Integer.MAX_VALUE).

I have over 14527 patients. When I try to get resources after the search has completed “fhirPatientService.searchForPatients(null, null, null, null, null, null, null, null, null, null, null, null, null, lastUpdated, null, null)”.

On getting the resources. that takes a lot of time.

lastUpdated resolves to either the date_created and date_updated fields in the database. Usually those are unindexed, so the underlying query is going to perform pretty badly. If you can add indexes on those fields that will probably help improve things.

Also, the translation time is probably a bit long if you’re trying to translate 14,527 patients in one go. This is something the module needs some work on (i.e. caching rarely changed information). Which is an area where work on the module is needed.

@slubwama Also, if you’re thinking of leveraging data for all the patients in your OpenMRS instance, I would strongly advise taking a look at either the analytics engine or openmrs-eip as the basis for extracting FHIR-formatted data from OpenMRS as there is a great deal of stuff already built around that. See for instance this app that leverages the FHIR2 module and OpenMRS EIP to integrate with Senaite (a lab system) or this proof of concept which leverages the analytics engine to extract OpenMRS data into a HAPI FHIR instance.

No one needs to be re-inventing the wheel.

@ibacher thanks for this its so helpful