FHIR Connectathon 7 for Java Dummies

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.

  1. 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…
  2. 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).
  3. 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)
  4. You also need to add a reference to the ‘dependencies’ sub-folder using the same process.
  5. 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!

About David Hay
I'm an independent contractor working with a number of Organizations in the health IT space. I'm an HL7 Fellow, Chair Emeritus of HL7 New Zealand and a co-chair of the FHIR Management Group. I have a keen interest in health IT, especially health interoperability with HL7 and the FHIR standard. I'm the author of a FHIR training and design tool - clinFHIR - which is sponsored by InterSystems Ltd.

5 Responses to FHIR Connectathon 7 for Java Dummies

  1. Peter Jordan says:

    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

    • bertverhees says:

      +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();

  2. Pingback: SMART on FHIR: Part 1 | Hay on FHIR

  3. Ravi says:

    Thanks Peter this is helpful. Looking forward to next set of tutorials

  4. Ray Murakami says:

    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?

Leave a Reply to Ray MurakamiCancel reply

Discover more from Hay on FHIR

Subscribe now to keep reading and get access to the full archive.

Continue reading