Fix some bugs on the generic client

This commit is contained in:
jamesagnew 2014-05-09 17:54:55 -04:00
parent b937d5c2dc
commit fb998a6a32
18 changed files with 606 additions and 506 deletions

View File

@ -20,7 +20,9 @@ package ca.uhn.fhir.parser;
* #L% * #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.IOException;
import java.io.Reader; import java.io.Reader;
@ -45,13 +47,10 @@ import javax.json.JsonValue;
import javax.json.JsonValue.ValueType; import javax.json.JsonValue.ValueType;
import javax.json.stream.JsonGenerator; import javax.json.stream.JsonGenerator;
import javax.json.stream.JsonGeneratorFactory; import javax.json.stream.JsonGeneratorFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import ch.qos.logback.core.boolex.EventEvaluator;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition; import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
@ -64,7 +63,6 @@ import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.model.api.BaseBundle; import ca.uhn.fhir.model.api.BaseBundle;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.ExtensionDt;
import ca.uhn.fhir.model.api.IElement; import ca.uhn.fhir.model.api.IElement;
@ -72,6 +70,7 @@ import ca.uhn.fhir.model.api.IIdentifiableElement;
import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions; import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.dstu.composite.ContainedDt; import ca.uhn.fhir.model.dstu.composite.ContainedDt;
@ -88,6 +87,8 @@ import ca.uhn.fhir.narrative.INarrativeGenerator;
public class JsonParser extends BaseParser implements IParser { public class JsonParser extends BaseParser implements IParser {
private static final Set<String> BUNDLE_TEXTNODE_CHILDREN; private static final Set<String> BUNDLE_TEXTNODE_CHILDREN;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParser.HeldExtension.class);
static { static {
HashSet<String> hashSet = new HashSet<String>(); HashSet<String> hashSet = new HashSet<String>();
hashSet.add("title"); hashSet.add("title");
@ -105,6 +106,48 @@ public class JsonParser extends BaseParser implements IParser {
myContext = theContext; myContext = theContext;
} }
private void addToHeldExtensions(int valueIdx, ArrayList<ArrayList<HeldExtension>> list, RuntimeChildDeclaredExtensionDefinition theDef, IElement theValue) {
list.ensureCapacity(valueIdx);
while (list.size() <= valueIdx) {
list.add(null);
}
if (list.get(valueIdx) == null) {
list.set(valueIdx, new ArrayList<JsonParser.HeldExtension>());
}
list.get(valueIdx).add(new HeldExtension(theDef, theValue));
}
private void addToHeldExtensions(int valueIdx, List<ExtensionDt> ext, ArrayList<ArrayList<HeldExtension>> list) {
if (ext.size() > 0) {
list.ensureCapacity(valueIdx);
while (list.size() <= valueIdx) {
list.add(null);
}
if (list.get(valueIdx) == null) {
list.set(valueIdx, new ArrayList<JsonParser.HeldExtension>());
}
for (ExtensionDt next : ext) {
list.get(valueIdx).add(new HeldExtension(next));
}
}
}
private void assertObjectOfType(JsonValue theResourceTypeObj, ValueType theValueType, String thePosition) {
if (theResourceTypeObj.getValueType() != theValueType) {
throw new DataFormatException("Invalid content of element " + thePosition + ", expected " + theValueType);
}
}
private JsonGenerator createJsonGenerator(Writer theWriter) {
Map<String, Object> properties = new HashMap<String, Object>(1);
if (myPrettyPrint) {
properties.put(JsonGenerator.PRETTY_PRINTING, myPrettyPrint);
}
JsonGeneratorFactory jgf = Json.createGeneratorFactory(properties);
JsonGenerator eventWriter = jgf.createGenerator(theWriter);
return eventWriter;
}
@Override @Override
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException { public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException {
JsonGenerator eventWriter = createJsonGenerator(theWriter); JsonGenerator eventWriter = createJsonGenerator(theWriter);
@ -170,173 +213,8 @@ public class JsonParser extends BaseParser implements IParser {
eventWriter.close(); eventWriter.close();
} }
@Override private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef,
public void encodeResourceToWriter(IResource theResource, Writer theWriter) throws IOException { String theChildName) throws IOException {
Validate.notNull(theResource, "Resource can not be null");
JsonGenerator eventWriter = createJsonGenerator(theWriter);
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null);
eventWriter.flush();
}
@Override
public void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException {
JsonGenerator eventWriter = createJsonGenerator(theWriter);
eventWriter.writeStartObject();
eventWriter.write("resourceType", TagList.ELEMENT_NAME);
eventWriter.writeStartArray(TagList.ATTR_CATEGORY);
for (Tag next : theTagList) {
eventWriter.writeStartObject();
if (isNotBlank(next.getTerm())) {
eventWriter.write(Tag.ATTR_TERM, next.getTerm());
}
if (isNotBlank(next.getLabel())) {
eventWriter.write(Tag.ATTR_LABEL, next.getLabel());
}
if (isNotBlank(next.getScheme())) {
eventWriter.write(Tag.ATTR_SCHEME, next.getScheme());
}
eventWriter.writeEnd();
}
eventWriter.writeEnd();
eventWriter.writeEnd();
eventWriter.flush();
}
@Override
public <T extends IResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader) {
JsonReader reader = Json.createReader(theReader);
JsonObject object = reader.readObject();
JsonValue resourceTypeObj = object.get("resourceType");
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
String resourceType = ((JsonString) resourceTypeObj).getString();
if (!"Bundle".equals(resourceType)) {
throw new DataFormatException("Trying to parse bundle but found resourceType other than 'Bundle'. Found: '" + resourceType + "'");
}
ParserState<Bundle> state = ParserState.getPreAtomInstance(myContext, theResourceType, true);
state.enteringNewElement(null, "feed");
parseBundleChildren(object, state);
state.endingElement();
Bundle retVal = state.getObject();
return retVal;
}
@Override
public <T extends IResource> T parseResource(Class<T> theResourceType, Reader theReader) {
JsonReader reader = Json.createReader(theReader);
JsonObject object = reader.readObject();
JsonValue resourceTypeObj = object.get("resourceType");
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
String resourceType = ((JsonString) resourceTypeObj).getString();
RuntimeResourceDefinition def;
if (theResourceType != null) {
def = myContext.getResourceDefinition(theResourceType);
} else {
def = myContext.getResourceDefinition(resourceType);
}
ParserState<? extends IResource> state = ParserState.getPreResourceInstance(def.getImplementingClass(), myContext, true);
state.enteringNewElement(null, def.getName());
parseChildren(object, state);
state.endingElement();
@SuppressWarnings("unchecked")
T retVal = (T) state.getObject();
return retVal;
}
@Override
public <T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString) {
return parseResource(theResourceType, new StringReader(theMessageString));
}
@Override
public TagList parseTagList(Reader theReader) {
JsonReader reader = Json.createReader(theReader);
JsonObject object = reader.readObject();
JsonValue resourceTypeObj = object.get("resourceType");
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
String resourceType = ((JsonString) resourceTypeObj).getString();
ParserState<TagList> state = ParserState.getPreTagListInstance(myContext, true);
state.enteringNewElement(null, resourceType);
parseChildren(object, state);
state.endingElement();
return state.getObject();
}
@Override
public IParser setPrettyPrint(boolean thePrettyPrint) {
myPrettyPrint = thePrettyPrint;
return this;
}
private void addToHeldExtensions(int valueIdx, ArrayList<ArrayList<HeldExtension>> list, RuntimeChildDeclaredExtensionDefinition theDef, IElement theValue) {
list.ensureCapacity(valueIdx);
while (list.size() <= valueIdx) {
list.add(null);
}
if (list.get(valueIdx) == null) {
list.set(valueIdx, new ArrayList<JsonParser.HeldExtension>());
}
list.get(valueIdx).add(new HeldExtension(theDef, theValue));
}
private void addToHeldExtensions(int valueIdx, List<ExtensionDt> ext, ArrayList<ArrayList<HeldExtension>> list) {
if (ext.size() > 0) {
list.ensureCapacity(valueIdx);
while (list.size() <= valueIdx) {
list.add(null);
}
if (list.get(valueIdx) == null) {
list.set(valueIdx, new ArrayList<JsonParser.HeldExtension>());
}
for (ExtensionDt next : ext) {
list.get(valueIdx).add(new HeldExtension(next));
}
}
}
private void assertObjectOfType(JsonValue theResourceTypeObj, ValueType theValueType, String thePosition) {
if (theResourceTypeObj.getValueType() != theValueType) {
throw new DataFormatException("Invalid content of element " + thePosition + ", expected " + theValueType);
}
}
private JsonGenerator createJsonGenerator(Writer theWriter) {
Map<String, Object> properties = new HashMap<String, Object>(1);
if (myPrettyPrint) {
properties.put(JsonGenerator.PRETTY_PRINTING, myPrettyPrint);
}
JsonGeneratorFactory jgf = Json.createGeneratorFactory(properties);
JsonGenerator eventWriter = jgf.createGenerator(theWriter);
return eventWriter;
}
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName) throws IOException {
switch (theChildDef.getChildType()) { switch (theChildDef.getChildType()) {
case PRIMITIVE_DATATYPE: { case PRIMITIVE_DATATYPE: {
@ -438,7 +316,8 @@ public class JsonParser extends BaseParser implements IParser {
} }
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren) throws IOException { private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter,
List<? extends BaseRuntimeChildDefinition> theChildren) throws IOException {
for (BaseRuntimeChildDefinition nextChild : theChildren) { for (BaseRuntimeChildDefinition nextChild : theChildren) {
if (nextChild instanceof RuntimeChildNarrativeDefinition) { if (nextChild instanceof RuntimeChildNarrativeDefinition) {
INarrativeGenerator gen = myContext.getNarrativeGenerator(); INarrativeGenerator gen = myContext.getNarrativeGenerator();
@ -570,7 +449,8 @@ public class JsonParser extends BaseParser implements IParser {
} }
} }
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef) throws IOException, DataFormatException { private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter,
BaseRuntimeElementCompositeDefinition<?> resDef) throws IOException, DataFormatException {
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions()); encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions());
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren()); encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren());
} }
@ -598,12 +478,52 @@ public class JsonParser extends BaseParser implements IParser {
theEventWriter.writeEnd(); theEventWriter.writeEnd();
} }
@Override
public void encodeResourceToWriter(IResource theResource, Writer theWriter) throws IOException {
Validate.notNull(theResource, "Resource can not be null");
JsonGenerator eventWriter = createJsonGenerator(theWriter);
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null);
eventWriter.flush();
}
@Override
public void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException {
JsonGenerator eventWriter = createJsonGenerator(theWriter);
eventWriter.writeStartObject();
eventWriter.write("resourceType", TagList.ELEMENT_NAME);
eventWriter.writeStartArray(TagList.ATTR_CATEGORY);
for (Tag next : theTagList) {
eventWriter.writeStartObject();
if (isNotBlank(next.getTerm())) {
eventWriter.write(Tag.ATTR_TERM, next.getTerm());
}
if (isNotBlank(next.getLabel())) {
eventWriter.write(Tag.ATTR_LABEL, next.getLabel());
}
if (isNotBlank(next.getScheme())) {
eventWriter.write(Tag.ATTR_SCHEME, next.getScheme());
}
eventWriter.writeEnd();
}
eventWriter.writeEnd();
eventWriter.writeEnd();
eventWriter.flush();
}
/** /**
* This is useful only for the two cases where extensions are encoded as * This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object called _name): resource extensions, and extension extensions
* direct children (e.g. not in some object called _name): resource
* extensions, and extension extensions
*/ */
private void extractAndWriteExtensionsAsDirectChild(IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IResource theResource) throws IOException { private void extractAndWriteExtensionsAsDirectChild(IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef,
IResource theResource) throws IOException {
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0); List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0); List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
@ -678,6 +598,30 @@ public class JsonParser extends BaseParser implements IParser {
} }
} }
@Override
public <T extends IResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader) {
JsonReader reader = Json.createReader(theReader);
JsonObject object = reader.readObject();
JsonValue resourceTypeObj = object.get("resourceType");
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
String resourceType = ((JsonString) resourceTypeObj).getString();
if (!"Bundle".equals(resourceType)) {
throw new DataFormatException("Trying to parse bundle but found resourceType other than 'Bundle'. Found: '" + resourceType + "'");
}
ParserState<Bundle> state = ParserState.getPreAtomInstance(myContext, theResourceType, true);
state.enteringNewElement(null, "feed");
parseBundleChildren(object, state);
state.endingElement();
Bundle retVal = state.getObject();
return retVal;
}
private void parseBundleChildren(JsonObject theObject, ParserState<?> theState) { private void parseBundleChildren(JsonObject theObject, ParserState<?> theState) {
for (String nextName : theObject.keySet()) { for (String nextName : theObject.keySet()) {
if ("resourceType".equals(nextName)) { if ("resourceType".equals(nextName)) {
@ -830,6 +774,65 @@ public class JsonParser extends BaseParser implements IParser {
} }
} }
@Override
public <T extends IResource> T parseResource(Class<T> theResourceType, Reader theReader) {
JsonReader reader = Json.createReader(theReader);
JsonObject object = reader.readObject();
JsonValue resourceTypeObj = object.get("resourceType");
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
String resourceType = ((JsonString) resourceTypeObj).getString();
RuntimeResourceDefinition def;
if (theResourceType != null) {
def = myContext.getResourceDefinition(theResourceType);
} else {
def = myContext.getResourceDefinition(resourceType);
}
ParserState<? extends IResource> state = ParserState.getPreResourceInstance(def.getImplementingClass(), myContext, true);
state.enteringNewElement(null, def.getName());
parseChildren(object, state);
state.endingElement();
@SuppressWarnings("unchecked")
T retVal = (T) state.getObject();
return retVal;
}
@Override
public <T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString) {
return parseResource(theResourceType, new StringReader(theMessageString));
}
@Override
public TagList parseTagList(Reader theReader) {
JsonReader reader = Json.createReader(theReader);
JsonObject object = reader.readObject();
JsonValue resourceTypeObj = object.get("resourceType");
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
String resourceType = ((JsonString) resourceTypeObj).getString();
ParserState<TagList> state = ParserState.getPreTagListInstance(myContext, true);
state.enteringNewElement(null, resourceType);
parseChildren(object, state);
state.endingElement();
return state.getObject();
}
@Override
public IParser setPrettyPrint(boolean thePrettyPrint) {
myPrettyPrint = thePrettyPrint;
return this;
}
private void writeAtomLink(JsonGenerator theEventWriter, String theRel, StringDt theLink) { private void writeAtomLink(JsonGenerator theEventWriter, String theRel, StringDt theLink) {
if (isNotBlank(theLink.getValue())) { if (isNotBlank(theLink.getValue())) {
theEventWriter.writeStartObject(); theEventWriter.writeStartObject();
@ -850,7 +853,8 @@ public class JsonParser extends BaseParser implements IParser {
} }
} }
private void writeExtensionsAsDirectChild(IResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) throws IOException { private void writeExtensionsAsDirectChild(IResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions,
List<HeldExtension> modifierExtensions) throws IOException {
if (extensions.isEmpty() == false) { if (extensions.isEmpty() == false) {
theEventWriter.writeStartArray("extension"); theEventWriter.writeStartArray("extension");
for (HeldExtension next : extensions) { for (HeldExtension next : extensions) {
@ -933,12 +937,13 @@ public class JsonParser extends BaseParser implements IParser {
} }
private void writeUndeclaredExt(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, ExtensionDt ext) throws IOException { private void writeUndeclaredExt(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theEventWriter, ExtensionDt ext) throws IOException {
IElement value = ext.getValue();
theEventWriter.writeStartObject(); theEventWriter.writeStartObject();
theEventWriter.write("url", ext.getUrl().getValue()); theEventWriter.write("url", ext.getUrl().getValue());
IElement value = ext.getValue();
if (value == null && ext.getAllUndeclaredExtensions().isEmpty()) { if (value == null && ext.getAllUndeclaredExtensions().isEmpty()) {
theEventWriter.writeNull(); ourLog.debug("Extension with URL[{}] has no value", ext.getUrl().getValue());
} else if (value == null) { } else if (value == null) {
theEventWriter.writeStartArray("extension"); theEventWriter.writeStartArray("extension");
for (ExtensionDt next : ext.getUndeclaredExtensions()) { for (ExtensionDt next : ext.getUndeclaredExtensions()) {
@ -961,5 +966,4 @@ public class JsonParser extends BaseParser implements IParser {
} }
} }
} }

View File

@ -60,19 +60,15 @@ public abstract class BaseClient {
private boolean myPrettyPrint = false; private boolean myPrettyPrint = false;
/** /**
* Returns the encoding that will be used on requests. Default is * Returns the encoding that will be used on requests. Default is <code>null</code>, which means the client will not explicitly request an encoding. (This is standard behaviour according to the
* <code>null</code>, which means the client will not explicitly request an * FHIR specification)
* encoding. (This is standard behaviour according to the FHIR
* specification)
*/ */
public EncodingEnum getEncoding() { public EncodingEnum getEncoding() {
return myEncoding; return myEncoding;
} }
/** /**
* Sets the encoding that will be used on requests. Default is * Sets the encoding that will be used on requests. Default is <code>null</code>, which means the client will not explicitly request an encoding. (This is standard behaviour according to the FHIR
* <code>null</code>, which means the client will not explicitly request an
* encoding. (This is standard behaviour according to the FHIR
* specification) * specification)
*/ */
public BaseClient setEncoding(EncodingEnum theEncoding) { public BaseClient setEncoding(EncodingEnum theEncoding) {
@ -87,16 +83,14 @@ public abstract class BaseClient {
} }
/** /**
* For now, this is a part of the internal API of HAPI - Use with caution as * For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
* this method may change!
*/ */
public HttpResponse getLastResponse() { public HttpResponse getLastResponse() {
return myLastResponse; return myLastResponse;
} }
/** /**
* For now, this is a part of the internal API of HAPI - Use with caution as * For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
* this method may change!
*/ */
public String getLastResponseBody() { public String getLastResponseBody() {
return myLastResponseBody; return myLastResponseBody;
@ -106,11 +100,11 @@ public abstract class BaseClient {
return myUrlBase; return myUrlBase;
} }
Object invokeClient(IClientResponseHandler binding, BaseClientInvocation clientInvocation) { <T> T invokeClient(IClientResponseHandler<T> binding, BaseClientInvocation clientInvocation) {
return invokeClient(binding, clientInvocation, false); return invokeClient(binding, clientInvocation, false);
} }
Object invokeClient(IClientResponseHandler binding, BaseClientInvocation clientInvocation, boolean theLogRequestAndResponse) { <T> T invokeClient(IClientResponseHandler<T> binding, BaseClientInvocation clientInvocation, boolean theLogRequestAndResponse) {
// TODO: handle non 2xx status codes by throwing the correct exception, // TODO: handle non 2xx status codes by throwing the correct exception,
// and ensure it's passed upwards // and ensure it's passed upwards
HttpRequestBase httpRequest; HttpRequestBase httpRequest;
@ -160,13 +154,24 @@ public abstract class BaseClient {
} }
if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() > 299) { if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() > 299) {
throw BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase()); BaseServerResponseException exception = BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase());
try {
String body = IOUtils.toString(reader);
exception.setResponseBody(body);
} catch (Exception e) {
ourLog.debug("Failed to read input stream", e);
} finally {
IOUtils.closeQuietly(reader);
}
throw exception;
} }
try { try {
return binding.invokeClient(mimeType, reader, response.getStatusLine().getStatusCode(), headers); return binding.invokeClient(mimeType, reader, response.getStatusLine().getStatusCode(), headers);
} finally { } finally {
reader.close(); IOUtils.closeQuietly(reader);
} }
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
@ -201,32 +206,28 @@ public abstract class BaseClient {
} }
/** /**
* For now, this is a part of the internal API of HAPI - Use with caution as * For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
* this method may change!
*/ */
public boolean isKeepResponses() { public boolean isKeepResponses() {
return myKeepResponses; return myKeepResponses;
} }
/** /**
* For now, this is a part of the internal API of HAPI - Use with caution as * For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
* this method may change!
*/ */
public void setKeepResponses(boolean theKeepResponses) { public void setKeepResponses(boolean theKeepResponses) {
myKeepResponses = theKeepResponses; myKeepResponses = theKeepResponses;
} }
/** /**
* For now, this is a part of the internal API of HAPI - Use with caution as * For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
* this method may change!
*/ */
public void setLastResponse(HttpResponse theLastResponse) { public void setLastResponse(HttpResponse theLastResponse) {
myLastResponse = theLastResponse; myLastResponse = theLastResponse;
} }
/** /**
* For now, this is a part of the internal API of HAPI - Use with caution as * For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
* this method may change!
*/ */
public void setLastResponseBody(String theLastResponseBody) { public void setLastResponseBody(String theLastResponseBody) {
myLastResponseBody = theLastResponseBody; myLastResponseBody = theLastResponseBody;
@ -252,20 +253,16 @@ public abstract class BaseClient {
} }
/** /**
* Returns the pretty print flag, which is a request to the server for it to * Returns the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
* return "pretty printed" responses. Note that this is currently a * HAPI based servers (and any other servers which might implement it).
* non-standard flag (_pretty) which is supported only by HAPI based servers
* (and any other servers which might implement it).
*/ */
public boolean isPrettyPrint() { public boolean isPrettyPrint() {
return myPrettyPrint; return myPrettyPrint;
} }
/** /**
* Sets the pretty print flag, which is a request to the server for it to * Sets the pretty print flag, which is a request to the server for it to return "pretty printed" responses. Note that this is currently a non-standard flag (_pretty) which is supported only by
* return "pretty printed" responses. Note that this is currently a * HAPI based servers (and any other servers which might implement it).
* non-standard flag (_pretty) which is supported only by HAPI based servers
* (and any other servers which might implement it).
*/ */
public BaseClient setPrettyPrint(boolean thePrettyPrint) { public BaseClient setPrettyPrint(boolean thePrettyPrint) {
myPrettyPrint = thePrettyPrint; myPrettyPrint = thePrettyPrint;

View File

@ -35,7 +35,7 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
public class ClientInvocationHandler extends BaseClient implements InvocationHandler { public class ClientInvocationHandler extends BaseClient implements InvocationHandler {
private final Map<Method, BaseMethodBinding> myBindings = new HashMap<Method, BaseMethodBinding>(); private final Map<Method, BaseMethodBinding<?>> myBindings = new HashMap<Method, BaseMethodBinding<?>>();
private final Map<Method, ILambda> myMethodToLambda = new HashMap<Method, ILambda>(); private final Map<Method, ILambda> myMethodToLambda = new HashMap<Method, ILambda>();
@ -59,7 +59,7 @@ public class ClientInvocationHandler extends BaseClient implements InvocationHan
} }
} }
public void addBinding(Method theMethod, BaseMethodBinding theBinding) { public void addBinding(Method theMethod, BaseMethodBinding<?> theBinding) {
myBindings.put(theMethod, theBinding); myBindings.put(theMethod, theBinding);
} }
@ -70,7 +70,7 @@ public class ClientInvocationHandler extends BaseClient implements InvocationHan
return directRetVal; return directRetVal;
} }
BaseMethodBinding binding = myBindings.get(theMethod); BaseMethodBinding<?> binding = myBindings.get(theMethod);
if (binding != null) { if (binding != null) {
BaseClientInvocation clientInvocation = binding.invokeClient(theArgs); BaseClientInvocation clientInvocation = binding.invokeClient(theArgs);
return invokeClient(binding, clientInvocation); return invokeClient(binding, clientInvocation);

View File

@ -40,6 +40,7 @@ import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.gclient.ICriterion; import ca.uhn.fhir.rest.gclient.ICriterion;
import ca.uhn.fhir.rest.gclient.ICriterionInternal; import ca.uhn.fhir.rest.gclient.ICriterionInternal;
import ca.uhn.fhir.rest.gclient.IQuery; import ca.uhn.fhir.rest.gclient.IQuery;
@ -68,8 +69,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
private HttpRequestBase myLastRequest; private HttpRequestBase myLastRequest;
/** /**
* For now, this is a part of the internal API of HAPI - Use with caution as * For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
* this method may change!
*/ */
public GenericClient(FhirContext theContext, HttpClient theHttpClient, String theServerBase) { public GenericClient(FhirContext theContext, HttpClient theHttpClient, String theServerBase) {
super(theHttpClient, theServerBase); super(theHttpClient, theServerBase);
@ -83,16 +83,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding()); myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
} }
IClientResponseHandler binding = new IClientResponseHandler() { ResourceResponseHandler<Conformance> binding = new ResourceResponseHandler<Conformance>(Conformance.class);
@Override Conformance resp = invokeClient(binding, invocation);
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
IParser parser = respType.newParser(myContext);
return parser.parseResource(Conformance.class, theResponseReader);
}
};
Conformance resp = (Conformance) invokeClient(binding, invocation);
return resp; return resp;
} }
@ -106,15 +98,9 @@ public class GenericClient extends BaseClient implements IGenericClient {
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
final String resourceName = def.getName(); final String resourceName = def.getName();
IClientResponseHandler binding = new IClientResponseHandler() { OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
@Override
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
MethodOutcome response = BaseOutcomeReturningMethodBinding.process2xxResponse(myContext, resourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
return response;
}
};
MethodOutcome resp = (MethodOutcome) invokeClient(binding, invocation); MethodOutcome resp = invokeClient(binding, invocation);
return resp; return resp;
} }
@ -127,15 +113,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
} }
final String resourceName = myContext.getResourceDefinition(theType).getName(); final String resourceName = myContext.getResourceDefinition(theType).getName();
IClientResponseHandler binding = new IClientResponseHandler() { OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
@Override MethodOutcome resp = invokeClient(binding, invocation);
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
MethodOutcome response = BaseOutcomeReturningMethodBinding.process2xxResponse(myContext, resourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
return response;
}
};
MethodOutcome resp = (MethodOutcome) invokeClient(binding, invocation);
return resp; return resp;
} }
@ -155,16 +134,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding()); myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
} }
IClientResponseHandler binding = new IClientResponseHandler() { BundleResponseHandler binding = new BundleResponseHandler(theType);
@Override Bundle resp = invokeClient(binding, invocation);
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
IParser parser = respType.newParser(myContext);
return parser.parseBundle(theType, theResponseReader);
}
};
Bundle resp = (Bundle) invokeClient(binding, invocation);
return resp; return resp;
} }
@ -181,17 +152,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding()); myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
} }
IClientResponseHandler binding = new IClientResponseHandler() { ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType);
@Override T resp = invokeClient(binding, invocation);
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
IParser parser = respType.newParser(myContext);
return parser.parseResource(theType, theResponseReader);
}
};
@SuppressWarnings("unchecked")
T resp = (T) invokeClient(binding, invocation);
return resp; return resp;
} }
@ -221,22 +183,13 @@ public class GenericClient extends BaseClient implements IGenericClient {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding()); myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
} }
IClientResponseHandler binding = new IClientResponseHandler() { BundleResponseHandler binding = new BundleResponseHandler(theType);
@Override Bundle resp = invokeClient(binding, invocation);
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
IParser parser = respType.newParser(myContext);
return parser.parseBundle(theType, theResponseReader);
}
};
Bundle resp = (Bundle) invokeClient(binding, invocation);
return resp; return resp;
} }
/** /**
* For now, this is a part of the internal API of HAPI - Use with caution as * For now, this is a part of the internal API of HAPI - Use with caution as this method may change!
* this method may change!
*/ */
public void setLastRequest(HttpRequestBase theLastRequest) { public void setLastRequest(HttpRequestBase theLastRequest) {
myLastRequest = theLastRequest; myLastRequest = theLastRequest;
@ -252,15 +205,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
final String resourceName = def.getName(); final String resourceName = def.getName();
IClientResponseHandler binding = new IClientResponseHandler() { OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
@Override MethodOutcome resp = invokeClient(binding, invocation);
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
MethodOutcome response = BaseOutcomeReturningMethodBinding.process2xxResponse(myContext, resourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
return response;
}
};
MethodOutcome resp = (MethodOutcome) invokeClient(binding, invocation);
return resp; return resp;
} }
@ -279,15 +225,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
final String resourceName = def.getName(); final String resourceName = def.getName();
IClientResponseHandler binding = new IClientResponseHandler() { OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
@Override MethodOutcome resp = invokeClient(binding, invocation);
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
MethodOutcome response = BaseOutcomeReturningMethodBinding.process2xxResponse(myContext, resourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
return response;
}
};
MethodOutcome resp = (MethodOutcome) invokeClient(binding, invocation);
return resp; return resp;
} }
@ -298,17 +237,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding()); myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
} }
IClientResponseHandler binding = new IClientResponseHandler() { ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theType);
@Override T resp = invokeClient(binding, invocation);
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
IParser parser = respType.newParser(myContext);
return parser.parseResource(theType, theResponseReader);
}
};
@SuppressWarnings("unchecked")
T resp = (T) invokeClient(binding, invocation);
return resp; return resp;
} }
@ -321,6 +251,57 @@ public class GenericClient extends BaseClient implements IGenericClient {
return myContext.getResourceDefinition(theType).getName(); return myContext.getResourceDefinition(theType).getName();
} }
private final class OutcomeResponseHandler implements IClientResponseHandler<MethodOutcome> {
private final String myResourceName;
private OutcomeResponseHandler(String theResourceName) {
myResourceName = theResourceName;
}
@Override
public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException,
BaseServerResponseException {
MethodOutcome response = BaseOutcomeReturningMethodBinding.process2xxResponse(myContext, myResourceName, theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
return response;
}
}
private final class ResourceResponseHandler<T extends IResource> implements IClientResponseHandler<T> {
private Class<T> myType;
public ResourceResponseHandler(Class<T> theType) {
myType = theType;
}
@Override
public T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
IParser parser = respType.newParser(myContext);
return parser.parseResource(myType, theResponseReader);
}
}
private final class BundleResponseHandler implements IClientResponseHandler<Bundle> {
private Class<? extends IResource> myType;
public BundleResponseHandler(Class<? extends IResource> theType) {
myType = theType;
}
@Override
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 new NonFhirResponseException(theResponseStatusCode, "");
}
IParser parser = respType.newParser(myContext);
return parser.parseBundle(myType, theResponseReader);
}
}
private class ForInternal implements IQuery { private class ForInternal implements IQuery {
private List<ICriterionInternal> myCriterion = new ArrayList<ICriterionInternal>(); private List<ICriterionInternal> myCriterion = new ArrayList<ICriterionInternal>();
@ -403,16 +384,9 @@ public class GenericClient extends BaseClient implements IGenericClient {
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding()); myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
} }
IClientResponseHandler binding = new IClientResponseHandler() { BundleResponseHandler binding = new BundleResponseHandler(myResourceType);
@Override
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
EncodingEnum respType = EncodingEnum.forContentType(theResponseMimeType);
IParser parser = respType.newParser(myContext);
return parser.parseBundle(myResourceType, theResponseReader);
}
};
Bundle resp = (Bundle) invokeClient(binding, invocation, myLogRequestAndResponse); Bundle resp = invokeClient(binding, invocation, myLogRequestAndResponse);
return resp; return resp;
} }

View File

@ -36,7 +36,8 @@ import org.apache.http.protocol.HttpContext;
* HTTP interceptor to be used for adding HTTP basic auth username/password tokens * HTTP interceptor to be used for adding HTTP basic auth username/password tokens
* to requests * to requests
* <p> * <p>
* See the * See the <a href="http://hl7api.sourceforge.net/hapi-fhir/doc_rest_client.html#HTTP_Basic_Authorization">HAPI Documentation</a>
* for information on how to use this class.
* </p> * </p>
*/ */
public class HttpBasicAuthInterceptor implements HttpRequestInterceptor { public class HttpBasicAuthInterceptor implements HttpRequestInterceptor {

View File

@ -52,7 +52,7 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding { public abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding<Void> {
private Class<? extends IResource> myType; private Class<? extends IResource> myType;
private Integer myIdParamIndex; private Integer myIdParamIndex;
@ -89,7 +89,7 @@ public abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding
} }
@Override @Override
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException { public Void invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
switch (theResponseStatusCode) { switch (theResponseStatusCode) {
case Constants.STATUS_HTTP_200_OK: case Constants.STATUS_HTTP_200_OK:
case Constants.STATUS_HTTP_201_CREATED: case Constants.STATUS_HTTP_201_CREATED:

View File

@ -78,7 +78,7 @@ import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.ReflectionUtil; import ca.uhn.fhir.util.ReflectionUtil;
public abstract class BaseMethodBinding implements IClientResponseHandler { public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T> {
private FhirContext myContext; private FhirContext myContext;
private Method myMethod; private Method myMethod;
@ -160,7 +160,7 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static BaseMethodBinding bindMethod(Method theMethod, FhirContext theContext, Object theProvider) { public static BaseMethodBinding<?> bindMethod(Method theMethod, FhirContext theContext, Object theProvider) {
Read read = theMethod.getAnnotation(Read.class); Read read = theMethod.getAnnotation(Read.class);
Search search = theMethod.getAnnotation(Search.class); Search search = theMethod.getAnnotation(Search.class);
Metadata conformance = theMethod.getAnnotation(Metadata.class); Metadata conformance = theMethod.getAnnotation(Metadata.class);

View File

@ -55,7 +55,7 @@ import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding { public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<MethodOutcome> {
private static final String LABEL = "label=\""; private static final String LABEL = "label=\"";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class);
private static final String SCHEME = "scheme=\""; private static final String SCHEME = "scheme=\"";
@ -94,7 +94,7 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
} }
@Override @Override
public Object 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 {
switch (theResponseStatusCode) { switch (theResponseStatusCode) {
case Constants.STATUS_HTTP_200_OK: case Constants.STATUS_HTTP_200_OK:
case Constants.STATUS_HTTP_201_CREATED: case Constants.STATUS_HTTP_201_CREATED:

View File

@ -65,7 +65,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding { abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Object> {
protected static final Set<String> ALLOWED_PARAMS; protected static final Set<String> ALLOWED_PARAMS;
static { static {

View File

@ -51,7 +51,7 @@ import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public class GetTagsMethodBinding extends BaseMethodBinding { public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
private Class<? extends IResource> myType; private Class<? extends IResource> myType;
private Integer myIdParamIndex; private Integer myIdParamIndex;
@ -80,7 +80,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding {
} }
@Override @Override
public Object 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 {
if (theResponseStatusCode == Constants.STATUS_HTTP_200_OK) { if (theResponseStatusCode == Constants.STATUS_HTTP_200_OK) {
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theResponseStatusCode); IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theResponseStatusCode);
TagList retVal = parser.parseTagList(theResponseReader); TagList retVal = parser.parseTagList(theResponseReader);

View File

@ -27,8 +27,8 @@ import java.util.Map;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
public interface IClientResponseHandler { public interface IClientResponseHandler<T> {
Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException; T invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException;
} }

View File

@ -28,6 +28,7 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import javax.servlet.ServletConfig; import javax.servlet.ServletConfig;
import javax.servlet.ServletException; import javax.servlet.ServletException;
@ -39,6 +40,7 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.io.output.WriterOutputStream; import org.apache.commons.io.output.WriterOutputStream;
import org.apache.commons.lang3.StringEscapeUtils; import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
@ -66,10 +68,10 @@ import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
public class PublicTesterServlet extends HttpServlet { public class RestfulServerTesterServlet extends HttpServlet {
private static final boolean DEBUGMODE = true; private static final boolean DEBUGMODE = true;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PublicTesterServlet.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServerTesterServlet.class);
private static final String PUBLIC_TESTER_RESULT_HTML = "/PublicTesterResult.html"; private static final String PUBLIC_TESTER_RESULT_HTML = "/PublicTesterResult.html";
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private FhirContext myCtx; private FhirContext myCtx;
@ -77,8 +79,9 @@ public class PublicTesterServlet extends HttpServlet {
private HashMap<String, String> myStaticResources; private HashMap<String, String> myStaticResources;
private TemplateEngine myTemplateEngine; private TemplateEngine myTemplateEngine;
private Set<String> myFilterHeaders;
public PublicTesterServlet() { public RestfulServerTesterServlet() {
myStaticResources = new HashMap<String, String>(); myStaticResources = new HashMap<String, String>();
myStaticResources.put("jquery-2.1.0.min.js", "text/javascript"); myStaticResources.put("jquery-2.1.0.min.js", "text/javascript");
myStaticResources.put("PublicTester.js", "text/javascript"); myStaticResources.put("PublicTester.js", "text/javascript");
@ -95,6 +98,10 @@ public class PublicTesterServlet extends HttpServlet {
myCtx = new FhirContext(); myCtx = new FhirContext();
} }
public FhirContext getFhirContext() {
return myCtx;
}
@Override @Override
public void init(ServletConfig theConfig) throws ServletException { public void init(ServletConfig theConfig) throws ServletException {
myTemplateEngine = new TemplateEngine(); myTemplateEngine = new TemplateEngine();
@ -120,7 +127,7 @@ public class PublicTesterServlet extends HttpServlet {
} }
private void streamResponse(String theResourceName, String theContentType, HttpServletResponse theResp) throws IOException { private void streamResponse(String theResourceName, String theContentType, HttpServletResponse theResp) throws IOException {
InputStream res = PublicTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/" + theResourceName); InputStream res = RestfulServerTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/" + theResourceName);
theResp.setContentType(theContentType); theResp.setContentType(theContentType);
IOUtils.copy(res, theResp.getOutputStream()); IOUtils.copy(res, theResp.getOutputStream());
} }
@ -401,6 +408,9 @@ public class PublicTesterServlet extends HttpServlet {
} }
} }
Header[] requestHeaders = applyHeaderFilters(lastRequest.getAllHeaders());
Header[] responseHeaders = applyHeaderFilters(lastResponse.getAllHeaders());
WebContext ctx = new WebContext(theReq, theResp, theReq.getServletContext(), theReq.getLocale()); WebContext ctx = new WebContext(theReq, theResp, theReq.getServletContext(), theReq.getLocale());
ctx.setVariable("base", myServerBase); ctx.setVariable("base", myServerBase);
ctx.setVariable("requestUrl", requestUrl); ctx.setVariable("requestUrl", requestUrl);
@ -410,8 +420,8 @@ public class PublicTesterServlet extends HttpServlet {
ctx.setVariable("requestSyntaxHighlighterClass", requestSyntaxHighlighterClass); ctx.setVariable("requestSyntaxHighlighterClass", requestSyntaxHighlighterClass);
ctx.setVariable("resultBody", StringEscapeUtils.escapeHtml4(resultBody)); ctx.setVariable("resultBody", StringEscapeUtils.escapeHtml4(resultBody));
ctx.setVariable("resultSyntaxHighlighterClass", resultSyntaxHighlighterClass); ctx.setVariable("resultSyntaxHighlighterClass", resultSyntaxHighlighterClass);
ctx.setVariable("requestHeaders", lastRequest.getAllHeaders()); ctx.setVariable("requestHeaders", requestHeaders);
ctx.setVariable("responseHeaders", lastResponse.getAllHeaders()); ctx.setVariable("responseHeaders", responseHeaders);
ctx.setVariable("narrative", narrativeString); ctx.setVariable("narrative", narrativeString);
myTemplateEngine.process(PUBLIC_TESTER_RESULT_HTML, ctx, theResp.getWriter()); myTemplateEngine.process(PUBLIC_TESTER_RESULT_HTML, ctx, theResp.getWriter());
@ -421,6 +431,32 @@ public class PublicTesterServlet extends HttpServlet {
} }
} }
private Header[] applyHeaderFilters(Header[] theAllHeaders) {
if (myFilterHeaders == null || myFilterHeaders.isEmpty()) {
return theAllHeaders;
}
ArrayList<Header> retVal = new ArrayList<Header>();
for (Header next : theAllHeaders) {
if (!myFilterHeaders.contains(next.getName().toLowerCase())) {
retVal.add(next);
}
}
return retVal.toArray(new Header[retVal.size()]);
}
/**
* If set, the headers named here will be stripped from requests/responses before they are displayed to the user. This can be used, for instance, to filter out "Authorization" headers. Note that
* names are not case sensitive.
*/
public void setFilterHeaders(String... theHeaderNames) {
myFilterHeaders = new HashSet<String>();
if (theHeaderNames != null) {
for (String next : theHeaderNames) {
myFilterHeaders.add(next.toLowerCase());
}
}
}
private String parseNarrative(EncodingEnum theCtEnum, String theResultBody) { private String parseNarrative(EncodingEnum theCtEnum, String theResultBody) {
try { try {
IResource resource = theCtEnum.newParser(myCtx).parseResource(theResultBody); IResource resource = theCtEnum.newParser(myCtx).parseResource(theResultBody);
@ -448,10 +484,10 @@ public class PublicTesterServlet extends HttpServlet {
public InputStream getResourceAsStream(TemplateProcessingParameters theTemplateProcessingParameters, String theName) { public InputStream getResourceAsStream(TemplateProcessingParameters theTemplateProcessingParameters, String theName) {
ourLog.debug("Loading template: {}", theName); ourLog.debug("Loading template: {}", theName);
if ("/".equals(theName)) { if ("/".equals(theName)) {
return PublicTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/PublicTester.html"); return RestfulServerTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/PublicTester.html");
} }
if (PUBLIC_TESTER_RESULT_HTML.equals(theName)) { if (PUBLIC_TESTER_RESULT_HTML.equals(theName)) {
return PublicTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/PublicTesterResult.html"); return RestfulServerTesterServlet.class.getResourceAsStream("/ca/uhn/fhir/rest/server/tester/PublicTesterResult.html");
} }
return null; return null;

View File

@ -51,6 +51,15 @@ import ca.uhn.fhir.narrative.INarrativeGenerator;
public class JsonParserTest { public class JsonParserTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserTest.class);
@Test
public void testEncodingNullExtension() {
Patient p = new Patient();
p.addUndeclaredExtension(new ExtensionDt(false, "http://foo#bar"));
String str = new FhirContext().newJsonParser().encodeResourceToString(p);
assertEquals("{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://foo#bar\"}]}", str);
}
@Test @Test
public void testTagList() { public void testTagList() {

View File

@ -61,6 +61,7 @@ import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QualifiedDateParam; import ca.uhn.fhir.rest.param.QualifiedDateParam;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
@ -585,6 +586,30 @@ public class ClientTest {
} }
@Test
public void testReadFailureInternalError() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 500, "INTERNAL"));
Header[] headers = new Header[1];
headers[0] = new BasicHeader(Constants.HEADER_LAST_MODIFIED, "2011-01-02T22:01:02");
when(httpResponse.getAllHeaders()).thenReturn(headers);
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_TEXT));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("Internal Failure"), Charset.forName("UTF-8")));
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
try {
client.getPatientById(new IdDt("111"));
fail();
} catch (InternalErrorException e) {
assertThat(e.getMessage(), containsString("INTERNAL"));
assertThat(e.getResponseBody(), containsString("Internal Failure"));
}
}
@Test @Test
public void testReadNoCharset() throws Exception { public void testReadNoCharset() throws Exception {

View File

@ -1,7 +1,10 @@
package ca.uhn.fhir.rest.client; package ca.uhn.fhir.rest.client;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*; 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.StringReader; import java.io.StringReader;
import java.nio.charset.Charset; import java.nio.charset.Charset;
@ -13,6 +16,7 @@ import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine; import org.apache.http.message.BasicStatusLine;
import org.hamcrest.core.StringContains;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
@ -24,7 +28,9 @@ import ca.uhn.fhir.model.dstu.resource.Encounter;
import ca.uhn.fhir.model.dstu.resource.Observation; import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Organization; import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public class GenericClientTest { public class GenericClientTest {
@ -278,4 +284,52 @@ public class GenericClientTest {
} }
@SuppressWarnings("unused")
@Test
public void testSearchWithInternalServerError() 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), 500, "OK"));
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_TEXT + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("Server Issues!"), Charset.forName("UTF-8")));
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
try {
client.search().forResource(Patient.class).execute();
fail();
} catch (InternalErrorException e) {
assertThat(e.getMessage(), StringContains.containsString("AAA"));
}
}
@SuppressWarnings("unused")
@Test
public void testSearchWithNonFhirResponse() 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_TEXT + "; charset=UTF-8"));
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("Server Issues!"), Charset.forName("UTF-8")));
IGenericClient client = myCtx.newRestfulGenericClient("http://example.com/fhir");
try {
client.search().forResource(Patient.class).execute();
fail();
} catch (NonFhirResponseException e) {
assertThat(e.getMessage(), StringContains.containsString("AAA"));
}
}
} }

View File

@ -40,7 +40,7 @@ import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.annotation.Validate; import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.tester.PublicTesterServlet; import ca.uhn.fhir.rest.server.tester.RestfulServerTesterServlet;
import ca.uhn.fhir.testutil.RandomServerPortProvider; import ca.uhn.fhir.testutil.RandomServerPortProvider;
public class TesterTest { public class TesterTest {
@ -62,7 +62,7 @@ public class TesterTest {
ServletContextHandler proxyHandler = new ServletContextHandler(); ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/"); proxyHandler.setContextPath("/");
PublicTesterServlet testerServlet = new PublicTesterServlet(); RestfulServerTesterServlet testerServlet = new RestfulServerTesterServlet();
testerServlet.setServerBase("http://localhost:" + myPort + "/fhir/context"); testerServlet.setServerBase("http://localhost:" + myPort + "/fhir/context");
// testerServlet.setServerBase("http://fhir.healthintersections.com.au/open"); // testerServlet.setServerBase("http://fhir.healthintersections.com.au/open");
ServletHolder handler = new ServletHolder(); ServletHolder handler = new ServletHolder();