FHIR Connectathon 7 for Java Dummies
July 31, 2014 5 Comments
Last week we had a look at using the .net client to access a FHIR server as a lead up to the next connectathon. I thought it might be fun to do the same thing using the HAPI Java client (Now that it’s at release .5 – half way there!).
Now a disclaimer: I am not a Java developer – in fact I’m learning Java doing this work. In some ways I’d rather be in the .net world, but I’m a Mac user, and .net plays uneasily on that platform, so Java it is. (Yes, I know there’s Mono – it just doesn’t work for me for some reason).
I am using an IDE (be crazy otherwise) – I went for the IntelliJ IDEA IDE as I use their webstorm product for JavaScript development and like it. There’s a free version which is enough for the contents of this post, though I’ve used the full version as I use the Tomcat integration (more on that later).
So here are the steps I followed.
- Download the HAPI libraries and unzip to a suitable location. You can also use Maven which is what the real developers would use – a bit too much for me at the moment…
- Create a new console application. (Create a new project but don’t select any templates on the front page – the next page is where you can select a console app. Not the most intuitive I must admit).
- Create a reference to the HAPI Library, which is in the file you downloaded above. (To get there go: File > Project Structure > Libraries, click the ‘+’, select a java library and then select the folder)
- You also need to add a reference to the ‘dependencies’ sub-folder using the same process.
- Now you can add the code you need. This is what I ended up with:
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.dstu.resource.Conformance; import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.valueset.AdministrativeGenderCodesEnum; import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; //Code copied very substantially from http://jamesagnew.github.io/hapi-fhir/doc_rest_client.html public class Main { public static void main(String[] args) { //create the FHIR context FhirContext ctx = new FhirContext(); String serverBase = "https://fhir.orionhealth.com/blaze/fhir"; IGenericClient client = ctx.newRestfulGenericClient(serverBase); // Retrieve the server's conformance statement and print its description Conformance conf = client.conformance(); System.out.println(conf.getDescription().getValue()); //Read a single patient //String id = "1"; //this is not found on the server, and will throw an exception String id = "77662"; try { Patient patient = client.read(Patient.class, id); //Do something with patient // Change the patient gender patient.setGender(AdministrativeGenderCodesEnum.M); //and save... client.update(id, patient); } catch (ResourceNotFoundException ex) { System.out.println("No patient with the ID of " + id); } catch (Exception ex){ System.out.println("Unexpected error: " + ex.getMessage()); } //Create a new Patient Patient newPatient = new Patient(); newPatient.addIdentifier("urn:oid:2.16.840.1.113883.2.18.2", "PRP1660"); newPatient.addName().addFamily("Power").addGiven("Cold"); client.create() .resource(newPatient) .encodedJson() .execute(); //do a simple search Bundle bundle = client.search() .forResource(Patient.class) .where(Patient.NAME.matches().value("eve")) .execute(); //bundle will be a HAPI bundle of patients System.out.println(bundle.getTitle()); System.out.println(bundle.getEntries().size() + " matches."); } }
One nice thing about the IDE is that it will add the ‘import’ statements for you (if you’ve set up the references in steps 3 & 4 above). Just type in the name of the class (which will auto-complete). It goes red to indicate it can’t find it, then displays a popup offering to import it. Pressing <Alt><Enter> adds the import. Cool!
So looking at the code (and comparing it with the .net version) you’ll see more similarities than differences in approach (this is by design – I love open source!), although there are differences due to the different platforms. Much of this code has been copied from the HAPI site with only minor changes to defend against charges of plagiarism…
- So we create a context and then a client that points to the Blaze server (could be any FHIR server of course).
- We retrieve the conformance statement and print out the description (getting auto-complete as we go of course).
- Next we retrieve a single resource by ID. If we have a valid ID then all is well, if we don’t then the server will return a 404 which HAPI will respond to by throwing an exception which we can trap. So I stuck a try/catch block in there. In real life you’d do this for all calls of course…
- Having got a Patient we update the gender. (Changing the name would be less intrusive, but I’m going for minimal code here). Note the Enum to make life simple.
- Next we create a new Patient and save it – using JSON, just for fun…
- Finally we do a search for patients by name (using the ‘fluent’ style popularized by jQuery) and get back a bundle of patients that we could do things with. There are some nice options for specifying the query when it gets a bit more complex – check out the docs, or just use the auto-complete.
So there you have it: Track 1 of the Connectathon done for you! No excuses! (Actually I didn’t do the history scenario – but that should easy for you now – and there’s even an example in the HAPI docs).
One thing I did note is that HAPI is quite verbose in its logging – this can be controlled using ‘LoggingInterceptors‘ – I’ll have to have a play with them next.
If I’m being honest (generally a good policy) I have to admit to an ulterior purpose in doing this myself rather than getting someone else to do it for me (as Peter kindly did for the .net version). I want to use HAPI to build a ‘proxy’ between our existing services and a FHIR interface, and as we’re a Java shop HAPI in a servlet container made the most sense. HAPI offers the basic infrastructure to build a FHIR server, which will save a lot of effort. I’ve pretty much got that working – and I’ll report on that in another post.
See you at Connectathon!
Interesting that the chained methods, on the client and bundle objects, look rather like .NET LINQ expressions (excepting the superfluous-looking execute() call!). I actually think that the optimal, and most elegant, .NET FHIR client might be a LINQ to FHIR library
+Peter
You write: “Interesting that the chained methods”
Chained commands like this
client.create()
.resource(newPatient)
.encodedJson()
.execute();
are a bad habit!!!
Although perfectly legal, it is difficult to maintain code, and difficult to debug.
For the compiler it makes no difference if you write it in separate lines/commands/objects (like example below), it gets optmized in the same way.
It is just a disadvantage for debuggers and maintainers, that is all.
Compilers are smart. There is a lot of effort in Java in optimizing code before compiling.
For example, suppose the Class types were correct, following code would execute exact the same instructions, but here is clear in one second which object does what, and in case of an exception you know exactly on which line it occurs.
Client c = client.create();
Patient p = c.resource(newPatient);
Json j = p.encodedJson();
j.execute();
Pingback: SMART on FHIR: Part 1 | Hay on FHIR
Thanks Peter this is helpful. Looking forward to next set of tutorials
From the post to our HL7 NZ Facebook page linking this article.
[Ayan Chax] How do I call a NTE segment from an instance of ADT_A01 adt=new DFT_P03();
adt.initQuickstart(“ADT”, “A01”, “P”);
Post this line of code, how can I call getNTE(), adt.getNTE() is not taken?