diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml
index 9d39212b6453..8074aa23b7e6 100644
--- a/hapi-fhir-android/pom.xml
+++ b/hapi-fhir-android/pom.xml
@@ -35,10 +35,6 @@
commons-codec
commons-codec
-
- org.codehaus.woodstox
- woodstox-core-asl
-
@@ -59,6 +55,11 @@
2.1-SNAPSHOT
true
+
+ org.codehaus.woodstox
+ woodstox-core-asl
+ true
+
org.apache.httpcomponents
httpclient-android
@@ -75,10 +76,6 @@
commons-io
commons-io
-
- commons-codec
- commons-codec
-
org.apache.commons
commons-lang3
@@ -98,25 +95,59 @@
org.apache.maven.plugins
maven-failsafe-plugin
-
+ dstu2_shade
integration-test
verify
+
+
+ **/*Dstu2ShadeIT.java
+
+
+ ${basedir}/target/hapi-fhir-android-${project.version}-dstu2.jar
+
+
+ ca.uhn.hapi.fhir:*
+ org.codehaus.woodstox:woodstox-core-asl
+ org.codehaus.woodstox:stax2-api
+
+
+
+
+ dstu2
+
+ integration-test
+ verify
+
+
+
+ **/*Dstu2IT.java
+
+
+ org.codehaus.woodstox:woodstox-core-asl
+ org.codehaus.woodstox:stax2-api
+
+
+
+
+ dstu3
+
+ integration-test
+ verify
+
+
+
+ **/*Dstu3IT.java
+
+
+ org.codehaus.woodstox:woodstox-core-asl
+ org.codehaus.woodstox:stax2-api
+
+
@@ -130,6 +161,9 @@
ca.uhn.hapi.fhir:hapi-fhir-base
+ org.codehaus.woodstox:woodstox-core-asl
+ javax.xml.stream:stax-api
+ org.codehaus.woodstox:stax2-api
@@ -141,6 +175,10 @@
javax.json
ca.uhn.fhir.repackage.javax.json
+
+ com.ctc.wstx.stax
+ ca.uhn.fhir.repackage.com.ctc.wstx.stax
+
@@ -182,8 +220,8 @@
package
shade
-
-
+
+
dstu
@@ -261,6 +299,27 @@
+
+ org.jacoco
+ jacoco-maven-plugin
+
+
+ ${basedir}/target/classes
+
+
+ ${basedir}/src/main/java
+
+ true
+
+
+
+ default-prepare-agent
+
+ prepare-agent
+
+
+
+
diff --git a/hapi-fhir-android/src/test/java/ca/uhn/fhir/android/BuiltJarDstu2IT.java b/hapi-fhir-android/src/test/java/ca/uhn/fhir/android/BuiltJarDstu2IT.java
new file mode 100644
index 000000000000..2ac7845d43b5
--- /dev/null
+++ b/hapi-fhir-android/src/test/java/ca/uhn/fhir/android/BuiltJarDstu2IT.java
@@ -0,0 +1,192 @@
+package ca.uhn.fhir.android;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.util.*;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import javax.naming.ConfigurationException;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.WildcardFileFilter;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
+import ca.uhn.fhir.model.dstu2.resource.Observation;
+import ca.uhn.fhir.model.dstu2.resource.Patient;
+import ca.uhn.fhir.rest.client.IGenericClient;
+import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
+
+public class BuiltJarDstu2IT {
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BuiltJarDstu2IT.class);
+
+ @BeforeClass
+ public static void beforeClass() {
+ System.setProperty("javax.xml.stream.XMLInputFactory", "FOO");
+ System.setProperty("javax.xml.stream.XMLOutputFactory", "FOO");
+ }
+
+ @Test
+ public void testParserXml() throws Exception {
+
+ FhirContext ctx = FhirContext.forDstu2();
+
+ Patient p = new Patient();
+ p.addIdentifier().setSystem("system");
+
+ try {
+ ctx.newXmlParser().encodeResourceToString(p);
+ fail();
+ } catch (ca.uhn.fhir.context.ConfigurationException e) {
+ assertEquals("Unable to initialize StAX - XML processing is disabled",e.getMessage());
+ }
+ }
+
+ @Test
+ public void testParserJson() {
+
+ FhirContext ctx = FhirContext.forDstu2();
+
+ Observation o = new Observation();
+ o.getCode().setText("TEXT");
+ o.setValue(new QuantityDt(123));
+ o.addIdentifier().setSystem("system");
+
+ String str = ctx.newJsonParser().encodeResourceToString(o);
+ Observation p2 = ctx.newJsonParser().parseResource(Observation.class, str);
+
+ assertEquals("TEXT", p2.getCode().getText());
+
+ QuantityDt dt = (QuantityDt) p2.getValue();
+ dt.getComparatorElement().getValueAsEnum();
+
+ }
+
+ /**
+ * A simple client test - We try to connect to a server that doesn't exist, but
+ * if we at least get the right exception it means we made it up to the HTTP/network stack
+ *
+ * Disabled for now - TODO: add the old version of the apache client (the one that
+ * android uses) and see if this passes
+ */
+ @SuppressWarnings("deprecation")
+ public void testClient() {
+ FhirContext ctx = FhirContext.forDstu2();
+ try {
+ IGenericClient client = ctx.newRestfulGenericClient("http://127.0.0.1:44442/SomeBase");
+ client.conformance();
+ } catch (FhirClientConnectionException e) {
+ // this is good
+ }
+ }
+
+ /**
+ * Android does not like duplicate entries in the JAR
+ */
+ @Test
+ public void testJarContents() throws Exception {
+ String wildcard = "hapi-fhir-android-*.jar";
+ Collection files = FileUtils.listFiles(new File("target"), new WildcardFileFilter(wildcard), null);
+ if (files.isEmpty()) {
+ throw new Exception("No files matching " + wildcard);
+ }
+
+ for (File file : files) {
+ if (file.getName().endsWith("sources.jar")) {
+ continue;
+ }
+ if (file.getName().endsWith("javadoc.jar")) {
+ continue;
+ }
+ if (file.getName().contains("original.jar")) {
+ continue;
+ }
+
+ ourLog.info("Testing file: {}", file);
+
+ ZipFile zip = new ZipFile(file);
+
+ int totalClasses = 0;
+ int totalMethods = 0;
+ TreeSet topMethods = new TreeSet();
+
+ try {
+ Set names = new HashSet();
+ for (Enumeration extends ZipEntry> iter = zip.entries(); iter.hasMoreElements();) {
+ ZipEntry next = iter.nextElement();
+ String nextName = next.getName();
+ if (!names.add(nextName)) {
+ throw new Exception("File " + file + " contains duplicate contents: " + nextName);
+ }
+
+ if (nextName.contains("$") == false) {
+ if (nextName.endsWith(".class")) {
+ String className = nextName.replace("/", ".").replace(".class", "");
+ try {
+ Class> clazz = Class.forName(className);
+ int methodCount = clazz.getMethods().length;
+ topMethods.add(new ClassMethodCount(className, methodCount));
+ totalClasses++;
+ totalMethods += methodCount;
+ } catch (NoClassDefFoundError e) {
+ // ignore
+ } catch (ClassNotFoundException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ ourLog.info("File {} contains {} entries", file, names.size());
+ ourLog.info("Total classes {} - Total methods {}", totalClasses, totalMethods);
+ ourLog.info("Top classes {}", new ArrayList(topMethods).subList(Math.max(0, topMethods.size() - 10), topMethods.size()));
+
+ } finally {
+ zip.close();
+ }
+ }
+ }
+
+ private static class ClassMethodCount implements Comparable {
+
+ private String myClassName;
+ private int myMethodCount;
+
+ public ClassMethodCount(String theClassName, int theMethodCount) {
+ myClassName = theClassName;
+ myMethodCount = theMethodCount;
+ }
+
+ @Override
+ public String toString() {
+ return myClassName + "[" + myMethodCount + "]";
+ }
+
+ @Override
+ public int compareTo(ClassMethodCount theO) {
+ return myMethodCount - theO.myMethodCount;
+ }
+
+ public String getClassName() {
+ return myClassName;
+ }
+
+ public void setClassName(String theClassName) {
+ myClassName = theClassName;
+ }
+
+ public int getMethodCount() {
+ return myMethodCount;
+ }
+
+ public void setMethodCount(int theMethodCount) {
+ myMethodCount = theMethodCount;
+ }
+
+ }
+
+}
diff --git a/hapi-fhir-android/src/test/java/ca/uhn/fhir/android/BuiltJarIT.java b/hapi-fhir-android/src/test/java/ca/uhn/fhir/android/BuiltJarDstu2ShadeIT.java
similarity index 85%
rename from hapi-fhir-android/src/test/java/ca/uhn/fhir/android/BuiltJarIT.java
rename to hapi-fhir-android/src/test/java/ca/uhn/fhir/android/BuiltJarDstu2ShadeIT.java
index f14c6534298c..89776450c80a 100644
--- a/hapi-fhir-android/src/test/java/ca/uhn/fhir/android/BuiltJarIT.java
+++ b/hapi-fhir-android/src/test/java/ca/uhn/fhir/android/BuiltJarDstu2ShadeIT.java
@@ -19,52 +19,41 @@
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
-public class BuiltJarIT {
- private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BuiltJarIT.class);
+public class BuiltJarDstu2ShadeIT {
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BuiltJarDstu2ShadeIT.class);
- @BeforeClass
- public static void beforeClass() {
- System.setProperty("javax.xml.stream.XMLInputFactory", "com.ctc.wstx.stax.WstxInputFactory");
- System.setProperty("javax.xml.stream.XMLOutputFactory", "com.ctc.wstx.stax.WstxOutputFactory");
- }
-
@Test
public void testParserXml() throws Exception {
- try {
- Class.forName("com.ctc.wstx.stax.WstxOutputFactory");
- } catch (ClassNotFoundException e) {
- return;
- }
-
+
FhirContext ctx = FhirContext.forDstu2();
-
+
Patient p = new Patient();
p.addIdentifier().setSystem("system");
-
+
String str = ctx.newXmlParser().encodeResourceToString(p);
Patient p2 = ctx.newXmlParser().parseResource(Patient.class, str);
-
+
assertEquals("system", p2.getIdentifierFirstRep().getSystemElement().getValueAsString());
}
-
+
@Test
public void testParserJson() {
-
+
FhirContext ctx = FhirContext.forDstu2();
-
+
Observation o = new Observation();
o.getCode().setText("TEXT");
o.setValue(new QuantityDt(123));
o.addIdentifier().setSystem("system");
-
+
String str = ctx.newJsonParser().encodeResourceToString(o);
Observation p2 = ctx.newJsonParser().parseResource(Observation.class, str);
-
+
assertEquals("TEXT", p2.getCode().getText());
-
+
QuantityDt dt = (QuantityDt) p2.getValue();
dt.getComparatorElement().getValueAsEnum();
-
+
}
/**
@@ -83,7 +72,7 @@ public void testClient() {
// this is good
}
}
-
+
/**
* Android does not like duplicate entries in the JAR
*/
@@ -105,15 +94,15 @@ public void testJarContents() throws Exception {
if (file.getName().contains("original.jar")) {
continue;
}
-
+
ourLog.info("Testing file: {}", file);
ZipFile zip = new ZipFile(file);
-
+
int totalClasses = 0;
int totalMethods = 0;
TreeSet topMethods = new TreeSet();
-
+
try {
Set names = new HashSet();
for (Enumeration extends ZipEntry> iter = zip.entries(); iter.hasMoreElements();) {
@@ -122,16 +111,16 @@ public void testJarContents() throws Exception {
if (!names.add(nextName)) {
throw new Exception("File " + file + " contains duplicate contents: " + nextName);
}
-
+
if (nextName.contains("$") == false) {
if (nextName.endsWith(".class")) {
String className = nextName.replace("/", ".").replace(".class", "");
try {
- Class> clazz = Class.forName(className);
- int methodCount = clazz.getMethods().length;
- topMethods.add(new ClassMethodCount(className, methodCount));
- totalClasses++;
- totalMethods += methodCount;
+ Class> clazz = Class.forName(className);
+ int methodCount = clazz.getMethods().length;
+ topMethods.add(new ClassMethodCount(className, methodCount));
+ totalClasses++;
+ totalMethods += methodCount;
} catch (NoClassDefFoundError e) {
// ignore
} catch (ClassNotFoundException e) {
@@ -140,11 +129,11 @@ public void testJarContents() throws Exception {
}
}
}
-
+
ourLog.info("File {} contains {} entries", file, names.size());
ourLog.info("Total classes {} - Total methods {}", totalClasses, totalMethods);
- ourLog.info("Top classes {}", new ArrayList(topMethods).subList(Math.max(0,topMethods.size() - 10), topMethods.size()));
-
+ ourLog.info("Top classes {}", new ArrayList(topMethods).subList(Math.max(0, topMethods.size() - 10), topMethods.size()));
+
} finally {
zip.close();
}
@@ -155,7 +144,7 @@ private static class ClassMethodCount implements Comparable {
private String myClassName;
private int myMethodCount;
-
+
public ClassMethodCount(String theClassName, int theMethodCount) {
myClassName = theClassName;
myMethodCount = theMethodCount;
@@ -186,7 +175,7 @@ public int getMethodCount() {
public void setMethodCount(int theMethodCount) {
myMethodCount = theMethodCount;
}
-
+
}
-
+
}
diff --git a/hapi-fhir-android/src/test/java/ca/uhn/fhir/android/client/GenericClientDstu3IT.java b/hapi-fhir-android/src/test/java/ca/uhn/fhir/android/client/GenericClientDstu3IT.java
new file mode 100644
index 000000000000..c44b29349175
--- /dev/null
+++ b/hapi-fhir-android/src/test/java/ca/uhn/fhir/android/client/GenericClientDstu3IT.java
@@ -0,0 +1,996 @@
+package ca.uhn.fhir.android.client;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.input.ReaderInputStream;
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.client.methods.HttpUriRequest;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.BasicStatusLine;
+import org.hl7.fhir.dstu3.model.*;
+import org.hl7.fhir.dstu3.model.Bundle.BundleType;
+import org.junit.*;
+import org.mockito.ArgumentCaptor;
+import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.context.FhirVersionEnum;
+import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
+import ca.uhn.fhir.model.primitive.DateTimeDt;
+import ca.uhn.fhir.model.primitive.StringDt;
+import ca.uhn.fhir.model.primitive.UriDt;
+import ca.uhn.fhir.parser.IParser;
+import ca.uhn.fhir.rest.api.MethodOutcome;
+import ca.uhn.fhir.rest.api.PreferReturnEnum;
+import ca.uhn.fhir.rest.client.IGenericClient;
+import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
+import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
+import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
+import ca.uhn.fhir.rest.client.interceptor.UserInfoInterceptor;
+import ca.uhn.fhir.rest.server.Constants;
+import ca.uhn.fhir.rest.server.EncodingEnum;
+import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
+import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
+import ca.uhn.fhir.util.TestUtil;
+import ca.uhn.fhir.util.UrlUtil;
+import ca.uhn.fhir.util.VersionUtil;
+
+public class GenericClientDstu3IT {
+
+
+ private static FhirContext ourCtx;
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientDstu3IT.class);
+ private int myAnswerCount;
+ private HttpClient myHttpClient;
+ private HttpResponse myHttpResponse;
+
+ @Before
+ public void before() {
+ myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
+ ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
+ ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
+ myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
+ myAnswerCount = 0;
+
+ }
+
+ private String expectedUserAgent() {
+ return "HAPI-FHIR/" + VersionUtil.getVersion() + " (FHIR Client; FHIR " + FhirVersionEnum.DSTU3.getFhirVersionString() + "/DSTU3; apache)";
+ }
+
+
+ private String extractBodyAsString(ArgumentCaptor capt) throws IOException {
+ String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent(), "UTF-8");
+ return body;
+ }
+
+ /**
+ * TODO: narratives don't work without stax
+ */
+ @Test
+ @Ignore
+ public void testBinaryCreateWithFhirContentType() throws Exception {
+ IParser p = ourCtx.newXmlParser();
+
+ OperationOutcome conf = new OperationOutcome();
+ conf.getText().setDivAsString("OK!");
+
+ final String respString = p.encodeResourceToString(conf);
+ ArgumentCaptor 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()).thenAnswer(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ Patient pt = new Patient();
+ pt.getText().setDivAsString("A PATIENT");
+
+ Binary bin = new Binary();
+ bin.setContent(ourCtx.newJsonParser().encodeResourceToString(pt).getBytes("UTF-8"));
+ bin.setContentType(Constants.CT_FHIR_JSON);
+ client.create().resource(bin).execute();
+
+ ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
+
+ assertEquals("http://example.com/fhir/Binary", capt.getAllValues().get(0).getURI().toASCIIString());
+ validateUserAgent(capt);
+
+ assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
+ assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
+ Binary output = ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt));
+ assertEquals(Constants.CT_FHIR_JSON, output.getContentType());
+
+ Patient outputPt = (Patient) ourCtx.newJsonParser().parseResource(new String(output.getContent(), "UTF-8"));
+ assertEquals("A PATIENT
", outputPt.getText().getDivAsString());
+ }
+
+ /**
+ * See #150
+ */
+ @Test
+ public void testNullAndEmptyParamValuesAreIgnored() throws Exception {
+ ArgumentCaptor capt = prepareClientForSearchResponse();
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+ int idx = 0;
+
+ //@formatter:off
+ client
+ .search()
+ .forResource(Patient.class)
+ .where(Patient.FAMILY.matches().value((String)null))
+ .and(Patient.BIRTHDATE.exactly().day((Date)null))
+ .and(Patient.GENDER.exactly().code((String)null))
+ .and(Patient.ORGANIZATION.hasId((String)null))
+ .returnBundle(Bundle.class)
+ .execute();
+ //@formatter:on
+
+ assertEquals("http://example.com/fhir/Patient?_format=json", capt.getAllValues().get(idx).getURI().toString());
+ idx++;
+
+ }
+
+
+
+ /**
+ * TODO: narratives don't work without stax
+ */
+ @Test
+ @Ignore
+ public void testBinaryCreateWithNoContentType() throws Exception {
+ IParser p = ourCtx.newJsonParser();
+
+ OperationOutcome conf = new OperationOutcome();
+ conf.getText().setDivAsString("OK!");
+
+ final String respString = p.encodeResourceToString(conf);
+ ArgumentCaptor 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_JSON_NEW + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ Binary bin = new Binary();
+ bin.setContent(new byte[] { 0, 1, 2, 3, 4 });
+ client.create().resource(bin).execute();
+
+ ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
+
+ assertEquals("http://example.com/fhir/Binary", capt.getAllValues().get(0).getURI().toASCIIString());
+ validateUserAgent(capt);
+
+ assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
+ assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
+ assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt)).getContent());
+
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ public void testClientFailures() throws Exception {
+ ArgumentCaptor 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()).thenThrow(IllegalStateException.class, RuntimeException.class, Exception.class);
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ try {
+ client.read().resource(Patient.class).withId("1").execute();
+ fail();
+ } catch (FhirClientConnectionException e) {
+ assertEquals("java.lang.IllegalStateException", e.getMessage());
+ }
+
+ try {
+ client.read().resource(Patient.class).withId("1").execute();
+ fail();
+ } catch (RuntimeException e) {
+ assertEquals("java.lang.RuntimeException", e.toString());
+ }
+
+ try {
+ client.read().resource(Patient.class).withId("1").execute();
+ fail();
+ } catch (FhirClientConnectionException e) {
+ assertEquals("java.lang.Exception", e.getMessage());
+ }
+ }
+
+
+
+ /**
+ * TODO: narratives don't work without stax
+ */
+ @Test
+ @Ignore
+ public void testCreateWithPreferRepresentationServerReturnsResource() throws Exception {
+ final IParser p = ourCtx.newJsonParser();
+
+ final Patient resp1 = new Patient();
+ resp1.getText().setDivAsString("FINAL VALUE");
+
+ ArgumentCaptor 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.getAllHeaders()).thenAnswer(new Answer() {
+ @Override
+ public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
+ }
+ });
+ when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON_NEW + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ myAnswerCount++;
+ return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ Patient pt = new Patient();
+ pt.getText().setDivAsString("A PATIENT");
+
+ MethodOutcome outcome = client.create().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute();
+
+ assertEquals(1, myAnswerCount);
+ assertNull(outcome.getOperationOutcome());
+ assertNotNull(outcome.getResource());
+
+ assertEquals("FINAL VALUE
", ((Patient) outcome.getResource()).getText().getDivAsString());
+
+ assertEquals(myAnswerCount, capt.getAllValues().size());
+ assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
+ }
+
+
+
+
+
+
+
+ @Test
+ public void testForceConformance() throws Exception {
+ final IParser p = ourCtx.newJsonParser();
+
+ final Conformance conf = new Conformance();
+ conf.setCopyright("COPY");
+
+ final Patient patient = new Patient();
+ patient.addName().addFamily("FAM");
+
+ ArgumentCaptor 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_JSON_NEW + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ private int myCount = 0;
+
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ final String respString;
+ if (myCount == 1 || myCount == 2) {
+ ourLog.info("Encoding patient");
+ respString = p.encodeResourceToString(patient);
+ } else {
+ ourLog.info("Encoding conformance");
+ respString = p.encodeResourceToString(conf);
+ }
+ myCount++;
+ return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
+ }
+ });
+
+ ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://testForceConformance.com/fhir");
+
+ client.read().resource("Patient").withId("1").execute();
+ assertEquals(2, capt.getAllValues().size());
+ assertEquals("http://testForceConformance.com/fhir/metadata?_format=json", capt.getAllValues().get(0).getURI().toASCIIString());
+ assertEquals("http://testForceConformance.com/fhir/Patient/1?_format=json", capt.getAllValues().get(1).getURI().toASCIIString());
+
+ client.read().resource("Patient").withId("1").execute();
+ assertEquals(3, capt.getAllValues().size());
+ assertEquals("http://testForceConformance.com/fhir/Patient/1?_format=json", capt.getAllValues().get(2).getURI().toASCIIString());
+
+ client.forceConformanceCheck();
+ assertEquals(4, capt.getAllValues().size());
+ assertEquals("http://testForceConformance.com/fhir/metadata?_format=json", capt.getAllValues().get(3).getURI().toASCIIString());
+ }
+
+ @Test
+ public void testHttp499() throws Exception {
+ ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
+ when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
+ when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 499, "Wacky Message"));
+ when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader("HELLO"), StandardCharsets.UTF_8);
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ try {
+ client.read().resource(Patient.class).withId("1").execute();
+ fail();
+ } catch (UnclassifiedServerFailureException e) {
+ assertEquals("ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException: HTTP 499 Wacky Message", e.toString());
+ assertEquals("HELLO", e.getResponseBody());
+ }
+
+ }
+
+ @Test
+ public void testHttp501() throws Exception {
+ ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class);
+ when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
+ when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 501, "Not Implemented"));
+ when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader("not implemented"), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ try {
+ client.read().resource(Patient.class).withId("1").execute();
+ fail();
+ } catch (NotImplementedOperationException e) {
+ assertEquals("HTTP 501 Not Implemented", e.getMessage());
+ }
+
+ }
+
+ @SuppressWarnings("deprecation")
+ @Test
+ public void testInvalidConformanceCall() {
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+ try {
+ client.conformance();
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("Must call fetchConformance() instead of conformance() for RI/STU3+ structures", e.getMessage());
+ }
+ }
+
+
+
+ @Test
+ public void testPutDoesntForceAllIdsJson() throws Exception {
+ IParser p = ourCtx.newJsonParser();
+
+ Patient patient = new Patient();
+ patient.setId("PATIENT1");
+ patient.addName().addFamily("PATIENT1");
+
+ Bundle bundle = new Bundle();
+ bundle.setId("BUNDLE1");
+ bundle.addEntry().setResource(patient);
+
+ final String encoded = p.encodeResourceToString(bundle);
+ assertEquals("{\"resourceType\":\"Bundle\",\"id\":\"BUNDLE1\",\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"PATIENT1\",\"name\":[{\"family\":[\"PATIENT1\"]}]}}]}", encoded);
+
+ ArgumentCaptor 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_JSON_NEW + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(encoded), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+ //@formatter:off
+ client
+ .update()
+ .resource(bundle)
+ .prefer(PreferReturnEnum.REPRESENTATION)
+ .encodedJson()
+ .execute();
+ //@formatter:on
+
+ HttpPut httpRequest = (HttpPut) capt.getValue();
+ assertEquals("http://example.com/fhir/Bundle/BUNDLE1?_format=json", httpRequest.getURI().toASCIIString());
+
+ String requestString = IOUtils.toString(httpRequest.getEntity().getContent(), StandardCharsets.UTF_8);
+ assertEquals(encoded, requestString);
+ }
+
+ @Test
+ public void testResponseHasContentTypeMissing() throws Exception {
+ IParser p = ourCtx.newJsonParser();
+ Patient patient = new Patient();
+ patient.addName().addFamily("FAM");
+ final String respString = p.encodeResourceToString(patient);
+
+ ArgumentCaptor 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(null);
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+ try {
+ client.read().resource(Patient.class).withId("1").execute();
+ fail();
+ } catch (NonFhirResponseException e) {
+ assertEquals("Response contains no Content-Type", e.getMessage());
+ }
+
+ // Patient resp = client.read().resource(Patient.class).withId("1").execute();
+ // assertEquals("FAM", resp.getNameFirstRep().getFamilyAsSingleString());
+ }
+
+ @Test
+ public void testResponseHasContentTypeNonFhir() throws Exception {
+ IParser p = ourCtx.newJsonParser();
+ Patient patient = new Patient();
+ patient.addName().addFamily("FAM");
+ final String respString = p.encodeResourceToString(patient);
+
+ ArgumentCaptor 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", "text/plain"));
+ // when(myHttpResponse.getEntity().getContentType()).thenReturn(null);
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+ try {
+ client.read().resource(Patient.class).withId("1").execute();
+ fail();
+ } catch (NonFhirResponseException e) {
+ assertEquals("Response contains non FHIR Content-Type 'text/plain' : {\"resourceType\":\"Patient\",\"name\":[{\"family\":[\"FAM\"]}]}", e.getMessage());
+ }
+
+ // Patient resp = client.read().resource(Patient.class).withId("1").execute();
+ // assertEquals("FAM", resp.getNameFirstRep().getFamilyAsSingleString());
+ }
+
+ @Test
+ public void testSearchByDate() throws Exception {
+ final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
+
+ ArgumentCaptor 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_JSON + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).then(new Answer() {
+ @Override
+ public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+ int idx = 0;
+
+ DateTimeDt now = DateTimeDt.withCurrentTime();
+ String dateString = now.getValueAsString().substring(0, 10);
+
+ //@formatter:off
+ client.search()
+ .forResource("Patient")
+ .where(Patient.BIRTHDATE.after().day(dateString))
+ .returnBundle(Bundle.class)
+ .execute();
+ //@formatter:on
+ assertEquals("http://example.com/fhir/Patient?birthdate=gt"+dateString + "&_format=json", capt.getAllValues().get(idx).getURI().toString());
+ idx++;
+
+ }
+
+
+ @Test
+ public void testSearchByString() throws Exception {
+ final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
+
+ ArgumentCaptor 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_JSON + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).then(new Answer() {
+ @Override
+ public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+ int idx = 0;
+
+ //@formatter:off
+ client.search()
+ .forResource("Patient")
+ .where(Patient.NAME.matches().value("AAA"))
+ .returnBundle(Bundle.class)
+ .execute();
+ //@formatter:on
+ assertEquals("http://example.com/fhir/Patient?name=AAA&_format=json", capt.getAllValues().get(idx).getURI().toString());
+ idx++;
+
+ //@formatter:off
+ client.search()
+ .forResource("Patient")
+ .where(Patient.NAME.matches().value(new StringDt("AAA")))
+ .returnBundle(Bundle.class)
+ .execute();
+ //@formatter:on
+ assertEquals("http://example.com/fhir/Patient?name=AAA&_format=json", capt.getAllValues().get(idx).getURI().toString());
+ idx++;
+
+ //@formatter:off
+ client.search()
+ .forResource("Patient")
+ .where(Patient.NAME.matches().values("AAA", "BBB"))
+ .returnBundle(Bundle.class)
+ .execute();
+ //@formatter:on
+ assertEquals("http://example.com/fhir/Patient?name=AAA,BBB&_format=json", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
+ idx++;
+
+ //@formatter:off
+ client.search()
+ .forResource("Patient")
+ .where(Patient.NAME.matches().values(Arrays.asList("AAA", "BBB")))
+ .returnBundle(Bundle.class)
+ .execute();
+ //@formatter:on
+ assertEquals("http://example.com/fhir/Patient?name=AAA,BBB&_format=json", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
+ idx++;
+
+ //@formatter:off
+ client.search()
+ .forResource("Patient")
+ .where(Patient.NAME.matchesExactly().value("AAA"))
+ .returnBundle(Bundle.class)
+ .execute();
+ //@formatter:on
+ assertEquals("http://example.com/fhir/Patient?name%3Aexact=AAA&_format=json", capt.getAllValues().get(idx).getURI().toString());
+ idx++;
+
+ //@formatter:off
+ client.search()
+ .forResource("Patient")
+ .where(Patient.NAME.matchesExactly().value(new StringDt("AAA")))
+ .returnBundle(Bundle.class)
+ .execute();
+ //@formatter:on
+ assertEquals("http://example.com/fhir/Patient?name%3Aexact=AAA&_format=json", capt.getAllValues().get(idx).getURI().toString());
+ idx++;
+
+ }
+
+ @Test
+ public void testSearchByUrl() throws Exception {
+ final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
+
+ ArgumentCaptor 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_JSON + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).then(new Answer() {
+ @Override
+ public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+ int idx = 0;
+
+ //@formatter:off
+ client.search()
+ .forResource("Device")
+ .where(Device.URL.matches().value("http://foo.com"))
+ .returnBundle(Bundle.class)
+ .execute();
+ //@formatter:on
+ assertEquals("http://example.com/fhir/Device?url=http://foo.com&_format=json", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
+ idx++;
+
+ }
+
+ @Test
+ public void testAcceptHeaderWithEncodingSpecified() throws Exception {
+ final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
+
+ ArgumentCaptor 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_JSON + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).then(new Answer() {
+ @Override
+ public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+ int idx = 0;
+
+ //@formatter:off
+ client.setEncoding(EncodingEnum.JSON);
+ client.search()
+ .forResource("Device")
+ .returnBundle(Bundle.class)
+ .execute();
+ //@formatter:on
+ assertEquals("http://example.com/fhir/Device?_format=json", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
+ assertEquals("application/fhir+json;q=1.0, application/json+fhir;q=0.9", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_ACCEPT).getValue());
+ idx++;
+
+ //@formatter:off
+ client.setEncoding(EncodingEnum.XML);
+ client.search()
+ .forResource("Device")
+ .returnBundle(Bundle.class)
+ .execute();
+ //@formatter:on
+ assertEquals("http://example.com/fhir/Device?_format=xml", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
+ assertEquals("application/fhir+xml;q=1.0, application/xml+fhir;q=0.9", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_ACCEPT).getValue());
+ idx++;
+
+ }
+
+ @Test
+ public void testSearchForUnknownType() throws Exception {
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+ try {
+ client.search(new UriDt("?aaaa"));
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("Unable to determine the resource type from the given URI: ?aaaa", e.getMessage());
+ }
+ }
+
+ private ArgumentCaptor prepareClientForSearchResponse() throws IOException, ClientProtocolException {
+ final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
+
+ ArgumentCaptor 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_JSON + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).then(new Answer() {
+ @Override
+ public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
+ }
+ });
+ return capt;
+ }
+
+
+
+ @Test
+ public void testTransactionWithInvalidBody() throws Exception {
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ // Transaction
+ try {
+ client.transaction().withBundle("FOO");
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage());
+ }
+
+ // Create
+ try {
+ client.create().resource("FOO").execute();
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage());
+ }
+
+ // Update
+ try {
+ client.update().resource("FOO").execute();
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage());
+ }
+
+ // Validate
+ try {
+ client.validate().resource("FOO").execute();
+ fail();
+ } catch (IllegalArgumentException e) {
+ assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage());
+ }
+
+
+ }
+
+ /**
+ * TODO: narratives don't work without stax
+ */
+ @Test
+ @Ignore
+ public void testUpdateById() throws Exception {
+ IParser p = ourCtx.newJsonParser();
+
+ OperationOutcome conf = new OperationOutcome();
+ conf.getText().setDivAsString("OK!");
+
+ final String respString = p.encodeResourceToString(conf);
+ ArgumentCaptor 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_JSON_NEW + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ Patient pt = new Patient();
+ pt.setId("222");
+ pt.getText().setDivAsString("A PATIENT");
+
+ client.update().resource(pt).withId("111").execute();
+
+ ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
+
+ assertEquals("http://example.com/fhir/Patient/111", capt.getAllValues().get(0).getURI().toASCIIString());
+ validateUserAgent(capt);
+
+ assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
+ assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
+ String body = extractBodyAsString(capt);
+ assertThat(body, containsString(""));
+ }
+
+ /**
+ * TODO: narratives don't work without stax
+ */
+ @Test
+ @Ignore
+ public void testUpdateWithPreferRepresentationServerReturnsOO() throws Exception {
+ final IParser p = ourCtx.newJsonParser();
+
+ final OperationOutcome resp0 = new OperationOutcome();
+ resp0.getText().setDivAsString("OK!");
+
+ final Patient resp1 = new Patient();
+ resp1.getText().setDivAsString("FINAL VALUE");
+
+ ArgumentCaptor 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.getAllHeaders()).thenAnswer(new Answer() {
+ @Override
+ public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
+ }
+ });
+ when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON_NEW + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ if (myAnswerCount++ == 0) {
+ return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8"));
+ } else {
+ return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
+ }
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ Patient pt = new Patient();
+ pt.setId("Patient/222");
+ pt.getText().setDivAsString("A PATIENT");
+
+ MethodOutcome outcome = client.update().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute();
+
+ assertEquals(2, myAnswerCount);
+ assertNotNull(outcome.getOperationOutcome());
+ assertNotNull(outcome.getResource());
+
+ assertEquals("OK!
", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString());
+ assertEquals("FINAL VALUE
", ((Patient) outcome.getResource()).getText().getDivAsString());
+
+ assertEquals(myAnswerCount, capt.getAllValues().size());
+ assertEquals("http://example.com/fhir/Patient/222", capt.getAllValues().get(0).getURI().toASCIIString());
+ assertEquals("http://foo.com/base/Patient/222/_history/3", capt.getAllValues().get(1).getURI().toASCIIString());
+ }
+
+ @Test
+ public void testUpdateWithPreferRepresentationServerReturnsResource() throws Exception {
+ final IParser p = ourCtx.newJsonParser();
+
+ final Patient resp1 = new Patient();
+ resp1.setActive(true);
+
+ ArgumentCaptor 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.getAllHeaders()).thenAnswer(new Answer() {
+ @Override
+ public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
+ }
+ });
+ when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON_NEW + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ myAnswerCount++;
+ return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ Patient pt = new Patient();
+ pt.setId("Patient/222");
+ pt.getText().setDivAsString("A PATIENT");
+
+ MethodOutcome outcome = client.update().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute();
+
+ assertEquals(1, myAnswerCount);
+ assertNull(outcome.getOperationOutcome());
+ assertNotNull(outcome.getResource());
+
+ assertEquals(true, ((Patient) outcome.getResource()).getActive());
+
+ assertEquals(myAnswerCount, capt.getAllValues().size());
+ assertEquals("http://example.com/fhir/Patient/222?_format=json", capt.getAllValues().get(0).getURI().toASCIIString());
+ }
+
+
+ @Test
+ public void testUserAgentForConformance() throws Exception {
+ IParser p = ourCtx.newJsonParser();
+
+ Conformance conf = new Conformance();
+ conf.setCopyright("COPY");
+
+ final String respString = p.encodeResourceToString(conf);
+ ArgumentCaptor 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_JSON_NEW + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ client.fetchConformance().ofType(Conformance.class).execute();
+ assertEquals("http://example.com/fhir/metadata?_format=json", capt.getAllValues().get(0).getURI().toASCIIString());
+ validateUserAgent(capt);
+ }
+
+
+ /**
+ * TODO: narratives don't work without stax
+ */
+ @Test
+ @Ignore
+ public void testValidate() throws Exception {
+ final IParser p = ourCtx.newXmlParser();
+
+ final OperationOutcome resp0 = new OperationOutcome();
+ resp0.getText().setDivAsString("OK!");
+
+ ArgumentCaptor 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.getAllHeaders()).thenAnswer(new Answer() {
+ @Override
+ public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
+ return new Header[] {};
+ }
+ });
+ when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() {
+ @Override
+ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+
+ Patient pt = new Patient();
+ pt.setId("Patient/222");
+ pt.getText().setDivAsString("A PATIENT");
+
+ MethodOutcome outcome = client.validate().resource(pt).execute();
+
+ assertNotNull(outcome.getOperationOutcome());
+ assertEquals("OK!
", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString());
+
+ }
+
+ private void validateUserAgent(ArgumentCaptor capt) {
+ assertEquals(1, capt.getAllValues().get(0).getHeaders("User-Agent").length);
+ assertEquals(expectedUserAgent(), capt.getAllValues().get(0).getHeaders("User-Agent")[0].getValue());
+ }
+
+ @AfterClass
+ public static void afterClassClearContext() {
+ TestUtil.clearAllStaticFieldsForUnitTest();
+ }
+
+ @BeforeClass
+ public static void beforeClass() {
+
+ // Force StAX to fail like it will on android
+ System.setProperty("javax.xml.stream.XMLInputFactory", "FOO");
+ System.setProperty("javax.xml.stream.XMLOutputFactory", "FOO");
+
+ ourCtx = FhirContext.forDstu3();
+ }
+
+}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java
index f0a1a32d277d..3bfb5bee98cb 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java
@@ -1,7 +1,5 @@
package ca.uhn.fhir.rest.client;
-import static org.apache.commons.lang3.StringUtils.isNotBlank;
-
/*
* #%L
* HAPI FHIR - Core Library
@@ -22,33 +20,20 @@
* #L%
*/
-import java.io.ByteArrayInputStream;
+import static org.apache.commons.lang3.StringUtils.isNotBlank;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
-import org.hl7.fhir.instance.model.api.IBase;
-import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
-import org.hl7.fhir.instance.model.api.IBaseResource;
-import org.hl7.fhir.instance.model.api.IIdType;
-import org.hl7.fhir.instance.model.api.IPrimitiveType;
-
-import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
-import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
-import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
-import ca.uhn.fhir.context.FhirContext;
-import ca.uhn.fhir.context.RuntimeResourceDefinition;
+import org.hl7.fhir.instance.model.api.*;
+
+import ca.uhn.fhir.context.*;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.SummaryEnum;
@@ -67,6 +52,7 @@
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.util.OperationOutcomeUtil;
+import ca.uhn.fhir.util.XmlUtil;
public abstract class BaseClient implements IRestfulClient {
@@ -105,6 +91,11 @@ public abstract class BaseClient implements IRestfulClient {
if ("true".equals(System.getProperty(HAPI_CLIENT_KEEPRESPONSES))) {
setKeepResponses(true);
}
+
+ if (XmlUtil.isStaxPresent() == false) {
+ myEncoding = EncodingEnum.JSON;
+ }
+
}
protected Map> createExtraParams() {
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java
index 22a17a003abd..8c3f4e86963f 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java
@@ -63,6 +63,8 @@ public class Constants {
public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
public static final String HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY = CT_FHIR_XML + ";q=1.0, " + CT_FHIR_JSON + ";q=1.0";
public static final String HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY = CT_FHIR_XML_NEW + ";q=1.0, " + CT_FHIR_JSON_NEW + ";q=1.0, " + HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY.replace("1.0", "0.9");
+ public static final String HEADER_ACCEPT_VALUE_XML_NON_LEGACY = CT_FHIR_XML_NEW + ";q=1.0, " + CT_FHIR_XML + ";q=0.9";
+ public static final String HEADER_ACCEPT_VALUE_JSON_NON_LEGACY = CT_FHIR_JSON_NEW + ";q=1.0, " + CT_FHIR_JSON + ";q=0.9";
public static final String HEADER_ALLOW = "Allow";
public static final String HEADER_AUTHORIZATION = "Authorization";
public static final String HEADER_AUTHORIZATION_VALPREFIX_BASIC = "Basic ";
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java
index 0fcf91941637..de786b8b1569 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java
@@ -787,9 +787,17 @@ public static void addAcceptHeaderToRequest(EncodingEnum theEncoding, IHttpReque
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY);
}
} else if (theEncoding == EncodingEnum.JSON) {
- theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON);
+ if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2) == false) {
+ theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON);
+ } else {
+ theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY);
+ }
} else if (theEncoding == EncodingEnum.XML) {
- theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML);
+ if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2) == false) {
+ theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML);
+ } else {
+ theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY);
+ }
}
}
diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java
index 98f625792103..55d6cf74e70b 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/XmlUtil.java
@@ -19,26 +19,12 @@
* limitations under the License.
* #L%
*/
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
-import java.io.Writer;
+import java.io.*;
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 javax.xml.stream.*;
import org.apache.commons.lang3.StringEscapeUtils;
import org.codehaus.stax2.XMLOutputFactory2;
@@ -47,6 +33,7 @@
import com.ctc.wstx.api.WstxInputProperties;
import com.ctc.wstx.stax.WstxOutputFactory;
+import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.util.jar.DependencyLogFactory;
import ca.uhn.fhir.util.jar.IDependencyLog;
@@ -62,6 +49,7 @@ public class XmlUtil {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlUtil.class);
private static Throwable ourNextException;
private static volatile XMLOutputFactory ourOutputFactory;
+ private static Boolean ourStaxPresent;
private static final Map VALID_ENTITY_NAMES;
private static final ExtendedEntityReplacingXmlResolver XML_RESOLVER = new ExtendedEntityReplacingXmlResolver();
@@ -1528,8 +1516,8 @@ private static XMLOutputFactory createOutputFactory() throws FactoryConfiguratio
// ok
}
- XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
-
+ XMLOutputFactory outputFactory = newOutputFactory();
+
if (!ourHaveLoggedStaxImplementation) {
logStaxImplementation(outputFactory.getClass());
}
@@ -1601,8 +1589,7 @@ private static XMLInputFactory getOrCreateInputFactory() throws FactoryConfigura
// ok
}
- XMLInputFactory inputFactory;
- inputFactory = XMLInputFactory.newInstance();
+ XMLInputFactory inputFactory = newInputFactory();
if (!ourHaveLoggedStaxImplementation) {
logStaxImplementation(inputFactory.getClass());
@@ -1645,7 +1632,6 @@ private static XMLInputFactory getOrCreateInputFactory() throws FactoryConfigura
return ourInputFactory;
}
-
private static XMLOutputFactory getOrCreateOutputFactory() throws FactoryConfigurationError {
if (ourOutputFactory == null) {
ourOutputFactory = createOutputFactory();
@@ -1661,6 +1647,29 @@ private static void logStaxImplementation(Class> theClass) {
ourHaveLoggedStaxImplementation = true;
}
+
+ static XMLInputFactory newInputFactory() throws FactoryConfigurationError {
+ XMLInputFactory inputFactory;
+ try {
+ inputFactory = XMLInputFactory.newInstance();
+ throwUnitTestExceptionIfConfiguredToDoSo();
+ } catch (Throwable e) {
+ throw new ConfigurationException("Unable to initialize StAX - XML processing is disabled", e);
+ }
+ return inputFactory;
+ }
+
+ static XMLOutputFactory newOutputFactory() throws FactoryConfigurationError {
+ XMLOutputFactory outputFactory;
+ try {
+ outputFactory = XMLOutputFactory.newInstance();
+ throwUnitTestExceptionIfConfiguredToDoSo();
+ } catch (Throwable e) {
+ throw new ConfigurationException("Unable to initialize StAX - XML processing is disabled", e);
+ }
+ return outputFactory;
+ }
+
/**
* FOR UNIT TESTS ONLY - Throw this exception for the next operation
*/
@@ -1691,6 +1700,26 @@ public Object resolveEntity(String thePublicID, String theSystemID, String theBa
}
}
+ /**
+ * This method will return true
if a StAX XML parsing library is present
+ * on the classpath
+ */
+ public static boolean isStaxPresent() {
+ Boolean retVal = ourStaxPresent;
+ if (retVal == null) {
+ try {
+ newInputFactory();
+ ourStaxPresent = Boolean.TRUE;
+ retVal = Boolean.TRUE;
+ } catch (ConfigurationException e) {
+ ourLog.info("StAX not detected on classpath, XML processing will be disabled");
+ ourStaxPresent = Boolean.FALSE;
+ retVal = Boolean.FALSE;
+ }
+ }
+ return retVal;
+ }
+
public static class MyEscaper implements EscapingWriterFactory {
@Override
diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml
index 508e0733b333..f84e913f1131 100644
--- a/hapi-fhir-jacoco/pom.xml
+++ b/hapi-fhir-jacoco/pom.xml
@@ -232,6 +232,7 @@
hapi-fhir-structures-dstu3/target/jacoco.exec
hapi-fhir-jpaserver-base/target/jacoco.exec
hapi-fhir-client-okhttp/target/jacoco.exec
+ hapi-fhir-android/target/jacoco.exec
diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java
index 2605b3c65ef1..c24b554f6ffc 100644
--- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java
+++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/client/GenericJaxRsClientDstu3Test.java
@@ -1644,7 +1644,7 @@ public void testSearchByPostUseJson() throws Exception {
assertEquals("name=james", ourRequestBodyString);
assertEquals("application/x-www-form-urlencoded", ourRequestContentType);
- assertEquals(Constants.CT_FHIR_JSON, ourRequestFirstHeaders.get("Accept").getValue());
+ assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, ourRequestFirstHeaders.get("Accept").getValue());
}
@Test
diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/util/XmlUtilTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/util/XmlUtilTest.java
index 52c0828c7e1e..f2ecfcf9353a 100644
--- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/util/XmlUtilTest.java
+++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/util/XmlUtilTest.java
@@ -1,14 +1,45 @@
package ca.uhn.fhir.util;
+import static org.junit.Assert.*;
+
import java.io.StringReader;
import java.io.StringWriter;
+import javax.xml.stream.FactoryConfigurationError;
+
+import org.junit.After;
import org.junit.AfterClass;
import org.junit.Test;
public class XmlUtilTest {
-
+ @Test
+ public void testCreateInputFactoryWithException() {
+ XmlUtil.setThrowExceptionForUnitTest(new Error("FOO ERROR"));
+ try {
+ XmlUtil.newInputFactory();
+ fail();
+ } catch (Exception e) {
+ assertEquals("Unable to initialize StAX - XML processing is disabled", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testCreateOutputFactoryWithException() {
+ XmlUtil.setThrowExceptionForUnitTest(new Error("FOO ERROR"));
+ try {
+ XmlUtil.newOutputFactory();
+ fail();
+ } catch (Exception e) {
+ assertEquals("Unable to initialize StAX - XML processing is disabled", e.getMessage());
+ }
+ }
+
+ @After
+ public void after() {
+ XmlUtil.setThrowExceptionForUnitTest(null);
+ }
+
@Test
public void testCreateReader() throws Exception {
XmlUtil.createXmlReader(new StringReader(""));
@@ -24,7 +55,6 @@ public void testCreateStreamWriter() throws Exception {
XmlUtil.createXmlStreamWriter(new StringWriter());
}
-
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/ClientMimetypeDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/ClientMimetypeDstu3Test.java
index 782b0979478b..1b4ddce962dc 100644
--- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/ClientMimetypeDstu3Test.java
+++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/ClientMimetypeDstu3Test.java
@@ -67,7 +67,7 @@ public void testMimetypeXmlNew() throws Exception {
assertEquals("FINAL VALUE
", ((Patient) outcome.getResource()).getText().getDivAsString());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
- assertEquals("application/xml+fhir", capt.getAllValues().get(0).getFirstHeader("accept").getValue());
+ assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getFirstHeader("accept").getValue());
assertEquals("A PATIENT
", extractBodyAsString(capt));
}
@@ -87,7 +87,7 @@ public void testMimetypeXmlLegacy() throws Exception {
assertEquals("FINAL VALUE
", ((Patient) outcome.getResource()).getText().getDivAsString());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
- assertEquals("application/xml+fhir", capt.getAllValues().get(0).getFirstHeader("accept").getValue());
+ assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getFirstHeader("accept").getValue());
assertEquals("A PATIENT
", extractBodyAsString(capt));
}
@@ -107,7 +107,7 @@ public void testMimetypeJsonNew() throws Exception {
assertEquals("FINAL VALUE
", ((Patient) outcome.getResource()).getText().getDivAsString());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(Constants.CT_FHIR_JSON, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
- assertEquals("application/json+fhir", capt.getAllValues().get(0).getFirstHeader("accept").getValue());
+ assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, capt.getAllValues().get(0).getFirstHeader("accept").getValue());
assertEquals("{\"resourceType\":\"Patient\",\"text\":{\"div\":\"A PATIENT
\"}}", extractBodyAsString(capt));
}
@@ -127,7 +127,7 @@ public void testMimetypeJsonLegacy() throws Exception {
assertEquals("FINAL VALUE
", ((Patient) outcome.getResource()).getText().getDivAsString());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(Constants.CT_FHIR_JSON, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
- assertEquals("application/json+fhir", capt.getAllValues().get(0).getFirstHeader("accept").getValue());
+ assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, capt.getAllValues().get(0).getFirstHeader("accept").getValue());
assertEquals("{\"resourceType\":\"Patient\",\"text\":{\"div\":\"A PATIENT
\"}}", extractBodyAsString(capt));
}
diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java
index 1238ab9c75f1..a08031fe81de 100644
--- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java
+++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu3Test.java
@@ -64,6 +64,7 @@
import ca.uhn.fhir.rest.client.interceptor.UserInfoInterceptor;
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.rest.server.Constants;
+import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
import ca.uhn.fhir.util.TestUtil;
@@ -204,7 +205,7 @@ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable
validateUserAgent(capt);
assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
- assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
+ assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
Binary output = ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt));
assertEquals(Constants.CT_FHIR_JSON, output.getContentType());
@@ -296,7 +297,7 @@ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable
validateUserAgent(capt);
assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
- assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
+ assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt)).getContent());
}
@@ -1283,6 +1284,48 @@ public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
}
+ @Test
+ public void testAcceptHeaderWithEncodingSpecified() throws Exception {
+ final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
+
+ ArgumentCaptor 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_JSON + "; charset=UTF-8"));
+ when(myHttpResponse.getEntity().getContent()).then(new Answer() {
+ @Override
+ public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
+ return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
+ }
+ });
+
+ IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
+ int idx = 0;
+
+ //@formatter:off
+ client.setEncoding(EncodingEnum.JSON);
+ client.search()
+ .forResource("Device")
+ .returnBundle(Bundle.class)
+ .execute();
+ //@formatter:on
+ assertEquals("http://example.com/fhir/Device?_format=json", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
+ assertEquals("application/fhir+json;q=1.0, application/json+fhir;q=0.9", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_ACCEPT).getValue());
+ idx++;
+
+ //@formatter:off
+ client.setEncoding(EncodingEnum.XML);
+ client.search()
+ .forResource("Device")
+ .returnBundle(Bundle.class)
+ .execute();
+ //@formatter:on
+ assertEquals("http://example.com/fhir/Device?_format=xml", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
+ assertEquals("application/fhir+xml;q=1.0, application/xml+fhir;q=0.9", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_ACCEPT).getValue());
+ idx++;
+
+ }
+
@Test
public void testSearchForUnknownType() throws Exception {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
@@ -1458,7 +1501,7 @@ public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable
validateUserAgent(capt);
assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
- assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
+ assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
String body = extractBodyAsString(capt);
assertThat(body, containsString(""));
}
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 0c2edf322b37..d9b88888a3bc 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -55,6 +55,13 @@
parent bundle were incorrectly given the ID of the parent. Thanks
to Filip Domazet for reporting!
+
+ STU clients now use an Accept header which
+ indicates support for both the old MimeTypes
+ (e.g. application/xml+fhir]]>)
+ and the new MimeTypes
+ (e.g. application/fhir+xml]]>)
+