Allow read/vread/search using absolute URL - Thanks to Doug Martin for

the contribution
This commit is contained in:
James Agnew 2014-09-18 17:13:43 -04:00
parent 06e2b6632d
commit b67a4cc4e4
20 changed files with 2328 additions and 587 deletions

View File

@ -17,172 +17,179 @@ import ca.uhn.fhir.rest.method.SearchStyleEnum;
public class GenericClientExample {
public static void simpleExample() {
// START SNIPPET: simple
FhirContext ctx = new FhirContext();
String serverBase = "http://fhirtest.uhn.ca/base";
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
public static void simpleExample() {
// START SNIPPET: simple
FhirContext ctx = new FhirContext();
String serverBase = "http://fhirtest.uhn.ca/base";
IGenericClient client = ctx.newRestfulGenericClient(serverBase);
// Perform a search
Bundle results = client.search()
.forResource(Patient.class)
.where(Patient.FAMILY.matches().value("duck"))
.execute();
// Perform a search
Bundle results = client.search().forResource(Patient.class).where(Patient.FAMILY.matches().value("duck")).execute();
System.out.println("Found " + results.size() + " patients named 'duck'");
// END SNIPPET: simple
}
System.out.println("Found " + results.size() + " patients named 'duck'");
// END SNIPPET: simple
}
@SuppressWarnings("unused")
public static void fluentSearch() {
FhirContext ctx = new FhirContext();
IGenericClient client = ctx.newRestfulGenericClient("http://fhir.healthintersections.com.au/open");
{
//START SNIPPET: create
Patient patient = new Patient();
// ..populate the patient object..
patient.addIdentifier("urn:system", "12345");
patient.addName().addFamily("Smith").addGiven("John");
@SuppressWarnings("unused")
public static void fluentSearch() {
FhirContext ctx = new FhirContext();
IGenericClient client = ctx.newRestfulGenericClient("http://fhir.healthintersections.com.au/open");
{
// START SNIPPET: create
Patient patient = new Patient();
// ..populate the patient object..
patient.addIdentifier("urn:system", "12345");
patient.addName().addFamily("Smith").addGiven("John");
// Invoke the server create method (and send pretty-printed JSON encoding to the server
// instead of the default which is non-pretty printed XML)
client.create()
.resource(patient)
.prettyPrint()
.encodedJson()
.execute();
//END SNIPPET: create
}
{
//START SNIPPET: update
Patient patient = new Patient();
// ..populate the patient object..
patient.addIdentifier("urn:system", "12345");
patient.addName().addFamily("Smith").addGiven("John");
// Invoke the server create method (and send pretty-printed JSON
// encoding to the server
// instead of the default which is non-pretty printed XML)
client.create().resource(patient).prettyPrint().encodedJson().execute();
// END SNIPPET: create
}
{
// START SNIPPET: update
Patient patient = new Patient();
// ..populate the patient object..
patient.addIdentifier("urn:system", "12345");
patient.addName().addFamily("Smith").addGiven("John");
// To update a resource, it should have an ID set (if the resource object
// comes from the results of a previous read or search, it will already
// have one though)
patient.setId("Patient/123");
// To update a resource, it should have an ID set (if the resource
// object
// comes from the results of a previous read or search, it will already
// have one though)
patient.setId("Patient/123");
// Invoke the server create method (and send pretty-printed JSON encoding to the server
// instead of the default which is non-pretty printed XML)
client.update()
.resource(patient)
.execute();
//END SNIPPET: update
}
{
//START SNIPPET: conformance
// Retrieve the server's conformance statement and print its description
Conformance conf = client.conformance();
System.out.println(conf.getDescription().getValue());
//END SNIPPET: conformance
}
{
//START SNIPPET: delete
// Retrieve the server's conformance statement and print its description
OperationOutcome outcome = client.delete()
.resourceById(new IdDt("Patient", "1234"))
.execute();
// Invoke the server create method (and send pretty-printed JSON
// encoding to the server
// instead of the default which is non-pretty printed XML)
client.update().resource(patient).execute();
// END SNIPPET: update
}
{
// START SNIPPET: conformance
// Retrieve the server's conformance statement and print its
// description
Conformance conf = client.conformance();
System.out.println(conf.getDescription().getValue());
// END SNIPPET: conformance
}
{
// START SNIPPET: delete
// Retrieve the server's conformance statement and print its
// description
OperationOutcome outcome = client.delete().resourceById(new IdDt("Patient", "1234")).execute();
// outcome may be null if the server didn't return one
if (outcome != null) {
System.out.println(outcome.getIssueFirstRep().getDetails().getValue());
}
//END SNIPPET: delete
}
{
//START SNIPPET: search
Bundle response = client.search()
.forResource(Patient.class)
.where(Patient.BIRTHDATE.beforeOrEquals().day("2011-01-01"))
.and(Patient.PROVIDER.hasChainedProperty(Organization.NAME.matches().value("Health")))
.execute();
//END SNIPPET: search
// outcome may be null if the server didn't return one
if (outcome != null) {
System.out.println(outcome.getIssueFirstRep().getDetails().getValue());
}
// END SNIPPET: delete
}
{
// START SNIPPET: search
Bundle response = client.search()
.forResource(Patient.class)
.where(Patient.BIRTHDATE.beforeOrEquals().day("2011-01-01"))
.and(Patient.PROVIDER.hasChainedProperty(Organization.NAME.matches().value("Health")))
.execute();
// END SNIPPET: search
//START SNIPPET: searchOr
response = client.search()
.forResource(Patient.class)
.where(Patient.FAMILY.matches().values("Smith", "Smyth"))
.execute();
//END SNIPPET: searchOr
// START SNIPPET: searchOr
response = client.search()
.forResource(Patient.class)
.where(Patient.FAMILY.matches().values("Smith", "Smyth"))
.execute();
// END SNIPPET: searchOr
//START SNIPPET: searchAnd
response = client.search()
.forResource(Patient.class)
.where(Patient.ADDRESS.matches().values("Toronto"))
.where(Patient.ADDRESS.matches().values("Ontario"))
.where(Patient.ADDRESS.matches().values("Canada"))
.execute();
//END SNIPPET: searchAnd
// START SNIPPET: searchAnd
response = client.search()
.forResource(Patient.class)
.where(Patient.ADDRESS.matches().values("Toronto"))
.where(Patient.ADDRESS.matches().values("Ontario"))
.where(Patient.ADDRESS.matches().values("Canada"))
.execute();
// END SNIPPET: searchAnd
//START SNIPPET: searchCompartment
response = client.search()
.forResource(Patient.class)
.withIdAndCompartment("123", "condition")
.where(Patient.ADDRESS.matches().values("Toronto"))
.execute();
//END SNIPPET: searchCompartment
// START SNIPPET: searchCompartment
response = client.search()
.forResource(Patient.class)
.withIdAndCompartment("123", "condition")
.where(Patient.ADDRESS.matches().values("Toronto"))
.execute();
// END SNIPPET: searchCompartment
//START SNIPPET: searchAdv
response = client.search()
.forResource(Patient.class)
.encodedJson()
.where(Patient.BIRTHDATE.beforeOrEquals().day("2012-01-22"))
.and(Patient.BIRTHDATE.after().day("2011-01-01"))
.include(Patient.INCLUDE_MANAGINGORGANIZATION)
.sort().ascending(Patient.BIRTHDATE)
.sort().descending(Patient.NAME)
.limitTo(123)
.execute();
//END SNIPPET: searchAdv
// START SNIPPET: searchAdv
response = client.search()
.forResource(Patient.class)
.encodedJson()
.where(Patient.BIRTHDATE.beforeOrEquals().day("2012-01-22"))
.and(Patient.BIRTHDATE.after().day("2011-01-01"))
.include(Patient.INCLUDE_MANAGINGORGANIZATION)
.sort().ascending(Patient.BIRTHDATE)
.sort().descending(Patient.NAME).limitTo(123)
.execute();
// END SNIPPET: searchAdv
//START SNIPPET: searchPost
response = client.search()
.forResource("Patient")
.where(Patient.NAME.matches().value("Tester"))
.usingStyle(SearchStyleEnum.POST)
.execute();
//END SNIPPET: searchPost
// START SNIPPET: searchPost
response = client.search()
.forResource("Patient")
.where(Patient.NAME.matches().value("Tester"))
.usingStyle(SearchStyleEnum.POST)
.execute();
// END SNIPPET: searchPost
// START SNIPPET: searchComposite
response = client.search()
.forResource("Observation")
.where(Observation.NAME_VALUE_DATE
.withLeft(Observation.NAME.exactly().code("FOO$BAR"))
.withRight(Observation.VALUE_DATE.exactly().day("2001-01-01")))
.execute();
// END SNIPPET: searchComposite
//START SNIPPET: searchComposite
response = client.search()
.forResource("Observation")
.where(Observation.NAME_VALUE_DATE
.withLeft(Observation.NAME.exactly().code("FOO$BAR"))
.withRight(Observation.VALUE_DATE.exactly().day("2001-01-01"))
)
.execute();
//END SNIPPET: searchComposite
// START SNIPPET: searchPaging
if (response.getLinkNext().isEmpty() == false) {
//START SNIPPET: searchPaging
if (response.getLinkNext().isEmpty() == false) {
// load next page
Bundle nextPage = client.loadPage().next(response).execute();
}
// END SNIPPET: searchPaging
}
{
// START SNIPPET: transaction
List<IResource> resources = new ArrayList<IResource>();
// .. populate this list - note that you can also pass in a populated
// Bundle if you want to create one manually ..
// load next page
Bundle nextPage = client.loadPage()
.next(response)
.execute();
}
//END SNIPPET: searchPaging
}
{
//START SNIPPET: transaction
List<IResource> resources = new ArrayList<IResource>();
// .. populate this list - note that you can also pass in a populated Bundle if you want to create one manually ..
List<IResource> response = client.transaction().withResources(resources).execute();
// END SNIPPET: transaction
}
List<IResource> response = client.transaction()
.withResources(resources)
.execute();
//END SNIPPET: transaction
}
{
// START SNIPPET: read
IdDt id = new IdDt("Patient", "123");
Patient patient = client.read(Patient.class, id); // search for patient 123
// END SNIPPET: read
}
{
// START SNIPPET: vread
IdDt id = new IdDt("Patient", "123", "888");
Patient patient = client.vread(Patient.class, id); // search for version 888 of patient 123
// END SNIPPET: vread
}
{
// START SNIPPET: readabsolute
IdDt id = new IdDt("http://example.com/fhir/Patient/123");
Patient patient = client.read(Patient.class, id); // search for patient 123 on example.com
// END SNIPPET: readabsolute
}
}
public static void main(String[] args) {
simpleExample();
}
}
public static void main(String[] args) {
fluentSearch();
}
}

View File

@ -7,8 +7,21 @@
</properties>
<body>
<release version="0.7" date="TBD">
<action type="add">
Documentation update, thanks to Suranga Nath Kasthurirathne of the OpenMRS project.
<action type="add" dev="suranga">
Documentation fixes
</action>
<action type="add" dev="dougmartin">
Add a collection of new methods on the generic client which support the
<![CDATA[
<b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#read(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">read</a></b>,
<b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#vread(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">read</a></b>,
and <b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#search(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">search</a></b>
]]>
operations using an absolute URL. This allows developers to perform these operations using
URLs they obtained from other sources (or external resource references within resources). In
addition, the existing read/vread operations will now access absolute URL references if
they are passed in. Thanks to Doug Martin of the Regenstrief Center for Biomedical Informatics
for contributing this implementation!
</action>
<action type="fix">
Server implementation was not correctly figuring out its own FHIR Base URL when deployed

View File

@ -36,12 +36,11 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.util.UrlUtil;
/**
* Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs,
* Resource References, etc. to represent a specific instance of a resource.
* Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs, Resource References, etc. to represent a specific instance of a resource.
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any
* other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -50,6 +49,7 @@ import ca.uhn.fhir.util.UrlUtil;
@DatatypeDef(name = "id")
public class IdDt extends BasePrimitive<String> {
private String myBaseUrl;
private boolean myHaveComponentParts;
private String myResourceType;
private String myUnqualifiedId;
@ -64,8 +64,7 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string
* representation.
* Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string representation.
*/
public IdDt(BigDecimal thePid) {
if (thePid != null) {
@ -83,12 +82,11 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Create a new ID using a string. This String may contain a simple ID (e.g. "1234") or it may contain a complete
* URL (http://example.com/fhir/Patient/1234).
* Create a new ID using a string. This String may contain a simple ID (e.g. "1234") or it may contain a complete URL (http://example.com/fhir/Patient/1234).
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -141,8 +139,14 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was deprocated because its name is
* ambiguous)
* Creates an ID based on a given URL
*/
public IdDt(UriDt theUrl) {
setValue(theUrl.getValueAsString());
}
/**
* @deprecated Use {@link #getIdPartAsBigDecimal()} instead (this method was deprocated because its name is ambiguous)
*/
public BigDecimal asBigDecimal() {
return getIdPartAsBigDecimal();
@ -162,6 +166,17 @@ public class IdDt extends BasePrimitive<String> {
return ObjectUtils.equals(getResourceType(), theId.getResourceType()) && ObjectUtils.equals(getIdPart(), theId.getIdPart()) && ObjectUtils.equals(getVersionIdPart(), theId.getVersionIdPart());
}
/**
* Returns the portion of this resource ID which corresponds to the server base URL. For example given the resource ID <code>http://example.com/fhir/Patient/123</code> the base URL would be
* <code>http://example.com/fhir</code>.
* <p>
* This method may return null if the ID contains no base (e.g. "Patient/123")
* </p>
*/
public String getBaseUrl() {
return myBaseUrl;
}
public String getIdPart() {
return myUnqualifiedId;
}
@ -199,8 +214,7 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a
* simple ID. Use {@link #getIdPart()} to get just the ID portion.
* Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a simple ID. Use {@link #getIdPart()} to get just the ID portion.
*
* @see #getIdPart()
*/
@ -241,6 +255,15 @@ public class IdDt extends BasePrimitive<String> {
}
}
/**
* Returns true if this ID has a base url
*
* @see #getBaseUrl()
*/
public boolean hasBaseUrl() {
return isNotBlank(myBaseUrl);
}
public boolean hasIdPart() {
return isNotBlank(getIdPart());
}
@ -254,8 +277,7 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Returns <code>true</code> if this ID contains an absolute URL (in other words, a URL starting with "http://" or
* "https://"
* Returns <code>true</code> if this ID contains an absolute URL (in other words, a URL starting with "http://" or "https://"
*/
public boolean isAbsolute() {
if (StringUtils.isBlank(getValue())) {
@ -265,8 +287,7 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only
* of digits)
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only of digits)
*/
public boolean isIdPartValidLong() {
String id = getIdPart();
@ -289,8 +310,7 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method
* but it is provided for consistency with the rest of the API.
* Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API.
*/
@Override
public void setId(IdDt theId) {
@ -301,8 +321,8 @@ public class IdDt extends BasePrimitive<String> {
* Set the value
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -331,6 +351,7 @@ public class IdDt extends BasePrimitive<String> {
myUnqualifiedVersionId = null;
}
myBaseUrl = null;
if (idIndex <= 0) {
myResourceType = null;
} else {
@ -339,6 +360,11 @@ public class IdDt extends BasePrimitive<String> {
myResourceType = theValue.substring(0, idIndex);
} else {
myResourceType = theValue.substring(typeIndex + 1, idIndex);
if (typeIndex > 4) {
myBaseUrl = theValue.substring(0, typeIndex);
}
}
}
@ -349,8 +375,8 @@ public class IdDt extends BasePrimitive<String> {
* Set the value
*
* <p>
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or
* any other combination of lowercase letters, numerals, "-" and ".", with a length limit of 36 characters.
* <b>Description</b>: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length
* limit of 36 characters.
* </p>
* <p>
* regex: [a-z0-9\-\.]{1,36}
@ -389,10 +415,8 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Returns a view of this ID as a fully qualified URL, given a server base and resource name (which will only be
* used if the ID does not already contain those respective parts). Essentially, because IdDt can contain either a
* complete URL or a partial one (or even jut a simple ID), this method may be used to translate into a complete
* URL.
* Returns a view of this ID as a fully qualified URL, given a server base and resource name (which will only be used if the ID does not already contain those respective parts). Essentially,
* because IdDt can contain either a complete URL or a partial one (or even jut a simple ID), this method may be used to translate into a complete URL.
*
* @param theServerBase
* The server base (e.g. "http://example.com/fhir")
@ -428,13 +452,11 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Creates a new instance of this ID which is identical, but refers to the specific version of this resource ID
* noted by theVersion.
* Creates a new instance of this ID which is identical, but refers to the specific version of this resource ID noted by theVersion.
*
* @param theVersion
* The actual version string, e.g. "1"
* @return A new instance of IdDt which is identical, but refers to the specific version of this resource ID noted
* by theVersion.
* @return A new instance of IdDt which is identical, but refers to the specific version of this resource ID noted by theVersion.
*/
public IdDt withVersion(String theVersion) {
Validate.notBlank(theVersion, "Version may not be null or empty");

View File

@ -32,13 +32,10 @@ import java.util.List;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import org.apache.commons.lang3.StringEscapeUtils;
import org.codehaus.stax2.XMLOutputFactory2;
import org.codehaus.stax2.io.EscapingWriterFactory;
import ca.uhn.fhir.context.ConfigurationException;
@ -46,6 +43,7 @@ import ca.uhn.fhir.model.api.BasePrimitive;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.util.XmlUtil;
@DatatypeDef(name = "xhtml")
public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
@ -94,7 +92,8 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
try {
ArrayList<XMLEvent> value = new ArrayList<XMLEvent>();
XMLEventReader er = XMLInputFactory.newInstance().createXMLEventReader(new StringReader(val));
StringReader reader = new StringReader(val);
XMLEventReader er = XmlUtil.createXmlReader(reader);
boolean first = true;
while (er.hasNext()) {
XMLEvent next = er.nextEvent();
@ -123,11 +122,7 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
}
try {
StringWriter w = new StringWriter();
XMLOutputFactory newInstance = XMLOutputFactory.newInstance();
newInstance.setProperty(XMLOutputFactory2.P_TEXT_ESCAPER, new MyEscaper());
XMLEventWriter ew = newInstance.createXMLEventWriter(w);
XMLEventWriter ew = XmlUtil.createXmlWriter(w);
for (XMLEvent next : myValue) {
if (next.isCharacters()) {
ew.add(next);
@ -144,55 +139,6 @@ public class XhtmlDt extends BasePrimitive<List<XMLEvent>> {
}
}
private static class MyEscaper implements EscapingWriterFactory {
@Override
public Writer createEscapingWriterFor(final Writer theW, String theEnc) throws UnsupportedEncodingException {
return new Writer() {
@Override
public void write(char[] theCbuf, int theOff, int theLen) throws IOException {
boolean hasEscapable = false;
for (int i = 0; i < theLen && !hasEscapable; i++) {
char nextChar = theCbuf[i + theOff];
switch (nextChar) {
case '<':
case '>':
case '"':
case '&':
hasEscapable = true;
}
}
if (!hasEscapable) {
theW.write(theCbuf, theOff, theLen);
return;
}
String escaped = StringEscapeUtils.escapeXml10(new String(theCbuf, theOff, theLen));
theW.write(escaped.toCharArray());
}
@Override
public void flush() throws IOException {
theW.flush();
}
@Override
public void close() throws IOException {
theW.close();
}
};
}
@Override
public Writer createEscapingWriterFor(OutputStream theOut, String theEnc) throws UnsupportedEncodingException {
// TODO Auto-generated method stub
return null;
}
}
@Override
public List<XMLEvent> getValue() {
return myValue;

View File

@ -20,7 +20,9 @@ package ca.uhn.fhir.parser;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.IOException;
import java.io.Reader;
@ -76,6 +78,7 @@ import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.util.NonPrettyPrintWriterWrapper;
import ca.uhn.fhir.util.PrettyPrintWriterWrapper;
import ca.uhn.fhir.util.XmlUtil;
public class XmlParser extends BaseParser implements IParser {
static final String RESREF_DISPLAY = "display";
@ -91,14 +94,10 @@ public class XmlParser extends BaseParser implements IParser {
private FhirContext myContext;
private boolean myPrettyPrint;
private XMLInputFactory myXmlInputFactory;
private XMLOutputFactory myXmlOutputFactory;
public XmlParser(FhirContext theContext) {
super(theContext);
myContext = theContext;
myXmlInputFactory = XMLInputFactory.newInstance();
myXmlOutputFactory = XMLOutputFactory.newInstance();
}
@Override
@ -264,7 +263,7 @@ public class XmlParser extends BaseParser implements IParser {
private XMLStreamWriter createXmlWriter(Writer theWriter) throws XMLStreamException {
XMLStreamWriter eventWriter;
eventWriter = myXmlOutputFactory.createXMLStreamWriter(theWriter);
eventWriter = XmlUtil.createXmlStreamWriter(theWriter);
eventWriter = decorateStreamWriter(eventWriter);
return eventWriter;
}
@ -329,15 +328,23 @@ public class XmlParser extends BaseParser implements IParser {
}
private XMLEventReader createStreamReader(Reader theReader) {
XMLEventReader streamReader;
try {
streamReader = myXmlInputFactory.createXMLEventReader(theReader);
} catch (XMLStreamException e) {
throw new DataFormatException(e);
} catch (FactoryConfigurationError e) {
throw new ConfigurationException("Failed to initialize STaX event factory", e);
return XmlUtil.createXmlReader(theReader);
} catch (FactoryConfigurationError e1) {
throw new ConfigurationException("Failed to initialize STaX event factory", e1);
} catch (XMLStreamException e1) {
throw new DataFormatException(e1);
}
return streamReader;
// XMLEventReader streamReader;
// try {
// streamReader = myXmlInputFactory.createXMLEventReader(theReader);
// } catch (XMLStreamException e) {
// throw new DataFormatException(e);
// } catch (FactoryConfigurationError e) {
// throw new ConfigurationException("Failed to initialize STaX event factory", e);
// }
// return streamReader;
}
private XMLStreamWriter decorateStreamWriter(XMLStreamWriter eventWriter) {

View File

@ -20,7 +20,8 @@ package ca.uhn.fhir.rest.client;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.IOException;
import java.io.Reader;
@ -47,6 +48,7 @@ import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome;
@ -73,7 +75,6 @@ import ca.uhn.fhir.rest.method.DeleteMethodBinding;
import ca.uhn.fhir.rest.method.HistoryMethodBinding;
import ca.uhn.fhir.rest.method.HttpDeleteClientInvocation;
import ca.uhn.fhir.rest.method.HttpGetClientInvocation;
import ca.uhn.fhir.rest.method.HttpPostClientInvocation;
import ca.uhn.fhir.rest.method.HttpSimpleGetClientInvocation;
import ca.uhn.fhir.rest.method.IClientResponseHandler;
import ca.uhn.fhir.rest.method.MethodUtil;
@ -87,8 +88,16 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
/**
* @author James Agnew
* @author Doug Martin (Regenstrief Center for Biomedical Informatics)
*/
public class GenericClient extends BaseClient implements IGenericClient {
private static final String I18N_CANNOT_DETEMINE_RESOURCE_TYPE = "ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri";
private static final String I18N_INCOMPLETE_URI_FOR_READ = "ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead";
private static final String I18N_NO_VERSION_ID_FOR_VREAD = "ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClient.class);
private FhirContext myContext;
@ -144,6 +153,19 @@ public class GenericClient extends BaseClient implements IGenericClient {
return new DeleteInternal();
}
// public IResource read(UriDt url) {
// return read(inferResourceClass(url), url);
// }
//
// @SuppressWarnings("unchecked")
// public <T extends IResource> T read(final Class<T> theType, UriDt url) {
// return (T) invoke(theType, url, new ResourceResponseHandler<T>(theType));
// }
//
// public Bundle search(UriDt url) {
// return search(inferResourceClass(url), url);
// }
@Override
public MethodOutcome delete(final Class<? extends IResource> theType, IdDt theId) {
HttpDeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(theId.withResourceType(toResourceName(theType)));
@ -166,6 +188,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
return myLastRequest;
}
protected String getPreferredId(IResource theResource, String theId) {
if (isNotBlank(theId)) {
return theId;
}
return theResource.getId().getIdPart();
}
@Override
public IGetTags getTags() {
return new GetTagsInternal();
@ -206,7 +235,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
throw new IllegalArgumentException("theId does not contain a valid ID, is: " + theId);
}
HttpGetClientInvocation invocation = ReadMethodBinding.createReadInvocation(theId, toResourceName(theType));
HttpGetClientInvocation invocation;
if (theId.hasBaseUrl()) {
invocation = ReadMethodBinding.createAbsoluteReadInvocation(theId.toVersionless());
} else {
invocation = ReadMethodBinding.createReadInvocation(theId, toResourceName(theType));
}
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
@ -221,6 +255,25 @@ public class GenericClient extends BaseClient implements IGenericClient {
return read(theType, new IdDt(theId));
}
@Override
public <T extends IResource> T read(final Class<T> theType, UriDt theUrl) {
return read(theType, new IdDt(theUrl));
}
@Override
public IResource read(UriDt theUrl) {
IdDt id = new IdDt(theUrl);
String resourceType = id.getResourceType();
if (isBlank(resourceType)) {
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_INCOMPLETE_URI_FOR_READ, theUrl.getValueAsString()));
}
RuntimeResourceDefinition def = myContext.getResourceDefinition(resourceType);
if (def == null) {
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString()));
}
return read(def.getImplementingClass(), id);
}
@Override
public IUntypedQuery search() {
return new SearchInternal();
@ -250,6 +303,48 @@ public class GenericClient extends BaseClient implements IGenericClient {
return resp;
}
@Override
public <T extends IResource> Bundle search(final Class<T> theType, UriDt theUrl) {
BaseHttpClientInvocation invocation = new HttpGetClientInvocation(theUrl.getValueAsString());
return invokeClient(myContext, new BundleResponseHandler(theType), invocation);
}
@Override
public Bundle search(UriDt theUrl) {
return search(inferResourceClass(theUrl), theUrl);
}
private Class<? extends IResource> inferResourceClass(UriDt theUrl) {
String urlString = theUrl.getValueAsString();
int i = urlString.indexOf('?');
if (i >= 0) {
urlString = urlString.substring(0, i);
}
i = urlString.indexOf("://");
if (i >= 0) {
urlString = urlString.substring(i + 3);
}
String[] pcs = urlString.split("\\/");
for (i = pcs.length - 1; i >= 0; i--) {
String s = pcs[i].trim();
if (!s.isEmpty()) {
RuntimeResourceDefinition def = myContext.getResourceDefinition(s);
if (def != null) {
return def.getImplementingClass();
}
}
}
throw new RuntimeException(myContext.getLocalizer().getMessage(I18N_CANNOT_DETEMINE_RESOURCE_TYPE, theUrl.getValueAsString()));
}
/**
* For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
*/
@ -262,6 +357,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
myLogRequestAndResponse = theLogRequestAndResponse;
}
private String toResourceName(Class<? extends IResource> theType) {
return myContext.getResourceDefinition(theType).getName();
}
@Override
public ITransaction transaction() {
return new TransactionInternal();
@ -279,6 +378,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
return resp.toListOfResources();
}
@Override
public IUpdate update() {
return new UpdateInternal();
}
@Override
public MethodOutcome update(IdDt theIdDt, IResource theResource) {
BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(theResource, null, theIdDt, myContext);
@ -315,24 +419,43 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public <T extends IResource> T vread(final Class<T> theType, IdDt theId, IdDt theVersionId) {
HttpGetClientInvocation invocation = ReadMethodBinding.createVReadInvocation(theId, theVersionId, toResourceName(theType));
public <T extends IResource> T vread(final Class<T> theType, IdDt theId) {
String resName = toResourceName(theType);
IdDt id = theId;
if (!id.hasBaseUrl()) {
id = new IdDt(resName, id.getIdPart(), id.getVersionIdPart());
}
if (id.hasVersionIdPart() == false) {
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_NO_VERSION_ID_FOR_VREAD, theId.getValue()));
}
HttpGetClientInvocation invocation;
if (id.hasBaseUrl()) {
invocation = ReadMethodBinding.createAbsoluteVReadInvocation(id);
} else {
invocation = ReadMethodBinding.createVReadInvocation(id);
}
if (isKeepResponses()) {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
}
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType, theId);
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType, id);
T resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse);
return resp;
}
/* also deprecated in interface */
@Deprecated
@Override
public <T extends IResource> T vread(Class<T> theType, String theId, String theVersionId) {
return vread(theType, new IdDt(theId), new IdDt(theVersionId));
public <T extends IResource> T vread(final Class<T> theType, IdDt theId, IdDt theVersionId) {
return vread(theType, theId.withVersion(theVersionId.getIdPart()));
}
private String toResourceName(Class<? extends IResource> theType) {
return myContext.getResourceDefinition(theType).getName();
@Override
public <T extends IResource> T vread(Class<T> theType, String theId, String theVersionId) {
IdDt resId = new IdDt(toResourceName(theType), theId, theVersionId);
return vread(theType, resId);
}
private abstract class BaseClientExecutable<T extends IClientExecutable<?, ?>, Y> implements IClientExecutable<T, Y> {
@ -340,6 +463,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
private Boolean myPrettyPrint;
private boolean myQueryLogRequestAndResponse;
protected void addParam(Map<String, List<String>> params, String parameterName, String parameterValue) {
if (!params.containsKey(parameterName)) {
params.put(parameterName, new ArrayList<String>());
}
params.get(parameterName).add(parameterValue);
}
@SuppressWarnings("unchecked")
@Override
public T andLogRequestAndResponse(boolean theLogRequestAndResponse) {
@ -347,6 +477,37 @@ public class GenericClient extends BaseClient implements IGenericClient {
return (T) this;
}
@SuppressWarnings("unchecked")
@Override
public T encodedJson() {
myParamEncoding = EncodingEnum.JSON;
return (T) this;
}
@SuppressWarnings("unchecked")
@Override
public T encodedXml() {
myParamEncoding = EncodingEnum.XML;
return (T) this;
}
protected <Z> Z invoke(Map<String, List<String>> theParams, IClientResponseHandler<Z> theHandler, BaseHttpClientInvocation theInvocation) {
// if (myParamEncoding != null) {
// theParams.put(Constants.PARAM_FORMAT, Collections.singletonList(myParamEncoding.getFormatContentType()));
// }
//
// if (myPrettyPrint != null) {
// theParams.put(Constants.PARAM_PRETTY, Collections.singletonList(myPrettyPrint.toString()));
// }
if (isKeepResponses()) {
myLastRequest = theInvocation.asHttpRequest(getServerBase(), theParams, getEncoding());
}
Z resp = invokeClient(myContext, theHandler, theInvocation, myParamEncoding, myPrettyPrint, myQueryLogRequestAndResponse || myLogRequestAndResponse);
return resp;
}
protected IResource parseResourceBody(String theResourceBody) {
EncodingEnum encoding = null;
for (int i = 0; i < theResourceBody.length() && encoding == null; i++) {
@ -365,20 +526,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
return encoding.newParser(myContext).parseResource(theResourceBody);
}
@SuppressWarnings("unchecked")
@Override
public T encodedJson() {
myParamEncoding = EncodingEnum.JSON;
return (T) this;
}
@SuppressWarnings("unchecked")
@Override
public T encodedXml() {
myParamEncoding = EncodingEnum.XML;
return (T) this;
}
@SuppressWarnings("unchecked")
@Override
public T prettyPrint() {
@ -386,30 +533,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
return (T) this;
}
protected void addParam(Map<String, List<String>> params, String parameterName, String parameterValue) {
if (!params.containsKey(parameterName)) {
params.put(parameterName, new ArrayList<String>());
}
params.get(parameterName).add(parameterValue);
}
protected <Z> Z invoke(Map<String, List<String>> theParams, IClientResponseHandler<Z> theHandler, BaseHttpClientInvocation theInvocation) {
// if (myParamEncoding != null) {
// theParams.put(Constants.PARAM_FORMAT, Collections.singletonList(myParamEncoding.getFormatContentType()));
// }
//
// if (myPrettyPrint != null) {
// theParams.put(Constants.PARAM_PRETTY, Collections.singletonList(myPrettyPrint.toString()));
// }
if (isKeepResponses()) {
myLastRequest = theInvocation.asHttpRequest(getServerBase(), theParams, getEncoding());
}
Z resp = invokeClient(myContext, theHandler, theInvocation, myParamEncoding, myPrettyPrint, myQueryLogRequestAndResponse || myLogRequestAndResponse);
return resp;
}
}
private final class BundleResponseHandler implements IClientResponseHandler<Bundle> {
@ -421,7 +544,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
public Bundle invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
@ -484,76 +608,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
private class UpdateInternal extends BaseClientExecutable<IUpdateTyped, MethodOutcome> implements IUpdate, IUpdateTyped {
private IdDt myId;
private String myResourceBody;
private IResource myResource;
@Override
public MethodOutcome execute() {
if (myResource == null) {
myResource = parseResourceBody(myResourceBody);
}
if (myId == null) {
myId = myResource.getId();
}
if (myId == null || myId.hasIdPart() == false) {
throw new InvalidRequestException("No ID supplied for resource to update, can not invoke server");
}
BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext);
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
final String resourceName = def.getName();
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
Map<String, List<String>> params = new HashMap<String, List<String>>();
return invoke(params, binding, invocation);
}
@Override
public IUpdateTyped resource(IResource theResource) {
Validate.notNull(theResource, "Resource can not be null");
myResource = theResource;
return this;
}
@Override
public IUpdateTyped resource(String theResourceBody) {
Validate.notBlank(theResourceBody, "Body can not be null or blank");
myResourceBody = theResourceBody;
return this;
}
@Override
public IUpdateTyped withId(IdDt theId) {
if (theId == null) {
throw new NullPointerException("theId can not be null");
}
if (theId.hasIdPart() == false) {
throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId.getValue());
}
myId = theId;
return this;
}
@Override
public IUpdateTyped withId(String theId) {
if (theId == null) {
throw new NullPointerException("theId can not be null");
}
if (isBlank(theId)) {
throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId);
}
myId = new IdDt(theId);
return this;
}
}
private class DeleteInternal extends BaseClientExecutable<IDeleteTyped, OperationOutcome> implements IDelete, IDeleteTyped {
private IdDt myId;
@ -712,7 +766,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
private final class OperationOutcomeResponseHandler implements IClientResponseHandler<OperationOutcome> {
@Override
public OperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
public OperationOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
return null;
@ -739,7 +794,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
MethodOutcome response = MethodUtil.process2xxResponse(myContext, myResourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
if (theResponseStatusCode == Constants.STATUS_HTTP_201_CREATED) {
response.setCreated(true);
@ -757,7 +813,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public List<IResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
public List<IResource> invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
return new BundleResponseHandler(myType).invokeClient(theResponseMimeType, theResponseReader, theResponseStatusCode, theHeaders).toListOfResources();
}
}
@ -793,15 +850,15 @@ public class GenericClient extends BaseClient implements IGenericClient {
private class SearchInternal extends BaseClientExecutable<IQuery, Bundle> implements IQuery, IUntypedQuery {
private String myCompartmentName;
private List<ICriterionInternal> myCriterion = new ArrayList<ICriterionInternal>();
private List<Include> myInclude = new ArrayList<Include>();
private Integer myParamLimit;
private String myResourceId;
private String myResourceName;
private Class<? extends IResource> myResourceType;
private List<SortInternal> mySort = new ArrayList<SortInternal>();
private SearchStyleEnum mySearchStyle;
private String myResourceId;
private String myCompartmentName;
private List<SortInternal> mySort = new ArrayList<SortInternal>();
public SearchInternal() {
myResourceType = null;
@ -843,10 +900,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
BundleResponseHandler binding = new BundleResponseHandler(myResourceType);
IdDt resourceId = myResourceId != null? new IdDt(myResourceId ) : null;
IdDt resourceId = myResourceId != null ? new IdDt(myResourceId) : null;
BaseHttpClientInvocation invocation = SearchMethodBinding.createSearchInvocation(myContext, myResourceName, params, resourceId, myCompartmentName, mySearchStyle);
return invoke(params, binding, invocation);
}
@ -856,17 +913,6 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
private void setType(Class<? extends IResource> theResourceType) {
myResourceType = theResourceType;
RuntimeResourceDefinition definition = myContext.getResourceDefinition(theResourceType);
myResourceName = definition.getName();
}
private void setType(String theResourceName) {
myResourceType = myContext.getResourceDefinition(theResourceName).getImplementingClass();
myResourceName = theResourceName;
}
@Override
public IQuery forResource(Class<? extends IResource> theResourceType) {
setType(theResourceType);
@ -895,6 +941,17 @@ public class GenericClient extends BaseClient implements IGenericClient {
return this;
}
private void setType(Class<? extends IResource> theResourceType) {
myResourceType = theResourceType;
RuntimeResourceDefinition definition = myContext.getResourceDefinition(theResourceType);
myResourceName = definition.getName();
}
private void setType(String theResourceName) {
myResourceType = myContext.getResourceDefinition(theResourceName).getImplementingClass();
myResourceName = theResourceName;
}
@Override
public ISort sort() {
SortInternal retVal = new SortInternal(this);
@ -903,14 +960,14 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
@Override
public IQuery where(ICriterion<?> theCriterion) {
myCriterion.add((ICriterionInternal) theCriterion);
public IQuery usingStyle(SearchStyleEnum theStyle) {
mySearchStyle = theStyle;
return this;
}
@Override
public IQuery usingStyle(SearchStyleEnum theStyle) {
mySearchStyle = theStyle;
public IQuery where(ICriterion<?> theCriterion) {
myCriterion.add((ICriterionInternal) theCriterion);
return this;
}
@ -967,7 +1024,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
private final class TagListResponseHandler implements IClientResponseHandler<TagList> {
@Override
public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
public TagList invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
if (respType == null) {
throw NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
@ -1022,16 +1080,74 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
protected String getPreferredId(IResource theResource, String theId) {
if (isNotBlank(theId)) {
return theId;
}
return theResource.getId().getIdPart();
}
private class UpdateInternal extends BaseClientExecutable<IUpdateTyped, MethodOutcome> implements IUpdate, IUpdateTyped {
private IdDt myId;
private IResource myResource;
private String myResourceBody;
@Override
public MethodOutcome execute() {
if (myResource == null) {
myResource = parseResourceBody(myResourceBody);
}
if (myId == null) {
myId = myResource.getId();
}
if (myId == null || myId.hasIdPart() == false) {
throw new InvalidRequestException("No ID supplied for resource to update, can not invoke server");
}
BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(myResource, myResourceBody, myId, myContext);
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
final String resourceName = def.getName();
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
Map<String, List<String>> params = new HashMap<String, List<String>>();
return invoke(params, binding, invocation);
}
@Override
public IUpdateTyped resource(IResource theResource) {
Validate.notNull(theResource, "Resource can not be null");
myResource = theResource;
return this;
}
@Override
public IUpdateTyped resource(String theResourceBody) {
Validate.notBlank(theResourceBody, "Body can not be null or blank");
myResourceBody = theResourceBody;
return this;
}
@Override
public IUpdateTyped withId(IdDt theId) {
if (theId == null) {
throw new NullPointerException("theId can not be null");
}
if (theId.hasIdPart() == false) {
throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId.getValue());
}
myId = theId;
return this;
}
@Override
public IUpdateTyped withId(String theId) {
if (theId == null) {
throw new NullPointerException("theId can not be null");
}
if (isBlank(theId)) {
throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId);
}
myId = new IdDt(theId);
return this;
}
@Override
public IUpdate update() {
return new UpdateInternal();
}
}

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.gclient.ICreate;
@ -40,22 +41,16 @@ import ca.uhn.fhir.rest.gclient.IUntypedQuery;
import ca.uhn.fhir.rest.gclient.IUpdate;
public interface IGenericClient {
/**
* Register a new interceptor for this client. An interceptor can be used to add additional
* logging, or add security headers, or pre-process responses, etc.
*/
void registerInterceptor(IClientInterceptor theInterceptor);
/**
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}
*/
void unregisterInterceptor(IClientInterceptor theInterceptor);
/**
* Retrieves and returns the server conformance statement
*/
Conformance conformance();
/**
* Fluent method for the "create" operation, which creates a new resource instance on the server
*/
ICreate create();
/**
* Implementation of the "type create" method.
*
@ -68,16 +63,9 @@ public interface IGenericClient {
MethodOutcome create(IResource theResource);
/**
* Implementation of the "transaction" method.
*
* @param theResources
* The resources to create/update in a single transaction
* @return A list of resource stubs (<b>these will not be fully populated</b>) containing IDs and other
* {@link IResource#getResourceMetadata() metadata}
* @deprecated Use {@link #transaction()}
*
* Fluent method for the "delete" operation, which performs a logical delete on a server resource
*/
List<IResource> transaction(List<IResource> theResources);
IDelete delete();
/**
* Implementation of the "delete instance" method.
@ -103,21 +91,24 @@ public interface IGenericClient {
*/
MethodOutcome delete(Class<? extends IResource> theType, String theId);
/**
* Fluent method for the "get tags" operation
*/
IGetTags getTags();
/**
* Implementation of the "history instance" method.
*
* @param theType
* The type of resource to return the history for, or
* <code>null<code> to search for history across all resources
* The type of resource to return the history for, or <code>null<code> to search for history across all resources
* @param theId
* The ID of the resource to return the history for, or <code>null</code> to search for all resource
* instances. Note that if this param is not null, <code>theType</code> must also not be null
* The ID of the resource to return the history for, or <code>null</code> to search for all resource instances. Note that if this param is not null, <code>theType</code> must also not
* be null
* @param theSince
* If not null, request that the server only return resources updated since this time
* @param theLimit
* If not null, request that the server return no more than this number of resources. Note that the
* server may return less even if more are available, but should not return more according to the FHIR
* specification.
* If not null, request that the server return no more than this number of resources. Note that the server may return less even if more are available, but should not return more
* according to the FHIR specification.
* @return A bundle containing returned resources
*/
<T extends IResource> Bundle history(Class<T> theType, IdDt theIdDt, DateTimeDt theSince, Integer theLimit);
@ -126,28 +117,38 @@ public interface IGenericClient {
* Implementation of the "history instance" method.
*
* @param theType
* The type of resource to return the history for, or
* <code>null<code> to search for history across all resources
* The type of resource to return the history for, or <code>null<code> to search for history across all resources
* @param theId
* The ID of the resource to return the history for, or <code>null</code> to search for all resource
* instances. Note that if this param is not null, <code>theType</code> must also not be null
* The ID of the resource to return the history for, or <code>null</code> to search for all resource instances. Note that if this param is not null, <code>theType</code> must also not
* be null
* @param theSince
* If not null, request that the server only return resources updated since this time
* @param theLimit
* If not null, request that the server return no more than this number of resources. Note that the
* server may return less even if more are available, but should not return more according to the FHIR
* specification.
* If not null, request that the server return no more than this number of resources. Note that the server may return less even if more are available, but should not return more
* according to the FHIR specification.
* @return A bundle containing returned resources
*/
<T extends IResource> Bundle history(Class<T> theType, String theIdDt, DateTimeDt theSince, Integer theLimit);
/**
* Implementation of the "instance read" method.
* Loads the previous/next bundle of resources from a paged set, using the link specified in the "link type=next" tag within the atom bundle.
*
* @see Bundle#getLinkNext()
*/
IGetPage loadPage();
/**
* Implementation of the "instance read" method. This method will only ever do a "read" for the latest version of a given resource instance, even if the ID passed in contains a version. If you
* wish to request a specific version of a resource (the "vread" operation), use {@link #vread(Class, IdDt)} instead.
* <p>
* Note that if an absolute resource ID is passed in (i.e. a URL containing a protocol and host as well as the resource type and ID) the server base for the client will be ignored, and the URL
* passed in will be queried.
* </p>
*
* @param theType
* The type of resource to load
* @param theId
* The ID to load
* The ID to load, including the resource ID and the resource version ID. Valid values include "Patient/123/_history/222", or "http://example.com/fhir/Patient/123/_history/222"
* @return The resource
*/
<T extends IResource> T read(Class<T> theType, IdDt theId);
@ -163,6 +164,31 @@ public interface IGenericClient {
*/
<T extends IResource> T read(Class<T> theType, String theId);
/**
* Perform the "read" operation (retrieve the latest version of a resource instance by ID) using an absolute URL.
*
* @param theType
* The resource type that is being retrieved
* @param theUrl
* The absolute URL, e.g. "http://example.com/fhir/Patient/123"
* @return The returned resource from the server
*/
<T extends IResource> T read(Class<T> theType, UriDt theUrl);
/**
* Perform the "read" operation (retrieve the latest version of a resource instance by ID) using an absolute URL.
*
* @param theUrl
* The absolute URL, e.g. "http://example.com/fhir/Patient/123"
* @return The returned resource from the server
*/
IResource read(UriDt theUrl);
/**
* Register a new interceptor for this client. An interceptor can be used to add additional logging, or add security headers, or pre-process responses, etc.
*/
void registerInterceptor(IClientInterceptor theInterceptor);
IUntypedQuery search();
/**
@ -175,6 +201,54 @@ public interface IGenericClient {
*/
<T extends IResource> Bundle search(Class<T> theType, Map<String, List<IQueryParameterType>> theParams);
/**
* Perform the "search" operation using an absolute URL.
*
* @param theType
* The primary resource type that is being retrieved
* @param theUrl
* The absolute URL, e.g. "http://example.com/fhir/Patient/123"
* @return The returned bundle from the server
*/
<T extends IResource> Bundle search(Class<T> theType, UriDt theUrl);
Bundle search(UriDt theUrl);
/**
* If set to <code>true</code>, the client will log all requests and all responses. This is probably not a good production setting since it will result in a lot of extra logging, but it can be
* useful for troubleshooting.
*
* @param theLogRequestAndResponse
* Should requests and responses be logged
*/
void setLogRequestAndResponse(boolean theLogRequestAndResponse);
/**
* Send a transaction (collection of resources) to the server to be executed as a single unit
*/
ITransaction transaction();
/**
* Implementation of the "transaction" method.
*
* @param theResources
* The resources to create/update in a single transaction
* @return A list of resource stubs (<b>these will not be fully populated</b>) containing IDs and other {@link IResource#getResourceMetadata() metadata}
* @deprecated Use {@link #transaction()}
*
*/
List<IResource> transaction(List<IResource> theResources);
/**
* Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)}
*/
void unregisterInterceptor(IClientInterceptor theInterceptor);
/**
* Fluent method for the "update" operation, which performs a logical delete on a server resource
*/
IUpdate update();
/**
* Implementation of the "instance update" method.
*
@ -208,6 +282,21 @@ public interface IGenericClient {
*/
MethodOutcome validate(IResource theResource);
/**
* Implementation of the "instance vread" method. Note that this method expects <code>theId</code> to contain a resource ID as well as a version ID, and will fail if it does not.
* <p>
* Note that if an absolute resource ID is passed in (i.e. a URL containing a protocol and host as well as the resource type and ID) the server base for the client will be ignored, and the URL
* passed in will be queried.
* </p>
*
* @param theType
* The type of resource to load
* @param theId
* The ID to load, including the resource ID and the resource version ID. Valid values include "Patient/123/_history/222", or "http://example.com/fhir/Patient/123/_history/222"
* @return The resource
*/
<T extends IResource> T vread(Class<T> theType, IdDt theId);
/**
* Implementation of the "instance vread" method.
*
@ -218,6 +307,7 @@ public interface IGenericClient {
* @param theVersionId
* The version ID
* @return The resource
* @deprecated Deprecated in 0.7 - IdDt can contain an ID and a version, so this class doesn't make a lot of sense
*/
<T extends IResource> T vread(Class<T> theType, IdDt theId, IdDt theVersionId);
@ -234,46 +324,4 @@ public interface IGenericClient {
*/
<T extends IResource> T vread(Class<T> theType, String theId, String theVersionId);
/**
* If set to <code>true</code>, the client will log all requests and all responses. This is probably not a good
* production setting since it will result in a lot of extra logging, but it can be useful for troubleshooting.
*
* @param theLogRequestAndResponse
* Should requests and responses be logged
*/
void setLogRequestAndResponse(boolean theLogRequestAndResponse);
/**
* Fluent method for the "get tags" operation
*/
IGetTags getTags();
/**
* Loads the previous/next bundle of resources from a paged set, using the link specified in the
* "link type=next" tag within the atom bundle.
*
* @see Bundle#getLinkNext()
*/
IGetPage loadPage();
/**
* Send a transaction (collection of resources) to the server to be executed as a single unit
*/
ITransaction transaction();
/**
* Fluent method for the "create" operation, which creates a new resource instance on the server
*/
ICreate create();
/**
* Fluent method for the "delete" operation, which performs a logical delete on a server resource
*/
IDelete delete();
/**
* Fluent method for the "update" operation, which performs a logical delete on a server resource
*/
IUpdate update();
}

View File

@ -20,8 +20,6 @@ package ca.uhn.fhir.rest.method;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
@ -50,11 +48,15 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
/**
* @author James Agnew
* @author Doug Martin (Regenstrief Center for Biomedical Informatics)
*/
abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvocation {
private final FhirContext myContext;
private final IResource myResource;
private final String myUrlExtension;
private final String myUrlPath;
private final TagList myTagList;
private final List<IResource> myResources;
private final Bundle myBundle;
@ -62,18 +64,18 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
private boolean myContentsIsBundle;
private Map<String, List<String>> myParams;
public BaseHttpClientInvocationWithContents(FhirContext theContext, IResource theResource, String theUrlExtension) {
public BaseHttpClientInvocationWithContents(FhirContext theContext, IResource theResource, String theUrlPath) {
super();
myContext = theContext;
myResource = theResource;
myUrlExtension = theUrlExtension;
myUrlPath = theUrlPath;
myTagList = null;
myResources = null;
myBundle = null;
myContents = null;
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, TagList theTagList, String... theUrlExtension) {
public BaseHttpClientInvocationWithContents(FhirContext theContext, TagList theTagList, String... theUrlPath) {
super();
if (theTagList == null) {
throw new NullPointerException("Tag list must not be null");
@ -86,14 +88,14 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myBundle = null;
myContents = null;
myUrlExtension = StringUtils.join(theUrlExtension, '/');
myUrlPath = StringUtils.join(theUrlPath, '/');
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, List<IResource> theResources) {
myContext = theContext;
myResource = null;
myTagList = null;
myUrlExtension = null;
myUrlPath = null;
myResources = theResources;
myBundle = null;
myContents = null;
@ -103,28 +105,28 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
myContext = theContext;
myResource = null;
myTagList = null;
myUrlExtension = null;
myUrlPath = null;
myResources = null;
myBundle = theBundle;
myContents = null;
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, String theContents, boolean theIsBundle, String theUrlExtension) {
public BaseHttpClientInvocationWithContents(FhirContext theContext, String theContents, boolean theIsBundle, String theUrlPath) {
myContext = theContext;
myResource = null;
myTagList = null;
myUrlExtension = theUrlExtension;
myUrlPath = theUrlPath;
myResources = null;
myBundle = null;
myContents = theContents;
myContentsIsBundle = theIsBundle;
}
public BaseHttpClientInvocationWithContents(FhirContext theContext, Map<String, List<String>> theParams, String... theUrlExtension) {
public BaseHttpClientInvocationWithContents(FhirContext theContext, Map<String, List<String>> theParams, String... theUrlPath) {
myContext = theContext;
myResource = null;
myTagList = null;
myUrlExtension = StringUtils.join(theUrlExtension, '/');
myUrlPath = StringUtils.join(theUrlPath, '/');
myResources = null;
myBundle = null;
myContents = null;
@ -135,12 +137,17 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
@Override
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) throws DataFormatException {
StringBuilder b = new StringBuilder();
b.append(theUrlBase);
if (isNotBlank(myUrlExtension)) {
if (!theUrlBase.endsWith("/")) {
b.append('/');
if (myUrlPath == null) {
b.append(theUrlBase);
} else {
if (!myUrlPath.contains("://")) {
b.append(theUrlBase);
if (!theUrlBase.endsWith("/")) {
b.append('/');
}
}
b.append(myUrlExtension);
b.append(myUrlPath);
}
appendExtraParamsWithQuestionMark(theExtraParams, b, b.indexOf("?") == -1);

View File

@ -35,6 +35,10 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.server.EncodingEnum;
/**
* @author James Agnew
* @author Doug Martin (Regenstrief Center for Biomedical Informatics)
*/
public class HttpGetClientInvocation extends BaseHttpClientInvocation {
private final Map<String, List<String>> myParameters;
@ -77,12 +81,15 @@ public class HttpGetClientInvocation extends BaseHttpClientInvocation {
@Override
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) {
StringBuilder b = new StringBuilder();
b.append(theUrlBase);
if (!theUrlBase.endsWith("/")) {
b.append('/');
}
b.append(myUrlPath);
if (!myUrlPath.contains("://")) {
b.append(theUrlBase);
if (!theUrlBase.endsWith("/")) {
b.append('/');
}
}
b.append(myUrlPath);
boolean first = b.indexOf("?") == -1;
for (Entry<String, List<String>> next : myParameters.entrySet()) {
if (next.getValue() == null || next.getValue().isEmpty()) {

View File

@ -56,7 +56,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
private Integer myIdIndex;
private boolean mySupportsVersion;
private Integer myVersionIdIndex;
public ReadMethodBinding(Class<? extends IResource> theAnnotatedResourceType, Method theMethod, FhirContext theContext, Object theProvider) {
super(theAnnotatedResourceType, theMethod, theContext, theProvider);
@ -142,11 +142,16 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
IdDt id = ((IdDt) theArgs[myIdIndex]);
if (myVersionIdIndex == null) {
String resourceName = getResourceName();
retVal = createReadInvocation(id, resourceName);
if (id.hasVersionIdPart()) {
retVal = createVReadInvocation(new IdDt(resourceName, id.getIdPart(), id.getVersionIdPart()));
} else {
retVal = createReadInvocation(id, resourceName);
}
} else {
IdDt vid = ((IdDt) theArgs[myVersionIdIndex]);
String resourceName = getResourceName();
retVal = createVReadInvocation(id, vid, resourceName);
retVal = createVReadInvocation(new IdDt(resourceName, id.getIdPart(), vid.getVersionIdPart()));
}
for (int idx = 0; idx < theArgs.length; idx++) {
@ -198,16 +203,20 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding implem
return mySupportsVersion || myVersionIdIndex != null;
}
public static HttpGetClientInvocation createReadInvocation(IdDt theId, String resourceName) {
if (theId.hasVersionIdPart()) {
return new HttpGetClientInvocation(resourceName, theId.getIdPart(), Constants.URL_TOKEN_HISTORY, theId.getVersionIdPart());
} else {
return new HttpGetClientInvocation(resourceName, theId.getIdPart());
}
public static HttpGetClientInvocation createAbsoluteReadInvocation(IdDt theId) {
return new HttpGetClientInvocation(theId.getValue());
}
public static HttpGetClientInvocation createVReadInvocation(IdDt theId, IdDt vid, String resourceName) {
return new HttpGetClientInvocation(resourceName, theId.getIdPart(), Constants.URL_TOKEN_HISTORY, vid.getIdPart());
public static HttpGetClientInvocation createAbsoluteVReadInvocation(IdDt theId) {
return new HttpGetClientInvocation(theId.getValue());
}
public static HttpGetClientInvocation createReadInvocation(IdDt theId, String theResourceName) {
return new HttpGetClientInvocation(new IdDt(theResourceName, theId.getIdPart()).getValue());
}
public static HttpGetClientInvocation createVReadInvocation(IdDt theId) {
return new HttpGetClientInvocation(theId.getValue());
}
}

View File

@ -0,0 +1,232 @@
package ca.uhn.fhir.util;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLResolver;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.codehaus.stax2.XMLOutputFactory2;
import org.codehaus.stax2.io.EscapingWriterFactory;
import com.ctc.wstx.api.WstxInputProperties;
import com.ctc.wstx.stax.WstxInputFactory;
public class XmlUtil {
private static final Map<String, Integer> VALID_ENTITY_NAMES;
public static void main(String[] args) {
System.out.println(Character.toString((char)167));
}
static {
HashMap<String, Integer> validEntityNames = new HashMap<String, Integer>();
validEntityNames.put("nbsp", 160); // no-break space = non-breaking space, U+00A0 ISOnum -->
validEntityNames.put("iexcl", 161); // inverted exclamation mark, U+00A1 ISOnum -->
validEntityNames.put("cent", 162); // cent sign, U+00A2 ISOnum -->
validEntityNames.put("pound", 163); // pound sign, U+00A3 ISOnum -->
validEntityNames.put("curren", 164); // currency sign, U+00A4 ISOnum -->
validEntityNames.put("yen", 165); // yen sign = yuan sign, U+00A5 ISOnum -->
validEntityNames.put("brvbar", 166); // broken bar = broken vertical bar, U+00A6 ISOnum -->
validEntityNames.put("sect", 167); // section sign, U+00A7 ISOnum -->
validEntityNames.put("uml", 168); // diaeresis = spacing diaeresis, U+00A8 ISOdia -->
validEntityNames.put("copy", 169); // copyright sign, U+00A9 ISOnum -->
validEntityNames.put("ordf", 170); // feminine ordinal indicator, U+00AA ISOnum -->
validEntityNames.put("laquo", 171); // left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum -->
validEntityNames.put("not", 172); // not sign = angled dash, U+00AC ISOnum -->
validEntityNames.put("shy", 173); // soft hyphen = discretionary hyphen, U+00AD ISOnum -->
validEntityNames.put("reg", 174); // registered sign = registered trade mark sign, U+00AE ISOnum -->
validEntityNames.put("macr", 175); // macron = spacing macron = overline = APL overbar, U+00AF ISOdia -->
validEntityNames.put("deg", 176); // degree sign, U+00B0 ISOnum -->
validEntityNames.put("plusmn", 177); // plus-minus sign = plus-or-minus sign, U+00B1 ISOnum -->
validEntityNames.put("sup2", 178); // superscript two = superscript digit two = squared, U+00B2 ISOnum -->
validEntityNames.put("sup3", 179); // superscript three = superscript digit three = cubed, U+00B3 ISOnum -->
validEntityNames.put("acute", 180); // acute accent = spacing acute, U+00B4 ISOdia -->
validEntityNames.put("micro", 181); // micro sign, U+00B5 ISOnum -->
validEntityNames.put("para", 182); // pilcrow sign = paragraph sign, U+00B6 ISOnum -->
validEntityNames.put("middot", 183); // middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum -->
validEntityNames.put("cedil", 184); // cedilla = spacing cedilla, U+00B8 ISOdia -->
validEntityNames.put("sup1", 185); // superscript one = superscript digit one, U+00B9 ISOnum -->
validEntityNames.put("ordm", 186); // masculine ordinal indicator, U+00BA ISOnum -->
validEntityNames.put("raquo", 187); // right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum -->
validEntityNames.put("frac14", 188); // vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum -->
validEntityNames.put("frac12", 189); // vulgar fraction one half = fraction one half, U+00BD ISOnum -->
validEntityNames.put("frac34", 190); // vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum -->
validEntityNames.put("iquest", 191); // inverted question mark = turned question mark, U+00BF ISOnum -->
validEntityNames.put("Agrave", 192); // latin capital letter A with grave = latin capital letter A grave, U+00C0 ISOlat1 -->
validEntityNames.put("Aacute", 193); // latin capital letter A with acute, U+00C1 ISOlat1 -->
validEntityNames.put("Acirc", 194); // latin capital letter A with circumflex, U+00C2 ISOlat1 -->
validEntityNames.put("Atilde", 195); // latin capital letter A with tilde, U+00C3 ISOlat1 -->
validEntityNames.put("Auml", 196); // latin capital letter A with diaeresis, U+00C4 ISOlat1 -->
validEntityNames.put("Aring", 197); // latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1 -->
validEntityNames.put("AElig", 198); // latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 -->
validEntityNames.put("Ccedil", 199); // latin capital letter C with cedilla, U+00C7 ISOlat1 -->
validEntityNames.put("Egrave", 200); // latin capital letter E with grave, U+00C8 ISOlat1 -->
validEntityNames.put("Eacute", 201); // latin capital letter E with acute, U+00C9 ISOlat1 -->
validEntityNames.put("Ecirc", 202); // latin capital letter E with circumflex, U+00CA ISOlat1 -->
validEntityNames.put("Euml", 203); // latin capital letter E with diaeresis, U+00CB ISOlat1 -->
validEntityNames.put("Igrave", 204); // latin capital letter I with grave, U+00CC ISOlat1 -->
validEntityNames.put("Iacute", 205); // latin capital letter I with acute, U+00CD ISOlat1 -->
validEntityNames.put("Icirc", 206); // latin capital letter I with circumflex, U+00CE ISOlat1 -->
validEntityNames.put("Iuml", 207); // latin capital letter I with diaeresis, U+00CF ISOlat1 -->
validEntityNames.put("ETH", 208); // latin capital letter ETH, U+00D0 ISOlat1 -->
validEntityNames.put("Ntilde", 209); // latin capital letter N with tilde, U+00D1 ISOlat1 -->
validEntityNames.put("Ograve", 210); // latin capital letter O with grave, U+00D2 ISOlat1 -->
validEntityNames.put("Oacute", 211); // latin capital letter O with acute, U+00D3 ISOlat1 -->
validEntityNames.put("Ocirc", 212); // latin capital letter O with circumflex, U+00D4 ISOlat1 -->
validEntityNames.put("Otilde", 213); // latin capital letter O with tilde, U+00D5 ISOlat1 -->
validEntityNames.put("Ouml", 214); // latin capital letter O with diaeresis, U+00D6 ISOlat1 -->
validEntityNames.put("times", 215); // multiplication sign, U+00D7 ISOnum -->
validEntityNames.put("Oslash", 216); // latin capital letter O with stroke = latin capital letter O slash, U+00D8 ISOlat1 -->
validEntityNames.put("Ugrave", 217); // latin capital letter U with grave, U+00D9 ISOlat1 -->
validEntityNames.put("Uacute", 218); // latin capital letter U with acute, U+00DA ISOlat1 -->
validEntityNames.put("Ucirc", 219); // latin capital letter U with circumflex, U+00DB ISOlat1 -->
validEntityNames.put("Uuml", 220); // latin capital letter U with diaeresis, U+00DC ISOlat1 -->
validEntityNames.put("Yacute", 221); // latin capital letter Y with acute, U+00DD ISOlat1 -->
validEntityNames.put("THORN", 222); // latin capital letter THORN, U+00DE ISOlat1 -->
validEntityNames.put("szlig", 223); // latin small letter sharp s = ess-zed, U+00DF ISOlat1 -->
validEntityNames.put("agrave", 224); // latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1 -->
validEntityNames.put("aacute", 225); // latin small letter a with acute, U+00E1 ISOlat1 -->
validEntityNames.put("acirc", 226); // latin small letter a with circumflex, U+00E2 ISOlat1 -->
validEntityNames.put("atilde", 227); // latin small letter a with tilde, U+00E3 ISOlat1 -->
validEntityNames.put("auml", 228); // latin small letter a with diaeresis, U+00E4 ISOlat1 -->
validEntityNames.put("aring", 229); // latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1 -->
validEntityNames.put("aelig", 230); // latin small letter ae = latin small ligature ae, U+00E6 ISOlat1 -->
validEntityNames.put("ccedil", 231); // latin small letter c with cedilla, U+00E7 ISOlat1 -->
validEntityNames.put("egrave", 232); // latin small letter e with grave, U+00E8 ISOlat1 -->
validEntityNames.put("eacute", 233); // latin small letter e with acute, U+00E9 ISOlat1 -->
validEntityNames.put("ecirc", 234); // latin small letter e with circumflex, U+00EA ISOlat1 -->
validEntityNames.put("euml", 235); // latin small letter e with diaeresis, U+00EB ISOlat1 -->
validEntityNames.put("igrave", 236); // latin small letter i with grave, U+00EC ISOlat1 -->
validEntityNames.put("iacute", 237); // latin small letter i with acute, U+00ED ISOlat1 -->
validEntityNames.put("icirc", 238); // latin small letter i with circumflex, U+00EE ISOlat1 -->
validEntityNames.put("iuml", 239); // latin small letter i with diaeresis, U+00EF ISOlat1 -->
validEntityNames.put("eth", 240); // latin small letter eth, U+00F0 ISOlat1 -->
validEntityNames.put("ntilde", 241); // latin small letter n with tilde, U+00F1 ISOlat1 -->
validEntityNames.put("ograve", 242); // latin small letter o with grave, U+00F2 ISOlat1 -->
validEntityNames.put("oacute", 243); // latin small letter o with acute, U+00F3 ISOlat1 -->
validEntityNames.put("ocirc", 244); // latin small letter o with circumflex, U+00F4 ISOlat1 -->
validEntityNames.put("otilde", 245); // latin small letter o with tilde, U+00F5 ISOlat1 -->
validEntityNames.put("ouml", 246); // latin small letter o with diaeresis, U+00F6 ISOlat1 -->
validEntityNames.put("divide", 247); // division sign, U+00F7 ISOnum -->
validEntityNames.put("oslash", 248); // latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1 -->
validEntityNames.put("ugrave", 249); // latin small letter u with grave, U+00F9 ISOlat1 -->
validEntityNames.put("uacute", 250); // latin small letter u with acute, U+00FA ISOlat1 -->
validEntityNames.put("ucirc", 251); // latin small letter u with circumflex, U+00FB ISOlat1 -->
validEntityNames.put("uuml", 252); // latin small letter u with diaeresis, U+00FC ISOlat1 -->
validEntityNames.put("yacute", 253); // latin small letter y with acute, U+00FD ISOlat1 -->
validEntityNames.put("thorn", 254); // latin small letter thorn, U+00FE ISOlat1 -->
validEntityNames.put("yuml", 255); // latin small letter y with diaeresis, U+00FF ISOlat1 -->
VALID_ENTITY_NAMES = Collections.unmodifiableMap(validEntityNames);
}
public static XMLEventReader createXmlReader(Reader reader) throws FactoryConfigurationError, XMLStreamException {
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
XMLResolver xmlResolver = new XMLResolver() {
@Override
public Object resolveEntity(String thePublicID, String theSystemID, String theBaseURI, String theNamespace) throws XMLStreamException {
if (thePublicID == null && theSystemID == null) {
if (theNamespace != null && VALID_ENTITY_NAMES.containsKey(theNamespace)) {
return IOUtils.toInputStream(Character.toString((char)VALID_ENTITY_NAMES.get(theNamespace).intValue()));
}
}
return null;
}
};
inputFactory.setProperty(WstxInputFactory.IS_REPLACING_ENTITY_REFERENCES, false);
// inputFactory.setProperty(WstxInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false);
// inputFactory.setProperty(WstxInputFactory.IS_VALIDATING, false);
// inputFactory.setProperty(WstxInputProperties.P_TREAT_CHAR_REFS_AS_ENTS, true);
// inputFactory.setProperty(WstxInputProperties.P_UNDECLARED_ENTITY_RESOLVER, xmlResolver);
XMLEventReader er = inputFactory.createXMLEventReader(reader);
return er;
}
public static XMLEventWriter createXmlWriter(Writer theWriter) throws FactoryConfigurationError, XMLStreamException {
XMLOutputFactory newInstance = XMLOutputFactory.newInstance();
newInstance.setProperty(XMLOutputFactory2.P_TEXT_ESCAPER, new MyEscaper());
XMLEventWriter ew = newInstance.createXMLEventWriter(theWriter);
return ew;
}
public static XMLStreamWriter createXmlStreamWriter(Writer theWriter) throws FactoryConfigurationError, XMLStreamException {
XMLOutputFactory newInstance = XMLOutputFactory.newInstance();
newInstance.setProperty(XMLOutputFactory2.P_TEXT_ESCAPER, new MyEscaper());
XMLStreamWriter ew = newInstance.createXMLStreamWriter(theWriter);
return ew;
}
public static class MyEscaper implements EscapingWriterFactory {
@Override
public Writer createEscapingWriterFor(OutputStream theOut, String theEnc) throws UnsupportedEncodingException {
return createEscapingWriterFor(new OutputStreamWriter(theOut), theEnc);
}
@Override
public Writer createEscapingWriterFor(final Writer theW, String theEnc) throws UnsupportedEncodingException {
return new Writer() {
@Override
public void close() throws IOException {
theW.close();
}
@Override
public void flush() throws IOException {
theW.flush();
}
@Override
public void write(char[] theCbuf, int theOff, int theLen) throws IOException {
boolean hasEscapable = false;
for (int i = 0; i < theLen && !hasEscapable; i++) {
char nextChar = theCbuf[i + theOff];
switch (nextChar) {
case '<':
case '>':
case '"':
case '&':
hasEscapable = true;
}
}
if (!hasEscapable) {
theW.write(theCbuf, theOff, theLen);
return;
}
String escaped = StringEscapeUtils.escapeXml10(new String(theCbuf, theOff, theLen));
theW.write(escaped.toCharArray());
}
};
}
}
}

View File

@ -1,4 +1,8 @@
ca.uhn.fhir.rest.client.GenericClient.noVersionIdForVread=No version specified in URL for 'vread' operation: {0}
ca.uhn.fhir.rest.client.GenericClient.incompleteUriForRead=The given URI is not an absolute URL and is not usable for this operation: {0}
ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri=Unable to determine the resource type from the given URI: {0}
ca.uhn.fhir.rest.method.SearchMethodBinding.invalidSpecialParamName=Method [{0}] in provider [{1}] contains search parameter annotated to use name [{2}] - This name is reserved according to the FHIR specification and can not be used as a search parameter name.
ca.uhn.fhir.rest.method.SearchMethodBinding.idWithoutCompartment=Method [{0}] in provider [{1}] has an @IdParam parameter. This is only allowable for compartment search (e.g. @Search(compartment="foo") )
ca.uhn.fhir.rest.method.SearchMethodBinding.idNullForCompartmentSearch=ID parameter can not be null or empty for compartment search

View File

@ -191,6 +191,36 @@
</macro>
</subsection>
<subsection name="Instance - Read / VRead">
<p>
Given a resource name and ID, it is simple to retrieve
the latest version of that resource (a 'read')
</p>
<macro name="snippet">
<param name="id" value="read" />
<param name="file"
value="examples/src/main/java/example/GenericClientExample.java" />
</macro>
<p>
By adding a version string, it is also possible to retrieve a
specific version (a 'vread')
</p>
<macro name="snippet">
<param name="id" value="vread" />
<param name="file"
value="examples/src/main/java/example/GenericClientExample.java" />
</macro>
<p>
It is also possible to retrieve a resource given its absolute
URL (this will override the base URL set on the client)
</p>
<macro name="snippet">
<param name="id" value="readabsolute" />
<param name="file"
value="examples/src/main/java/example/GenericClientExample.java" />
</macro>
</subsection>
<subsection name="Instance - Delete">
<p>
The following example shows how to perform a delete

View File

@ -17,11 +17,28 @@ public class IdDtTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IdDtTest.class);
@Test
public void testDetermineBase() {
IdDt rr;
rr = new IdDt("http://foo/fhir/Organization/123");
assertEquals("http://foo/fhir", rr.getBaseUrl());
rr = new IdDt("http://foo/fhir/Organization/123/_history/123");
assertEquals("http://foo/fhir", rr.getBaseUrl());
rr = new IdDt("Organization/123/_history/123");
assertEquals(null, rr.getBaseUrl());
}
@Test
public void testParseValueAbsolute() {
Patient patient = new Patient();
IdDt rr = new IdDt();
rr.setValue("http://foo/fhir/Organization/123");
patient.setManagingOrganization(new ResourceReferenceDt(rr));
Patient actual = parseAndEncode(patient);
@ -33,12 +50,12 @@ public class IdDtTest {
@Test
public void testBigDecimalIds() {
IdDt id = new IdDt(new BigDecimal("123"));
assertEquals(id.getIdPartAsBigDecimal(), new BigDecimal("123"));
}
@Test
public void testParseValueAbsoluteWithVersion() {
Patient patient = new Patient();
@ -69,7 +86,6 @@ public class IdDtTest {
}
@Test
public void testParseValueMissingType1() {
Patient patient = new Patient();

View File

@ -40,4 +40,28 @@ public class XhtmlDtTest {
}
@Test
public void testBasicCharacterEntity() {
String input = "amp &amp;";
XhtmlDt x = new XhtmlDt();
x.setValueAsString(input);
assertEquals("<div>amp &amp;</div>", x.getValueAsString());
}
@Test
public void testCharacterEntities() {
String input = "Sect: &sect; uuml: &uuml; &Uuml;";
XhtmlDt x = new XhtmlDt();
x.setValueAsString(input);
// <div>Sect: § uuml: ü Ü</div>
assertEquals("<div>Sect: &sect; uuml: &uuml; &Uuml;</div>", x.getValueAsString());
}
}

View File

@ -446,8 +446,13 @@ public class JsonParserTest {
@Test
public void testParseJsonProfile() throws IOException {
parseAndEncode("/patient.profile.json");
parseAndEncode("/alert.profile.json");
}
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/alert.profile.json"));
private void parseAndEncode(String name) throws IOException {
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream(name));
ourLog.info(msg);
IParser p = ourCtx.newJsonParser();

View File

@ -181,6 +181,28 @@ public class XmlParserTest {
}
@Test
public void testEncodeEscapedChars() {
Patient p = new Patient();
p.addName().addFamily("and <>&ü");
String enc = ourCtx.newXmlParser().encodeResourceToString(p);
ourLog.info(enc);
p = ourCtx.newXmlParser().parseResource(Patient.class, enc);
assertEquals("and <>&ü", p.getNameFirstRep().getFamilyFirstRep().getValue());
p = ourCtx.newXmlParser().parseResource(Patient.class, "<Patient xmlns=\"http://hl7.org/fhir\"><name><family value=\"quot &quot;\"/></name></Patient>");
assertEquals("quot \"", p.getNameFirstRep().getFamilyFirstRep().getValue());
// Unescaped entities are swallowed
p = ourCtx.newXmlParser().parseResource(Patient.class, "<Patient xmlns=\"http://hl7.org/fhir\"><name><family value=\"uuml &uuml;\"/></name></Patient>");
assertEquals("uuml ", p.getNameFirstRep().getFamilyFirstRep().getValue());
}
@Test
public void testEncodeContainedAndIncludedResources() {

View File

@ -42,6 +42,7 @@ import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.method.SearchStyleEnum;
@ -95,7 +96,6 @@ public class GenericClientTest {
return msg;
}
private String getResourceResult() {
//@formatter:off
String msg =
@ -111,8 +111,7 @@ public class GenericClientTest {
//@formatter:on
return msg;
}
@Test
public void testSearchByCompartment() throws Exception {
@ -155,8 +154,6 @@ public class GenericClientTest {
}
@Test
public void testCreateWithTag() throws Exception {
@ -185,22 +182,22 @@ public class GenericClientTest {
Header catH = capt.getValue().getFirstHeader("Category");
assertNotNull(Arrays.asList(capt.getValue().getAllHeaders()).toString(), catH);
assertEquals("urn:happytag; label=\"This is a happy resource\"; scheme=\"http://hl7.org/fhir/tag\"", catH.getValue());
/*
* Try fluent options
*/
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
client.create().resource(p1).withId("123").execute();
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(1).getURI().toString());
String resourceText = "<Patient xmlns=\"http://hl7.org/fhir\"> </Patient>";
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
client.create().resource(resourceText).withId("123").execute();
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(2).getURI().toString());
assertEquals(resourceText, IOUtils.toString(((HttpPost)capt.getAllValues().get(2)).getEntity().getContent()));
assertEquals(resourceText, IOUtils.toString(((HttpPost) capt.getAllValues().get(2)).getEntity().getContent()));
}
@Test
public void testCreateWithTagNonFluent() throws Exception {
@ -231,13 +228,12 @@ public class GenericClientTest {
assertEquals("urn:happytag; label=\"This is a happy resource\"; scheme=\"http://hl7.org/fhir/tag\"", catH.getValue());
}
@Test
public void testDelete() throws Exception {
OperationOutcome oo = new OperationOutcome();
oo.addIssue().addLocation().setValue("testDelete01");
String ooStr = myCtx.newXmlParser().encodeResourceToString(oo);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK"));
@ -248,18 +244,18 @@ public class GenericClientTest {
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
OperationOutcome outcome = client.delete().resourceById("Patient", "123").execute();
assertEquals("http://example.com/fhir/Patient/123", capt.getValue().getURI().toString());
assertEquals("DELETE", capt.getValue().getMethod());
assertEquals("testDelete01",outcome.getIssueFirstRep().getLocationFirstRep().getValue());
assertEquals("testDelete01", outcome.getIssueFirstRep().getLocationFirstRep().getValue());
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("LKJHLKJGLKJKLL"), Charset.forName("UTF-8")));
outcome = client.delete().resourceById(new IdDt("Location", "123","456")).prettyPrint().encodedJson().execute();
outcome = client.delete().resourceById(new IdDt("Location", "123", "456")).prettyPrint().encodedJson().execute();
assertEquals("http://example.com/fhir/Location/123?_format=json&_pretty=true", capt.getAllValues().get(1).getURI().toString());
assertEquals("DELETE", capt.getValue().getMethod());
assertEquals(null,outcome);
assertEquals(null, outcome);
}
@Test
@ -300,7 +296,6 @@ public class GenericClientTest {
}
@Test
public void testRead() throws Exception {
@ -311,13 +306,11 @@ public class GenericClientTest {
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
Header[] headers = new Header[] {
new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
new BasicHeader(Constants.HEADER_CATEGORY, "http://foo/tagdefinition.html; scheme=\"http://hl7.org/fhir/tag\"; label=\"Some tag\"")
};
Header[] headers = new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
new BasicHeader(Constants.HEADER_CATEGORY, "http://foo/tagdefinition.html; scheme=\"http://hl7.org/fhir/tag\"; label=\"Some tag\"") };
when(myHttpResponse.getAllHeaders()).thenReturn(headers);
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
//@formatter:off
@ -334,13 +327,71 @@ public class GenericClientTest {
TagList tags = ResourceMetadataKeyEnum.TAG_LIST.get(response);
assertNotNull(tags);
assertEquals(1,tags.size());
assertEquals(1, tags.size());
assertEquals("http://foo/tagdefinition.html", tags.get(0).getTerm());
assertEquals("http://hl7.org/fhir/tag",tags.get(0).getScheme());
assertEquals("Some tag",tags.get(0).getLabel());
assertEquals("http://hl7.org/fhir/tag", tags.get(0).getScheme());
assertEquals("Some tag", tags.get(0).getLabel());
}
@Test
public void testReadWithAbsoluteUrl() throws Exception {
String msg = getResourceResult();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
Header[] headers = new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
new BasicHeader(Constants.HEADER_CATEGORY, "http://foo/tagdefinition.html; scheme=\"http://hl7.org/fhir/tag\"; label=\"Some tag\"") };
when(myHttpResponse.getAllHeaders()).thenReturn(headers);
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
Patient response = client.read(Patient.class, new IdDt("http://somebase.com/path/to/base/Patient/1234"));
assertThat(response.getNameFirstRep().getFamilyAsSingleString(), StringContains.containsString("Cardinal"));
assertEquals("http://somebase.com/path/to/base/Patient/1234", capt.getAllValues().get(0).getURI().toString());
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
response = client.read(Patient.class, new IdDt("http://somebase.com/path/to/base/Patient/1234/_history/2222"));
assertThat(response.getNameFirstRep().getFamilyAsSingleString(), StringContains.containsString("Cardinal"));
assertEquals("http://somebase.com/path/to/base/Patient/1234", capt.getAllValues().get(1).getURI().toString());
}
@Test
public void testVReadWithAbsoluteUrl() throws Exception {
String msg = getResourceResult();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
Header[] headers = new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
new BasicHeader(Constants.HEADER_CATEGORY, "http://foo/tagdefinition.html; scheme=\"http://hl7.org/fhir/tag\"; label=\"Some tag\"") };
when(myHttpResponse.getAllHeaders()).thenReturn(headers);
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
Patient response = client.vread(Patient.class, new IdDt("http://somebase.com/path/to/base/Patient/1234/_history/2222"));
assertThat(response.getNameFirstRep().getFamilyAsSingleString(), StringContains.containsString("Cardinal"));
assertEquals("http://somebase.com/path/to/base/Patient/1234/_history/2222", capt.getAllValues().get(0).getURI().toString());
try {
client.vread(Patient.class, new IdDt("http://somebase.com/path/to/base/Patient/1234"));
fail();
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("No version specified in URL"));
}
}
@SuppressWarnings("unused")
@Test
public void testSearchAllResources() throws Exception {
@ -365,6 +416,7 @@ public class GenericClientTest {
assertEquals("http://example.com/fhir/?name=james", capt.getValue().getURI().toString());
}
@SuppressWarnings("unused")
@Test
public void testSearchByComposite() throws Exception {
@ -389,10 +441,9 @@ public class GenericClientTest {
.execute();
//@formatter:on
assertEquals("http://foo/Observation?" + Observation.SP_NAME_VALUE_DATE + "=" + URLEncoder.encode("FOO\\$BAR$2001-01-01","UTF-8"), capt.getValue().getURI().toString());
assertEquals("http://foo/Observation?" + Observation.SP_NAME_VALUE_DATE + "=" + URLEncoder.encode("FOO\\$BAR$2001-01-01", "UTF-8"), capt.getValue().getURI().toString());
}
@SuppressWarnings("unused")
@Test
@ -409,7 +460,7 @@ public class GenericClientTest {
GenericClient client = (GenericClient) myCtx.newRestfulGenericClient("http://example.com/fhir");
client.setPrettyPrint(true);
client.setEncoding(EncodingEnum.JSON);
//@formatter:off
Bundle response = client.search()
.forResource(Patient.class)
@ -419,8 +470,7 @@ public class GenericClientTest {
assertEquals("http://example.com/fhir/Patient?_format=json&_pretty=true", capt.getValue().getURI().toString());
}
@SuppressWarnings("unused")
@Test
public void testSearchByDate() throws Exception {
@ -448,10 +498,58 @@ public class GenericClientTest {
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json", capt.getValue().getURI().toString());
assertEquals(
"http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json",
capt.getValue().getURI().toString());
}
@Test
public void testSearchWithAbsoluteUrl() throws Exception {
String msg = getPatientFeedWithOneResult();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
Bundle response = client.search(new UriDt("http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json"));
assertEquals(
"http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json",
capt.getValue().getURI().toString());
assertEquals(1, response.size());
}
@Test
public void testSearchWithAbsoluteUrlAndType() throws Exception {
String msg = getPatientFeedWithOneResult();
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
Bundle response = client.search(Patient.class, new UriDt("http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json"));
assertEquals(
"http://example.com/fhir/Patient?birthdate=%3C%3D2012-01-22&birthdate=%3E2011-01-01&_include=Patient.managingOrganization&_sort%3Aasc=birthdate&_sort%3Adesc=name&_count=123&_format=json",
capt.getValue().getURI().toString());
assertEquals(1, response.size());
}
@SuppressWarnings("unused")
@Test
public void testSearchByNumberExact() throws Exception {
@ -551,7 +649,7 @@ public class GenericClientTest {
assertEquals("http://example.com/fhir/Patient?provider=123", capt.getValue().getURI().toString());
}
@SuppressWarnings("unused")
@Test
public void testSearchByString() throws Exception {
@ -583,7 +681,7 @@ public class GenericClientTest {
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?name=" + URLEncoder.encode("AAA,BBB,C\\,C","UTF-8"), capt.getAllValues().get(1).getURI().toString());
assertEquals("http://example.com/fhir/Patient?name=" + URLEncoder.encode("AAA,BBB,C\\,C", "UTF-8"), capt.getAllValues().get(1).getURI().toString());
}
@ -656,8 +754,7 @@ public class GenericClientTest {
assertEquals("http://example.com/fhir/Patient?identifier=" + URLEncoder.encode("A|B,C|D", "UTF-8"), capt.getAllValues().get(2).getURI().toString());
}
@SuppressWarnings("unused")
@Test
public void testSearchUsingPost() throws Exception {
@ -681,7 +778,7 @@ public class GenericClientTest {
//@formatter:on
assertEquals("http://example.com/fhir/Patient/_search", capt.getValue().getURI().toString());
HttpEntityEnclosingRequestBase enc = (HttpEntityEnclosingRequestBase) capt.getValue();
UrlEncodedFormEntity ent = (UrlEncodedFormEntity) enc.getEntity();
String string = IOUtils.toString(ent.getContent());
@ -689,7 +786,6 @@ public class GenericClientTest {
assertEquals("name=james", string);
}
@SuppressWarnings("unused")
@Test
public void testSearchAutomaticallyUsesPost() throws Exception {
@ -705,7 +801,7 @@ public class GenericClientTest {
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
String longValue = StringUtils.leftPad("", 20000, 'B');
//@formatter:off
Bundle response = client.search()
.forResource("Patient")
@ -714,15 +810,14 @@ public class GenericClientTest {
//@formatter:on
assertEquals("http://example.com/fhir/Patient/_search", capt.getValue().getURI().toString());
HttpEntityEnclosingRequestBase enc = (HttpEntityEnclosingRequestBase) capt.getValue();
UrlEncodedFormEntity ent = (UrlEncodedFormEntity) enc.getEntity();
String string = IOUtils.toString(ent.getContent());
ourLog.info(string);
assertEquals("name="+longValue, string);
assertEquals("name=" + longValue, string);
}
@SuppressWarnings("unused")
@Test
public void testSearchUsingGetSearch() throws Exception {
@ -747,7 +842,7 @@ public class GenericClientTest {
assertEquals("http://example.com/fhir/Patient/_search?name=james", capt.getValue().getURI().toString());
}
@SuppressWarnings("unused")
@Test
public void testSearchWithInternalServerError() throws Exception {
@ -817,9 +912,7 @@ public class GenericClientTest {
assertEquals("http://example.com/fhir", capt.getValue().getURI().toString());
assertEquals(bundle.getEntries().get(0).getId(), response.getEntries().get(0).getId());
}
@Test
public void testTransactionJson() throws Exception {
String bundleStr = IOUtils.toString(getClass().getResourceAsStream("/bundle.json"));
@ -841,16 +934,15 @@ public class GenericClientTest {
//@formatter:on
HttpEntityEnclosingRequestBase value = (HttpEntityEnclosingRequestBase) capt.getValue();
Header ct = value.getEntity().getContentType();
assertNotNull(ct);
assertEquals(Constants.CT_FHIR_JSON + "; charset=UTF-8", ct.getValue());
assertEquals("http://example.com/fhir?_format=json", value.getURI().toString());
assertThat(IOUtils.toString(value.getEntity().getContent()), StringContains.containsString("\"resourceType\""));
assertEquals(bundle.getEntries().get(0).getId(), response.getEntries().get(0).getId());
}
@Test
public void testUpdate() throws Exception {
@ -877,12 +969,12 @@ public class GenericClientTest {
} catch (InvalidRequestException e) {
// should happen because no ID set
}
assertEquals(0, capt.getAllValues().size());
p1.setId("44");
client.update().resource(p1).execute();
assertEquals(1, capt.getAllValues().size());
MethodOutcome outcome = client.update().resource(p1).execute();
@ -896,7 +988,7 @@ public class GenericClientTest {
Header catH = capt.getValue().getFirstHeader("Category");
assertNotNull(Arrays.asList(capt.getValue().getAllHeaders()).toString(), catH);
assertEquals("urn:happytag; label=\"This is a happy resource\"; scheme=\"http://hl7.org/fhir/tag\"", catH.getValue());
/*
* Try fluent options
*/
@ -904,14 +996,14 @@ public class GenericClientTest {
client.update().resource(p1).withId("123").execute();
assertEquals(3, capt.getAllValues().size());
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(2).getURI().toString());
String resourceText = "<Patient xmlns=\"http://hl7.org/fhir\"> </Patient>";
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
client.update().resource(resourceText).withId("123").execute();
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(3).getURI().toString());
assertEquals(resourceText, IOUtils.toString(((HttpPut)capt.getAllValues().get(3)).getEntity().getContent()));
assertEquals(resourceText, IOUtils.toString(((HttpPut) capt.getAllValues().get(3)).getEntity().getContent()));
assertEquals(4, capt.getAllValues().size());
}
@BeforeClass

File diff suppressed because one or more lines are too long

17
pom.xml
View File

@ -56,11 +56,24 @@
</developer>
<developer>
<name>Josh Mandel</name>
<url></url>
<organization>Boston Children's Hospital</organization>
</developer>
<developer>
<name>Laura MacDougall Sookraj</name>
<url></url>
</developer>
<developer>
<name>David Hay</name>
<organization>Orion Health</organization>
</developer>
<developer>
<id>suranga</id>
<name>Suranga Nath Kasthurirathne</name>
<organization>OpenMRS</organization>
</developer>
<developer>
<id>dougmartin</id>
<name>Doug Martin</name>
<organization>Regenstrief Center for Biomedical Informatics</organization>
</developer>
</developers>