20161116 hapi fhir java beginner and advanced for dev days (1)
TRANSCRIPT
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
HAPI-FHIR for Java Developers
James AgnewFHIR Developer DaysNovember 16, 2016
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Who am I?
● Name: James Agnew● Company: Centre for Global eHealth Innovation (University Health Network)
● Background:● Software Development Manager● Project lead for HAPI for 11 years
2
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
About this Presentation
● All code samples are available on GitHub in fully working form
https://github.com/furore-fhir/fhirstarters
● HAPI is too big a topic for 45 minutesso…● I will be around! Let’s talk!
3
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Show of Hands
● Who here is new to FHIR?● Who here is new to HAPI FHIR?
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
FHIR: A Quick Recap
● There are two key parts of FHIR to consider:● The first is a data model for healthcare Resources
and supporting Datatypes
5
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
● The second is a RESTful API for interacting with that model
6
FHIR: A Quick Recap (2)
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
● FHIR has had several releases● FHIR DSTU1 (Released 2014)● FHIR DSTU2 (Released 2015)● FHIR STU3 (Will be released early 2017?)
● This presentation focuses on STU3 but the concepts apply to all versions
7
FHIR: A Quick Recap (3)
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
The HAPI Project
● HAPI started in 2001 as an HL7 v2 Library● Built to support a simple web portal, now used
in applications around the world
HL7 v2 - http://hl7api.sourceforge.net FHIR - http://hapifhir.io
8
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
HAPI FHIR: What is it?
● Not a client or a server, but a toolkit for building either
9
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
HAPI FHIR: Why did we build it?● Two use cases at our hospital:
● As a unified clinical data access layer● As a backend for our mobile apps
10
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
FHIR in Java
● HAPI is now the Java Reference Implementation● Many RI features are “copied in” for each release
11
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Design Goals
● Use Anywhere● Apache 2.0 License for all components● Minimal dependencies
● Be Flexible● Loosely coupled, pluggable components
● Be Powerful● “Steal” all the best ideas from existing frameworks:
JAX-WS, Springframework, .NET FHIR API ☺..etc..
12
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
ComponentsHAPI FHIR Modules
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Today (Beginner)Tomorrow (Advanced)
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Structures Classes:The FHIR Model
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Structures Classes:The FHIR Model
● HAPI supports multiple versions of FHIR via different “structures JARs”
hapi-fhir-structures-dstu-2.1.jar
hapi-fhir-structures-dstu2-2.1.jar
hapi-fhir-structures-dstu3-2.1.jar
FHIR Version HAPI Version
Patient Resource ModelsPatient Resource ModelsPatient Resource Models
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Structures Classes:The FHIR Model
● HAPI Defines several sets of classes which form the data model
● Resource definition classes implement IBaseResource
● Examples: Patient, CarePlan, Encounter, Practitioner, Medication
17
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Structures Classes: The FHIR Model (2)
● HAPI also defines a class for each data type
● Primitive classes are named [name]Type
● Primitive types include: StringType, BooleanType
● Composite types include: Address, Ratio, HumanName
18
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Structures Classes: The FHIR Model (3)
● JavaDocs for structures are available here: http://hapifhir.io/apidocs-dstu3/index.html
19
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Using the Structures:Creating a Resource
20
1: public class Example01_CreateAPatient { 2: public static void main(String[] theArgs) { 3: // Create a resource instance 4: Patient pat = new Patient(); 5: 6: // Add a "name" element 7: HumanName name = pat.addName(); 8: name.addFamily("Simpson").addGiven("Homer").addGiven("J"); 9: 10: // Add an "identifier" element11: Identifier identifier = pat.addIdentifier();12: identifier.setSystem("http://acme.org/MRNs").setValue("7000135");13: 14: // Model is designed to be chained15: pat.addIdentifier().setSystem("http://acme.org/MRNs").setValue("12345");16: }17: }
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
1: public class Example01_CreateAPatient { 2: public static void main(String[] theArgs) { 3: // Create a resource instance 4: Patient pat = new Patient(); 5: 6: // Add a "name" element 7: HumanName name = pat.addName(); 8: name.addFamily("Simpson").addGiven("Homer").addGiven("J"); 9: 10: // Add an "identifier" element11: Identifier identifier = pat.addIdentifier();12: identifier.setSystem("http://acme.org/MRNs").setValue("7000135");13: 14: // Model is designed to be chained15: pat.addIdentifier().setSystem("http://acme.org/MRNs").setValue("12345");16: }17: }
Using the Structures:Use the Autocomplete!
21
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Using the Structures:Enumerated Types
22
1: public class Example02_EnumeratedTypes { 2: public static void main(String[] theArgs) { 3: 4: Patient pat = new Patient(); 5: 6: pat.addName().addFamily("Simpson").addGiven("Homer").addGiven("J"); 7: pat.addIdentifier().setSystem("http://acme.org/MRNs").setValue("7000135"); 8: 9: // Enumerated types are provided for many coded elements10: ContactPoint contact = pat.addTelecom();11: contact.setUse(ContactPointUse.HOME);12: contact.setSystem(ContactPointSystem.PHONE);13: contact.setValue("1 (416) 340-4800");14: 15: pat.setGender(AdministrativeGender.MALE);16: }17: }
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Using the Structures:Primitive Fields
HumanName.family is a string in FHIR
There are two ways of interacting with this field:String HumanName.getFamily()void HumanName.setFamily(String)
StringDt HumanName.getFamilyElement();void HumanName.setFamilyElement(StringDt);
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Using the Structures:Add Extensions
24
1: public class Example03_AddSomeExtensions { 2: public static void main(String[] theArgs) { 3: Patient pat = new Patient(); 4: pat.addName().addFamily("Simpson").addGiven("Homer").addGiven("J"); 5: 6: // Add an extension on the resource 7: pat.addExtension() 8: .setUrl("http://hl7.org/fhir/StructureDefinition/patient-importance") 9: .setValue(new CodeableConcept().setText("Patient is a VIP"));10: 11: // Add an extension on a primitive12: pat.getBirthDateElement().setValueAsString("1955-02-22");13: pat.getBirthDateElement().addExtension()14: .setUrl("http://hl7.org/fhir/StructureDefinition/patient-birthTime")15: .setValue(new TimeType("23:30"));16: }17: }
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Parsers:Serializing & Deserializing
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Playing with Parsers
26
● The starting point for much of the HAPI-FHIR API is the FhirContext class
● FhirContext acts as a factory for the rest of the API, including the two parsers:● XmlParser● JsonParser
● FhirContext is designed to be created once and reused (important for performance!)
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Playing with Parsers:Encoding Resources
27
1: public class Example04_EncodeResource { 2: public static void main(String[] theArgs) { 3: 4: // Create a Patient 5: Patient pat = new Patient(); 6: pat.addName().addFamily("Simpson").addGiven("Homer").addGiven("J"); 7: pat.addIdentifier().setSystem("http://acme.org/MRNs").setValue("7000135"); 8: pat.addTelecom().setSystem(ContactPointSystem.PHONE).setValue("1 (416) 340-4800"); 9: pat.setGender(AdministrativeGender.MALE);10: 11: // Create a context12: FhirContext ctx = FhirContext.forDstu3();13: 14: // Create a XML parser15: IParser parser = ctx.newJsonParser();16: parser.setPrettyPrint(true);17: 18: String encode = parser.encodeResourceToString(pat);19: System.out.println(encode);20: 21: }22: }
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Playing with Parsers:Encoding Resources (2)
28
// Create a contextFhirContext ctx = FhirContext.forDstu3();
// Create a XML parserIParser parser = ctx.newJsonParser();parser.setPrettyPrint(true);
String encode = parser .encodeResourceToString(pat);System.out.println(encode);
1: { 2: "resourceType": "Patient", 3: "identifier": [ { 4: "system": "http://acme.org/MRNs", 5: "value": "7000135" 6: } ], 7: "name": [ { 8: "family": [ "Simpson" ], 9: "given": [ "Homer", "J" ]10: } ],11: "telecom": [ {12: "system": "phone",13: "value": "1 (416) 340-4800",14: "use": "home"15: } ],16: "gender": "male"17: }
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Playing with Parsers:Parsing Resources
29
1: public class Example05_ParseResource { 2: public static void main(String[] theArgs) { 3: String resourceBody = "{\"resourceType\":\"Patient\",\"identifier\":[{\"system\":\"http://acme.org/MRNs\",\"value\":\"7000135\"}],\"name\":[{\"family\":[\"Simpson\"],\"given\":[\"Homer\",\"J\"]}]}"; 4: 5: // Create a context 6: FhirContext ctx = FhirContext.forDstu3(); 7: 8: // Create a JSON parser 9: IParser parser = ctx.newJsonParser();10: Patient pat = parser.parseResource(Patient.class, resourceBody);11: 12: List<Identifier> identifiers = pat.getIdentifier();13: String idSystemString = identifiers.get(0).getSystem();14: String idValueString = identifiers.get(0).getValue();15: 16: System.out.println(idSystemString + " " + idValueString);17: }18: } http://acme.org/MRNs - 7000135
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Clients:Moving Data Around
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
FHIR Clients:Recap on REST● FHIR defines basic CRUD operations that
can be performed on a FHIR compliant server (*not a complete list)
31
Name HTTP URL
type create POST http://base/[type]
instance read GET http://base/[type]/[id]
instance update PUT http://base/[type]/[id]
instance delete DELETE http://base/[type]/[id]
type search GET http://base/[type]?[params]
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
FHIR Clients:Basic CRUD - Create
32
1: public class Example06_ClientCreate { 2: public static void main(String[] theArgs) { 3: Patient pat = new Patient(); 4: pat.addName().addFamily("Simpson").addGiven("Homer").addGiven("J"); 5: pat.addIdentifier().setSystem("http://acme.org/MRNs").setValue("7000135"); 6: pat.setGender(AdministrativeGender.MALE); 7: 8: // Create a context 9: FhirContext ctx = FhirContext.forDstu3();10: 11: // Create a client12: String serverBaseUrl = "http://fhirtest.uhn.ca/baseDstu3";13: IGenericClient client = ctx.newRestfulGenericClient(serverBaseUrl);14: 15: // Use the client to store a new resource instance 16: MethodOutcome outcome = client.create().resource(pat).execute();17: 18: // Print the ID of the newly created resource19: System.out.println(outcome.getId());20: }21: } http://fhirtest.uhn.ca/base/Patient/4529/_history/1
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
1: public class Example07_ClientReadAndUpdate { 2: public static void main(String[] theArgs) { 3: // Create a client 4: String serverBaseUrl = "http://fhirtest.uhn.ca/baseDstu3"; 5: FhirContext ctx = FhirContext.forDstu3(); 6: IGenericClient client = ctx.newRestfulGenericClient(serverBaseUrl); 7: 8: // Use the client to read back the new instance using the ID 9: Patient patient = client.read(Patient.class, "190002");10: 11: // Print the ID of the newly created resource12: System.out.println("Found ID: " + patient.getId());13: 14: // Change the gender and send an update to the server15: patient.setGender(AdministrativeGender.FEMALE);16: MethodOutcome outcome = client.update().resource(patient).execute();17: 18: System.out.println("Now have ID: " + outcome.getId());19: }20: }
FHIR Clients:Basic CRUD - Read/Update
http://fhirtest.uhn.ca/base/Patient/190002/_history/2
http://fhirtest.uhn.ca/base/Patient/190002/_history/1
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
● FHIR defines a powerful search mechanism● Searches are specially crafted URLs to
express queries such as:● Find a Patient with the given Identifier● Find all Patients with given gender and
DOB● Find all lab reports for a given patient
identifier with an “abnormal” interpretation34
FHIR Clients:Searching
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
● Searching is powerful! Learn about it athttp://hl7.org/fhir/search.html
● For now, let’s imagine a search for a Patient named “Test” whose birthdate is before 2014
35
FHIR Clients:Searching (2)
=
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
1: public class Example08_ClientSearch { 2: public static void main(String[] theArgs) { 3: FhirContext ctx = FhirContext.forDstu3(); 4: IGenericClient client = ctx.newRestfulGenericClient("http://fhirtest.uhn.ca/baseDstu3"); 5: 6: // Log requests and responses (very verbose for testing!) 7: client.registerInterceptor(new LoggingInterceptor(true)); 8: 9: // Build a search and execute it10: Bundle response = client.search()11: .forResource(Patient.class)12: .where(Patient.NAME.matches().value("Test"))13: .and(Patient.BIRTHDATE.before().day("2014-01-01"))14: .count(100)15: .returnBundle(Bundle.class)16: .execute();17: 18: // How many resources did we find?19: System.out.println("Responses: " + response.getTotal());20: 21: // Print the ID of the first one22: System.out.println(response.getEntry().get(0).getResource().getId());23: }24: }
FHIR Clients:Searching (3)
http://fhirtest.uhn.ca/baseDstu3/Patient/82599/_history/1
27
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
9: // Build a search and execute it10: Bundle response = client.search()11: .forResource(Patient.class)12: .where(Patient.NAME.matches().value("Test"))13: .and(Patient.BIRTHDATE.before().day("2014-01-01"))14: .count(100)15: .returnBundle(Bundle.class)16: .execute();
FHIR Clients:Searching (4)
37
Many more available options for searching!
See:http://hapifhir.io/doc_rest_client.html#SearchQuery_-_Type
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
FHIR Clients:Lots More● You can follow a similar pattern to do many
more operations:● Delete, Validate, History, Tags, etc…
● Client logging interceptor can be very helpful● http://hapifhir.io/hapi-fhir/doc_rest_client_interceptor.html#Lo
gging:_Log_Requests_and_Responses
38
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
FHIR Server in a boxhapi-fhir-cli
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Questions?
We will cover more tomorrow at HAPI Advanced Tutorial. Any requests?
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
HAPI-FHIR for Java Developers -
AdvancedJames Agnew
FHIR Developer DaysNovember 17, 2016
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Today’s Agenda
We will cover more tomorrow at HAPI Advanced Tutorial
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Validation
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Error checking vs Validation in HAPIError Checking during Parsing (learn more: hapifhir.io)
● Parser can be configured with an “Error Handler” which logs or fails on error
● Only catches structural issues
44
Validation
(our focus for today)
● Applies a complete set of rules to a resource instance
● Currently far more powerful
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Using Schema Validator1: public class Example12_ParserErrorHandler { 2: public static void main(String[] args) { 3: String input = "<Encounter xmlns=\"http://hl7.org/fhir\"><AAAA value=\"foo\"/></Encounter>"; 4: 5: IParser p = FhirContext.forDstu3().newXmlParser(); 6: 7: // Parse with (default) lenient error handler 8: p.setParserErrorHandler(new LenientErrorHandler()); 9: p.parseResource(input);10: 11: // Parse with strict error handler12: p.setParserErrorHandler(new StrictErrorHandler());13: p.parseResource(input);14: }15: }
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Parser Error Handling1: public class Example12_ParserErrorHandler { 2: public static void main(String[] args) { 3: String input = "<Encounter xmlns=\"http://hl7.org/fhir\"><AAAA value=\"foo\"/></Encounter>"; 4: 5: IParser p = FhirContext.forDstu3().newXmlParser(); 6: 7: // Parse with (default) lenient error handler 8: p.setParserErrorHandler(new LenientErrorHandler()); 9: p.parseResource(input);10: 11: // Parse with strict error handler12: p.setParserErrorHandler(new StrictErrorHandler());13: p.parseResource(input);14: }15: }
07:53:40.536 WARN c.u.fhir.parser.LenientErrorHandler Unknown element 'AAAA' found while parsing
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Parser Error Handling1: public class Example12_ParserErrorHandler { 2: public static void main(String[] args) { 3: String input = "<Encounter xmlns=\"http://hl7.org/fhir\"><AAAA value=\"foo\"/></Encounter>"; 4: 5: IParser p = FhirContext.forDstu3().newXmlParser(); 6: 7: // Parse with (default) lenient error handler 8: p.setParserErrorHandler(new LenientErrorHandler()); 9: p.parseResource(input);10: 11: // Parse with strict error handler12: p.setParserErrorHandler(new StrictErrorHandler());13: p.parseResource(input);14: }15: }
Exception in thread "main" ca.uhn.fhir.parser.DataFormatException: DataFormatException at [[row,col {unknown-source}]: [1,40]]: Unknown element 'AAAA' found during parse
at ca.uhn.fhir.parser.XmlParser.doXmlLoop(XmlParser.java:277)
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Validation in FHIR
Schema (XSD) and Schematron (SCH)
● Uses XSD+SCH files● Covers base FHIR
specification● XML validation only
(HAPI will convert)● Simpler to use
48
Profile Validation
● Uses FHIR resources:● StructureDefinition● ValueSet / CodeSystem
● Native JSON/XML support
● Profile validation● Slicing validation● Terminology
validation
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Validation in HAPI
● HAPI’s validator uses modules and collects the results from any that are enabled
● Create your own if you want!
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Schema/Schematron Validation
Dependencies:● hapi-fhir-validation-resources-dstu3-2.1.jar● phloc-schematron-2.7.0.jar
Inputs (pick one):● Resource Java model object● Raw JSON Resource● Raw XML Resource
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Using Schema Validator1: public class Example09_ValidateResource { 2: public static void main(String[] args) { 3: 4: // Create an incomplete encounter (status is required) 5: Encounter enc = new Encounter(); 6: enc.addIdentifier().setSystem("http://acme.org/encNums").setValue("12345"); 7: 8: // Create a new validator 9: FhirContext ctx = FhirContext.forDstu3();10: FhirValidator validator = ctx.newValidator();11: 12: // Did we succeed?13: ValidationResult result = validator.validateWithResult(enc);14: System.out.println("Success: " + result.isSuccessful());15: 16: // What was the result17: OperationOutcome outcome = (OperationOutcome) result.toOperationOutcome();18: IParser parser = ctx.newXmlParser().setPrettyPrint(true);19: System.out.println(parser.encodeResourceToString(outcome));20: }22: }
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Using Schema Validator1: public class Example09_ValidateResource { 2: public static void main(String[] args) { 3: 4: // Create an incomplete encounter (status is required) 5: Encounter enc = new Encounter(); 6: enc.addIdentifier().setSystem("http://acme.org/encNums").setValue("12345"); 7: 8: // Create a new validator 9: FhirContext ctx = FhirContext.forDstu3();10: FhirValidator validator = ctx.newValidator();11: 12: // Did we succeed?13: ValidationResult result = validator.validateWithResult(enc);14: System.out.println("Success: " + result.isSuccessful());15: 16: // What was the result17: OperationOutcome outcome = (OperationOutcome) result.toOperationOutcome();18: IParser parser = ctx.newXmlParser().setPrettyPrint(true);19: System.out.println(parser.encodeResourceToString(outcome));20: }22: }
<OperationOutcome xmlns="http://hl7.org/fhir"> <issue> <severity value="error"/> <code value="processing"/> <diagnostics value="cvc-complex-type.2.4.b: The content of element 'Encounter' is not complete. One of '{"http://hl7.org/fhir":identifier, "http://hl7.org/fhir":status}' is expected."/> <location value="Line[1] Col[140]"/> </issue></OperationOutcome>
false
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Using Schema Validator with String input
1: public class Example10_ValidateResourceString { 2: public static void main(String[] args) { 3: 4: String input = "<Encounter xmlns=\"http://hl7.org/fhir\"></Encounter>"; 5: 6: // Create a new validator 7: FhirContext ctx = FhirContext.forDstu3(); 8: FhirValidator validator = ctx.newValidator(); 9: 10: // Did we succeed?11: ValidationResult result = validator.validateWithResult(input);12: System.out.println("Success: " + result.isSuccessful());13: 14: // What was the result15: OperationOutcome outcome = (OperationOutcome) result.toOperationOutcome();16: IParser parser = ctx.newXmlParser().setPrettyPrint(true);17: System.out.println(parser.encodeResourceToString(outcome));18: }19: }
String is also fine as input
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Profile Validation
● FhirInstanceValidator module requires an instance of IValidationSupport
● Several implementations are supplied with HAPI FHIR
● You can also create your own
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Profile Validation
Use built-in FHIR definitions and terminology
Load your own definitions and terminology
Chain multiple providers together
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Profile Validation1: public class Example11_ValidateResourceInstanceValidator { 2: public static void main(String[] args) { 3: // Create an incomplete encounter (status is required) 4: Encounter enc = new Encounter(); 5: enc.addIdentifier().setSystem("http://acme.org/encNums").setValue("12345"); 6: 7: // Create a new validator 8: FhirValidator validator = FhirContext.forDstu3().newValidator(); 9: 10: // Cache this! Supplies structure definitions11: DefaultProfileValidationSupport support = new DefaultProfileValidationSupport();12: 13: // Create the validator14: FhirInstanceValidator module = new FhirInstanceValidator(support);15: validator.registerValidatorModule(module);16: 17: // Did we succeed?18: IParser parser = FhirContext.forDstu3().newXmlParser().setPrettyPrint(true);19: System.out.println(parser.encodeResourceToString(validator.validateWithResult(enc).toOperationOutcome()));20: }21: }
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Profile Validation1: public class Example11_ValidateResourceInstanceValidator { 2: public static void main(String[] args) { 3: // Create an incomplete encounter (status is required) 4: Encounter enc = new Encounter(); 5: enc.addIdentifier().setSystem("http://acme.org/encNums").setValue("12345"); 6: 7: // Create a new validator 8: FhirValidator validator = FhirContext.forDstu3().newValidator(); 9: 10: // Cache this! Supplies structure definitions11: DefaultProfileValidationSupport support = new DefaultProfileValidationSupport();12: 13: // Create the validator14: FhirInstanceValidator module = new FhirInstanceValidator(support);15: validator.registerValidatorModule(module);16: 17: // Did we succeed?18: IParser parser = FhirContext.forDstu3().newXmlParser().setPrettyPrint(true);19: System.out.println(parser.encodeResourceToString(validator.validateWithResult(enc).toOperationOutcome()));20: }21: }
13:25:41.843 INFO Loading structure definitions from classpath: /org/hl7/fhir/instance/model/dstu3/profile/profiles-resources.xml13:25:43.962 INFO Loading structure definitions from classpath: /org/hl7/fhir/instance/model/dstu3/profile/profiles-types.xml13:25:44.085 INFO Loading structure definitions from classpath: /org/hl7/fhir/instance/model/dstu3/profile/profiles-others.xml
<OperationOutcome xmlns="http://hl7.org/fhir"> <issue> <severity value="error"/> <code value="processing"/> <diagnostics value="Profile http://hl7.org/fhir/StructureDefinition/Encounter, Element 'Encounter.status': minimum required = 1, but only found 0"/> <location value="Encounter"/> </issue></OperationOutcome>
LoadingProfiles
Friendly Errors
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Creating a Server
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
FHIR Server: Architecture
● HAPI provides a REST Server framework● Based on standard JEE/Servlet 2.5+
(Tomcat, Glassfish, Websphere, JBoss, etc)● Inspired by (but not based on) JAX-RS, RestEasy,
Spring REST, etc.
59
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
FHIR Server: Architecture (2)● Architecture is based
on “Resource Providers” which are custom classes you write to interact with your resources
● This is a “low level” API for building servers, not an “off the shelf” solution
60
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
FHIR Server:Defining Resource Providers
● Resource Providers implement your server logic
61
1: public class Example01_StubResourceProvider implements IResourceProvider { 2: public Class<? extends IBaseResource> getResourceType() { 3: return Patient.class; 4: } 5: 6: @Read 7: public Patient read(@IdParam IdType theId) { return null; /* populate this */ }10: 11: @Create 12: void create(@ResourceParam Patient thePatient) { /* save the resource */ }15: 16: @Search17: List<Patient> search(18: @OptionalParam(name="family") StringParam theFamily,19: @OptionalParam(name="given") StringParam theGiven20: ) {21: return null; // populate this22: }23: }
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
● The following slides show a simple example building a FHIR server using HAPI
● Resources are stored in a HashMap (could just as easily be a database or something else!)
● These samples can be downloaded and executed on your laptop very easily
62
FHIR Server:A Simple Example
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
FHIR Server:A simple resource provider
63
1: public class Example02_PatientResourceProvider implements IResourceProvider { 2: 3: private Map<String, Patient> myPatients = new HashMap<String, Patient>(); 4: 5: /** Constructor */ 6: public Example02_PatientResourceProvider() { 7: Patient pat1 = new Patient(); 8: pat1.setId("1"); 9: pat1.addIdentifier().setSystem("http://acme.com/MRNs").setValue("7000135");10: pat1.addName().addFamily("Simpson").addGiven("Homer").addGiven("J");11: myPatients.put("1", pat1);12: }13: 14: /** Simple implementation of the "read" method */15: @Read(version=false)16: public Patient read(@IdParam IdType theId) {17: Patient retVal = myPatients.get(theId.getIdPart());18: if (retVal == null) {19: throw new ResourceNotFoundException(theId);20: }21: return retVal;22: }23: 24: @Override25: public Class<? extends IBaseResource> getResourceType() {26: return Patient.class;27: }28: 29: 30: }
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
FHIR Server:A simple server
64
1: @WebServlet("/*") 2: public class Example03_SimpleRestfulServer extends RestfulServer { 3: 4: @Override 5: protected void initialize() throws ServletException { 6: // Create a context for the appropriate version 7: setFhirContext(FhirContext.forDstu3()); 8: 9: // Register resource providers10: registerProvider(new Example02_PatientResourceProvider());11: }12: }
● The servlet is very simple: it creates an instance of each resource provider and declares the servlet path
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
● Console 1: Start Server
65
FHIR Server:Trying the server out
james$ mvn jetty:run[INFO] Scanning for projects...2014-11-21 12:27:37.622:WARN:oejsh.RequestLogHandler:main: !RequestLog2014-11-21 12:27:37.669:INFO:oejs.ServerConnector:main: Started ServerConnect[INFO] Started Jetty Server
● Console 2: Try it out!james$ curl “http://localhost:8080/Patient/1"<Patient xmlns="http://hl7.org/fhir"><identifier><system value="http://acme.com/MRNs"/><value value="7000135"/></identifier><name><family value="Simpson"/><given value="Homer"/><given value="J"/></name></Patient>
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
66
FHIR Server:Trying the server out (2)
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
67
1: public class Example04_PatientResourceProviderWithCreate implements IResourceProvider { 2: // ---- not all methods shown ---- 3: @Create 4: public MethodOutcome create(@ResourceParam Patient thePatient) { 5: // Give the resource the next sequential ID 6: int id = myNextId++; 7: thePatient.setId(new IdType(id)); 8: 9: // Store the resource in memory10: myPatients.put(Integer.toString(id), thePatient);11: 12: // Inform the server of the ID for the newly stored resource13: return new MethodOutcome().setId(thePatient.getIdElement());14: }15: 16: @Search17: public List<Patient> search() {18: List<Patient> retVal = new ArrayList<Patient>();19: retVal.addAll(myPatients.values());20: return retVal;21: }22: }
FHIR Server:Adding Create and Search
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
● The following command executes a ‘create’
68
curl -H "Content-Type: application/xml+fhir" \ -X POST \ -d '<Patient xmlns="http://hl7.org/fhir"><name><family value="Fireman"/><given value="John"/></name></Patient>' \ "http://localhost:8080/Patient"
FHIR Server:Testing out Create
curl "http://localhost:8080/Patient?_pretty=true"
[ … snip … ]<Bundle xmlns="http://hl7.org/fhir"> <type value="searchset"/> <total value="3"/>
● Now perform a search
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
69
FHIR Server:Adding Search Params
https://github.com/jamesagnew/hapi-fhir/tree/master/hapi-fhir-tutorial/simple-server
1: @Search 2: public List<Patient> search(@RequiredParam(name = Patient.SP_FAMILY) StringParam theParam) { 3: List<Patient> retVal = new ArrayList<Patient>(); 4: 5: // Loop through the patients looking for matches 6: for (Patient next : myPatients.values()) { 7: String familyName = next.getNameFirstRep().getFamilyAsSingleString().toLowerCase(); 8: if (familyName.contains(theParam.getValue().toLowerCase()) == false) { 9: continue;10: }11: retVal.add(next);12: }13: 14: return retVal;15: }
“family”
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
FHIR Server:Much more is available● Combining multiple parameters● Parameters for sorting, limiting, paging, etc.
70
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
HAPI FHIR JPA
We will cover more tomorrow at HAPI Advanced Tutorial
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
HAPI FHIR JPA:Introduction
● HAPI JPA Server is a complete FHIR Server implementation based on Hibernate & Hibernate Search
● This is the module that powers http://fhirtest.uhn.ca
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
HAPI FHIR JPA:Components
Implementation of most operations, search types, terminology svc, etc.
Database of your choosing
The same server from the previous section
Generated ResourceProviders for all resource types
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
HAPI FHIR JPA:Features
● Supports most of the basic FHIR HTTP specification (create, search, conditionals, etc)
● Supports many advanced features too (subscriptions, patch, etc)
● Includes full validator and terminology server
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
HAPI FHIR JPA:Databases
● HAPI examples typically use Apache Derby database● Derby is very convenient but not scalable
● HAPI is known to work with MySQL, Postgres, Oracle, SQL Server
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
HAPI FHIR JPA:Lucene
● HAPI uses Apache Lucene to provide two features:● Fulltext searching within resources (_text
and _content parameters)● Terminology Services
● Lucene stores its files on the filesystem● Lucene can be safely disabled
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Create your own JPA Server
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Trying out HAPI FHIR JPA
There is a complete sample project available here:
https://github.com/furore-fhir/fhirstarters/tree/master/java/hapi-fhirstarters-jpaserver-example
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Taking the Example for a Spin...
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
An Even Easier Way to Try HAPI FHIR JPA
HAPI-FHIR-CLI Bundles this
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Get Help!
● See our website for documentation:http://hapifhir.io
● We also have a Google Group / Mailing Listhttps://groups.google.com/d/forum/hapi-fhir
81
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Get Involved!
● HAPI is a large worldwide community of developers
● Today most are working on HL7 v2, but this is changing fast
● We are very grateful to the many people who have contributed so far, maybe you could be next?
82
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Thank You!
● [email protected]● jamesagnew214 on Skype
83
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Some bonus slides follow… :)
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
Extensions: The Easy Way● Every element has a collection of
“undeclared” extensions
85
public class Example10_Extensions {
public static void main(String[] args) {Patient pat = new Patient();pat.addName().addFamily("Simpson").addGiven("Homer");
String url = "http://acme.org#eyeColour";boolean isModifier = false;pat.addUndeclaredExtension(isModifier, url).setValue(new CodeDt(“blue"));
IParser p = new FhirContext().newXmlParser().setPrettyPrint(true);String encoded = p.encodeResourceToString(pat);
System.out.println(encoded);}
}
<Patient xmlns="http://hl7.org/fhir"> <extension url="http://acme.org#eyeColour"> <valueCode value="blue"/> </extension> <name> <family value="Simpson"/> <given value="Homer"/> </name></Patient>
© 2014 HL7 ® International. Licensed under Creative Commons. HL7 & Health Level Seven are registered trademarks of Health Level Seven International. Reg. U.S. TM Office.
● HAPI also provides a set of annotations for creating statically typed extensions
86
@ResourceDef(name="Patient")public class Example11_ExtendedPatient extends Patient {
@Child(name = "eyeColour")@Extension(url="http://acme.org/#extpt", definedLocally = false, isModifier = false)private CodeDt myEyeColour;
public CodeDt getEyeColour() {if (myEyeColour == null) {
myEyeColour = new CodeDt();}return myEyeColour;
}
public void setEyeColour(CodeDt theEyeColour) {myEyeColour = theEyeColour;
}
}
Extensions: The “Hard” Way