working on binary resources

This commit is contained in:
jamesagnew 2014-05-28 07:30:40 -04:00
parent 0ad18c0b77
commit adde115c53
12 changed files with 568 additions and 98 deletions

View File

@ -20,38 +20,64 @@ package ca.uhn.fhir.model.dstu.resource;
* #L% * #L%
*/ */
import java.util.Collections;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.model.api.BaseResource; import ca.uhn.fhir.model.api.BaseResource;
import ca.uhn.fhir.model.api.IElement; import ca.uhn.fhir.model.api.IElement;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt; import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.primitive.Base64BinaryDt;
@ResourceDef(name="Binary", profile="http://hl7.org/fhir/profiles/Binary", id="binary") @ResourceDef(name = "Binary", profile = "http://hl7.org/fhir/profiles/Binary", id = "binary")
public class Binary extends BaseResource implements IResource { public class Binary extends BaseResource implements IResource {
// TODO: implement binary private Base64BinaryDt myContent = new Base64BinaryDt();
private String myContentType;
@Override
public boolean isEmpty() {
return true;
}
@Override @Override
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) { public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
return null; return Collections.emptyList();
} }
@Override public byte[] getContent() {
public ContainedDt getContained() { return myContent.getValue();
return null;
} }
public String getContentAsBase64() {
return myContent.getValueAsString();
}
public String getContentType() {
return myContentType;
}
/**
* Do not call - throws {@link UnsupportedOperationException}
*/
@Override @Override
public NarrativeDt getText() { public NarrativeDt getText() {
throw new IllegalStateException(); throw new UnsupportedOperationException();
}
@Override
public boolean isEmpty() {
return (myContent.isEmpty()) && StringUtils.isBlank(myContentType);
}
public void setContent(byte[] theContent) {
myContent.setValue(theContent);
}
public void setContentAsBase64(String theContent) {
myContent.setValueAsString(theContent);
}
public void setContentType(String theContentType) {
myContentType = theContentType;
} }
} }

View File

@ -76,6 +76,7 @@ import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.dstu.composite.ContainedDt; import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt; import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.BooleanDt; import ca.uhn.fhir.model.primitive.BooleanDt;
import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
@ -477,10 +478,14 @@ public class JsonParser extends BaseParser implements IParser {
theEventWriter.write("id", theResource.getId().getValue()); theEventWriter.write("id", theResource.getId().getValue());
} }
extractAndWriteExtensionsAsDirectChild(theResource, theEventWriter, resDef, theResDef, theResource); if (theResource instanceof Binary) {
Binary bin = (Binary) theResource;
encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, resDef); theEventWriter.write("contentType", bin.getContentType());
theEventWriter.write("content",bin.getContentAsBase64());
} else {
extractAndWriteExtensionsAsDirectChild(theResource, theEventWriter, resDef, theResDef, theResource);
encodeCompositeElementToStreamWriter(theResDef, theResource, theResource, theEventWriter, resDef);
}
theEventWriter.writeEnd(); theEventWriter.writeEnd();
} }

View File

@ -61,6 +61,7 @@ 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.dstu.composite.ContainedDt; import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.XhtmlDt; import ca.uhn.fhir.model.primitive.XhtmlDt;
@ -267,14 +268,14 @@ class ParserState<T> {
} else { } else {
id = entry.getId(); id = entry.getId();
} }
IResource resource = entry.getResource(); IResource resource = entry.getResource();
if (resource == null && id != null && isNotBlank(id.getResourceType())) { if (resource == null && id != null && isNotBlank(id.getResourceType())) {
resource = myContext.getResourceDefinition(id.getResourceType()).newInstance(); resource = myContext.getResourceDefinition(id.getResourceType()).newInstance();
resource.setId(id); resource.setId(id);
entry.setResource(resource); entry.setResource(resource);
} }
if (resource != null) { if (resource != null) {
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.DELETED_AT, entry.getDeletedAt()); resource.getResourceMetadata().put(ResourceMetadataKeyEnum.DELETED_AT, entry.getDeletedAt());
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, id); resource.getResourceMetadata().put(ResourceMetadataKeyEnum.VERSION_ID, id);
@ -346,10 +347,10 @@ class ParserState<T> {
} }
IdDt id = myEntry.getId(); IdDt id = myEntry.getId();
if(id!=null && id.isEmpty()==false) { if (id != null && id.isEmpty() == false) {
myEntry.getResource().setId(id); myEntry.getResource().setId(id);
} }
Map<ResourceMetadataKeyEnum, Object> metadata = myEntry.getResource().getResourceMetadata(); Map<ResourceMetadataKeyEnum, Object> metadata = myEntry.getResource().getResourceMetadata();
if (myEntry.getPublished().isEmpty() == false) { if (myEntry.getPublished().isEmpty() == false) {
metadata.put(ResourceMetadataKeyEnum.PUBLISHED, myEntry.getPublished()); metadata.put(ResourceMetadataKeyEnum.PUBLISHED, myEntry.getPublished());
@ -459,6 +460,77 @@ class ParserState<T> {
} }
private class BinaryResourceState extends BaseState {
private static final int SUBSTATE_CT = 1;
private static final int SUBSTATE_CONTENT = 2;
private Binary myInstance;
private String myData;
private int mySubState = 0;
public BinaryResourceState(PreResourceState thePreResourceState, Binary theInstance) {
super(thePreResourceState);
myInstance = theInstance;
}
@Override
public void attributeValue(String theName, String theValue) throws DataFormatException {
if ("contentType".equals(theName)) {
myInstance.setContentType(theValue);
} else if (myJsonMode && "value".equals(theName)) {
string(theValue);
}
}
@Override
public void endingElement() throws DataFormatException {
if (mySubState == SUBSTATE_CT) {
myInstance.setContentType(myData);
mySubState = 0;
myData=null;
return;
} else if (mySubState == SUBSTATE_CONTENT) {
myInstance.setContentAsBase64(myData);
mySubState = 0;
myData=null;
return;
} else {
if (!myJsonMode) {
myInstance.setContentAsBase64(myData);
}
pop();
}
}
@Override
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
if (myJsonMode && "contentType".equals(theLocalPart) && mySubState == 0) {
mySubState = SUBSTATE_CT;
} else if (myJsonMode && "content".equals(theLocalPart) && mySubState == 0) {
mySubState = SUBSTATE_CONTENT;
} else {
throw new DataFormatException("Unexpected nested element in atom tag: " + theLocalPart);
}
}
@Override
public void string(String theData) {
if (myData == null) {
myData = theData;
} else {
// this shouldn't generally happen so it's ok that it's
// inefficient
myData = myData + theData;
}
}
@Override
protected IElement getCurrentElement() {
return null;
}
}
private class AtomPrimitiveState extends BaseState { private class AtomPrimitiveState extends BaseState {
private String myData; private String myData;
@ -1061,7 +1133,11 @@ class ParserState<T> {
myEntry.setResource(myInstance); myEntry.setResource(myInstance);
} }
push(new ElementCompositeState(this, def, myInstance)); if ("Binary".equals(def.getName())) {
push(new BinaryResourceState(this, (Binary) myInstance));
} else {
push(new ElementCompositeState(this, def, myInstance));
}
} }
public Map<String, IResource> getContainedResources() { public Map<String, IResource> getContainedResources() {

View File

@ -70,6 +70,7 @@ import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.ContainedDt; import ca.uhn.fhir.model.dstu.composite.ContainedDt;
import ca.uhn.fhir.model.dstu.composite.NarrativeDt; import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
@ -551,7 +552,13 @@ public class XmlParser extends BaseParser implements IParser {
theEventWriter.writeAttribute("id", theResource.getId().getValue()); theEventWriter.writeAttribute("id", theResource.getId().getValue());
} }
encodeCompositeElementToStreamWriter(resDef, theResource, theResource, theEventWriter, resDef, theIncludedResource); if (theResource instanceof Binary) {
Binary bin = (Binary) theResource;
theEventWriter.writeAttribute("contentType", bin.getContentType());
theEventWriter.writeCharacters(bin.getContentAsBase64());
} else {
encodeCompositeElementToStreamWriter(resDef, theResource, theResource, theEventWriter, resDef, theIncludedResource);
}
theEventWriter.writeEndElement(); theEventWriter.writeEndElement();
} }

View File

@ -112,11 +112,9 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
@Override @Override
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException { public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest);
IParser parser = encoding.newParser(getContext());
IResource resource; IResource resource;
if (requestContainsResource()) { if (requestContainsResource()) {
resource = parser.parseResource(theRequest.getInputReader()); resource = parseIncomingServerResource(theRequest);
TagList tagList = new TagList(); TagList tagList = new TagList();
for (Enumeration<String> enumeration = theRequest.getServletRequest().getHeaders(Constants.HEADER_CATEGORY); enumeration.hasMoreElements();) { for (Enumeration<String> enumeration = theRequest.getServletRequest().getHeaders(Constants.HEADER_CATEGORY); enumeration.hasMoreElements();) {
String nextTagComplete = enumeration.nextElement(); String nextTagComplete = enumeration.nextElement();
@ -138,10 +136,12 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
response = (MethodOutcome) invokeServerMethod(params); response = (MethodOutcome) invokeServerMethod(params);
} catch (InternalErrorException e) { } catch (InternalErrorException e) {
ourLog.error("Internal error during method invocation", e); ourLog.error("Internal error during method invocation", e);
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest);
streamOperationOutcome(e, theServer, encoding, theResponse, theRequest); streamOperationOutcome(e, theServer, encoding, theResponse, theRequest);
return; return;
} catch (BaseServerResponseException e) { } catch (BaseServerResponseException e) {
ourLog.info("Exception during method invocation: " + e.getMessage()); ourLog.info("Exception during method invocation: " + e.getMessage());
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest);
streamOperationOutcome(e, theServer, encoding, theResponse, theRequest); streamOperationOutcome(e, theServer, encoding, theResponse, theRequest);
return; return;
} }
@ -172,8 +172,10 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
theServer.addHeadersToResponse(theResponse); theServer.addHeadersToResponse(theResponse);
if (response != null && response.getOperationOutcome() != null) { if (response != null && response.getOperationOutcome() != null) {
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest);
theResponse.setContentType(encoding.getResourceContentType()); theResponse.setContentType(encoding.getResourceContentType());
Writer writer = theResponse.getWriter(); Writer writer = theResponse.getWriter();
IParser parser = encoding.newParser(getContext());
parser.setPrettyPrint(RestfulServer.prettyPrintResponse(theRequest)); parser.setPrettyPrint(RestfulServer.prettyPrintResponse(theRequest));
try { try {
parser.encodeResourceToWriter(response.getOperationOutcome(), writer); parser.encodeResourceToWriter(response.getOperationOutcome(), writer);
@ -189,6 +191,14 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
// getMethod().in // getMethod().in
} }
private IResource parseIncomingServerResource(Request theRequest) {
IResource resource;
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest);
IParser parser = encoding.newParser(getContext());
resource = parser.parseResource(theRequest.getInputReader());
return resource;
}
/* /*
* @Override public void invokeServer(RestfulServer theServer, Request * @Override public void invokeServer(RestfulServer theServer, Request
* theRequest, HttpServletResponse theResponse) throws * theRequest, HttpServletResponse theResponse) throws

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.rest.method;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
@ -35,6 +35,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -50,8 +51,7 @@ import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag; 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.ResourceDef; import ca.uhn.fhir.model.api.annotation.ResourceDef;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum; import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
@ -289,6 +289,24 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
private void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException { private void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
theHttpResponse.setStatus(200); theHttpResponse.setStatus(200);
if (theResource instanceof Binary) {
Binary bin = (Binary) theResource;
if (isNotBlank(bin.getContentType())) {
theHttpResponse.setContentType(bin.getContentType());
}else {
theHttpResponse.setContentType(Constants.CT_OCTET_STREAM);
}
if (bin.getContent()==null || bin.getContent().length==0) {
return;
}
theHttpResponse.setContentLength(bin.getContent().length);
ServletOutputStream oos = theHttpResponse.getOutputStream();
oos.write(bin.getContent());
oos.close();
return;
}
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) { if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
theHttpResponse.setContentType(theResponseEncoding.getBrowserFriendlyBundleContentType()); theHttpResponse.setContentType(theResponseEncoding.getBrowserFriendlyBundleContentType());
} else if (theNarrativeMode == NarrativeModeEnum.ONLY) { } else if (theNarrativeMode == NarrativeModeEnum.ONLY) {

View File

@ -28,54 +28,55 @@ import java.util.Set;
public class Constants { public class Constants {
public static final String CHARSET_UTF_8 = "UTF-8";
public static final String CT_ATOM_XML = "application/atom+xml";
public static final String CT_FHIR_JSON = "application/json+fhir"; public static final String CT_FHIR_JSON = "application/json+fhir";
public static final String CT_FHIR_XML = "application/xml+fhir"; public static final String CT_FHIR_XML = "application/xml+fhir";
public static final String PARAM_FORMAT = "_format"; public static final String CT_HTML = "text/html";
public static final String URL_TOKEN_HISTORY = "_history"; public static final String CT_JSON = "application/json";
public static final String CT_ATOM_XML = "application/atom+xml"; public static final String CT_OCTET_STREAM = "application/octet-stream";
public static final Set<String> FORMAT_VAL_XML; public static final String CT_TEXT = "text/plain";
public static final String CT_XML = "application/xml";
public static final String FORMAT_JSON = "json";
public static final Set<String> FORMAT_VAL_JSON; public static final Set<String> FORMAT_VAL_JSON;
public static final Map<String, EncodingEnum> FORMAT_VAL_TO_ENCODING; public static final Map<String, EncodingEnum> FORMAT_VAL_TO_ENCODING;
public static final String CT_XML = "application/xml"; public static final Set<String> FORMAT_VAL_XML;
public static final String CT_JSON = "application/json"; public static final String FORMAT_XML = "xml";
public static final String CT_HTML = "text/html"; public static final String HEADER_ACCEPT = "Accept";
public static final String PARAM_NARRATIVE = "_narrative"; public static final String HEADER_CATEGORY = "Category";
public static final String PARAM_HISTORY = "_history";
public static final String PARAM_PRETTY = "_pretty";
public static final String PARAM_QUERY = "_query";
public static final int STATUS_HTTP_201_CREATED = 201;
public static final String CT_TEXT = "text/plain";
public static final int STATUS_HTTP_200_OK = 200;
public static final int STATUS_HTTP_422_UNPROCESSABLE_ENTITY = 422;
public static final int STATUS_HTTP_404_NOT_FOUND = 404;
public static final int STATUS_HTTP_400_BAD_REQUEST = 400;
public static final int STATUS_HTTP_405_METHOD_NOT_ALLOWED = 405;
public static final int STATUS_HTTP_409_CONFLICT = 409;
public static final int STATUS_HTTP_412_PRECONDITION_FAILED = 412;
public static final String HEADER_CONTENT_LOCATION = "Content-Location"; public static final String HEADER_CONTENT_LOCATION = "Content-Location";
public static final int STATUS_HTTP_204_NO_CONTENT = 204;
public static final String PARAM_COUNT = "_count";
public static final String PARAM_SINCE = "_since";
public static final String PARAM_SEARCH = "_search";
public static final String HEADER_LAST_MODIFIED = "Last-Modified"; public static final String HEADER_LAST_MODIFIED = "Last-Modified";
public static final String HEADER_LAST_MODIFIED_LOWERCASE = HEADER_LAST_MODIFIED.toLowerCase(); public static final String HEADER_LAST_MODIFIED_LOWERCASE = HEADER_LAST_MODIFIED.toLowerCase();
public static final String PARAM_VALIDATE = "_validate"; public static final String OPENSEARCH_NS_OLDER = "http://purl.org/atompub/tombstones/1.0";
public static final int STATUS_HTTP_401_CLIENT_UNAUTHORIZED = 401; public static final String PARAM_COUNT = "_count";
public static final int STATUS_HTTP_500_INTERNAL_ERROR = 500; public static final String PARAM_DELETE = "_delete";
public static final String PARAM_FORMAT = "_format";
public static final String PARAM_HISTORY = "_history";
public static final String PARAM_INCLUDE = "_include";
public static final String PARAM_NARRATIVE = "_narrative";
public static final String PARAM_PRETTY = "_pretty";
public static final String PARAM_PRETTY_VALUE_TRUE = "true"; public static final String PARAM_PRETTY_VALUE_TRUE = "true";
public static final String PARAM_QUERY = "_query";
public static final String PARAM_SEARCH = "_search";
public static final String PARAM_SINCE = "_since";
public static final String PARAM_SORT = "_sort"; public static final String PARAM_SORT = "_sort";
public static final String PARAM_SORT_ASC = "_sort:asc"; public static final String PARAM_SORT_ASC = "_sort:asc";
public static final String PARAM_SORT_DESC = "_sort:desc"; public static final String PARAM_SORT_DESC = "_sort:desc";
public static final String HEADER_CATEGORY = "Category";
public static final String OPENSEARCH_NS_OLDER = "http://purl.org/atompub/tombstones/1.0";
public static final String PARAM_TAGS = "_tags"; public static final String PARAM_TAGS = "_tags";
public static final String CHARSET_UTF_8 = "UTF-8"; public static final String PARAM_VALIDATE = "_validate";
public static final String PARAM_DELETE = "_delete";
public static final String FORMAT_XML = "xml";
public static final String FORMAT_JSON = "json";
public static final String PARAM_INCLUDE = "_include";
public static final String PARAMQUALIFIER_STRING_EXACT = ":exact"; public static final String PARAMQUALIFIER_STRING_EXACT = ":exact";
public static final String HEADER_ACCEPT = "Accept"; public static final int STATUS_HTTP_200_OK = 200;
public static final int STATUS_HTTP_201_CREATED = 201;
public static final int STATUS_HTTP_204_NO_CONTENT = 204;
public static final int STATUS_HTTP_400_BAD_REQUEST = 400;
public static final int STATUS_HTTP_401_CLIENT_UNAUTHORIZED = 401;
public static final int STATUS_HTTP_404_NOT_FOUND = 404;
public static final int STATUS_HTTP_405_METHOD_NOT_ALLOWED = 405;
public static final int STATUS_HTTP_409_CONFLICT = 409;
public static final int STATUS_HTTP_412_PRECONDITION_FAILED = 412;
public static final int STATUS_HTTP_422_UNPROCESSABLE_ENTITY = 422;
public static final int STATUS_HTTP_500_INTERNAL_ERROR = 500;
public static final String URL_TOKEN_HISTORY = "_history";
static { static {
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>(); Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();

View File

@ -343,40 +343,49 @@ public class RestfulServer extends HttpServlet {
findResourceMethods(theProvider, clazz); findResourceMethods(theProvider, clazz);
} }
private void findResourceMethods(Object theProvider, Class<?> clazz) { private void findResourceMethods(Object theProvider, Class<?> clazz) throws ConfigurationException {
int count = 0;
for (Method m : clazz.getDeclaredMethods()) { for (Method m : clazz.getDeclaredMethods()) {
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, myFhirContext, theProvider);
if (foundMethodBinding == null) {
continue;
}
count++;
if (!Modifier.isPublic(m.getModifiers())) { if (!Modifier.isPublic(m.getModifiers())) {
ourLog.debug("Ignoring non-public method: {}", m); throw new ConfigurationException("Method '" + m.getName() + "' is not public, FHIR RESTful methods must be public");
} else { } else {
if (!Modifier.isStatic(m.getModifiers())) { if (Modifier.isStatic(m.getModifiers())) {
throw new ConfigurationException("Method '" + m.getName() + "' is static, FHIR RESTful methods must not be static");
} else {
ourLog.debug("Scanning public method: {}#{}", theProvider.getClass(), m.getName()); ourLog.debug("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, myFhirContext, theProvider); String resourceName = foundMethodBinding.getResourceName();
if (foundMethodBinding != null) { ResourceBinding resourceBinding;
if (resourceName == null) {
String resourceName = foundMethodBinding.getResourceName(); resourceBinding = myNullResourceBinding;
ResourceBinding resourceBinding;
if (resourceName == null) {
resourceBinding = myNullResourceBinding;
} else {
RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(resourceName);
if (myResourceNameToProvider.containsKey(definition.getName())) {
resourceBinding = myResourceNameToProvider.get(definition.getName());
} else {
resourceBinding = new ResourceBinding();
resourceBinding.setResourceName(resourceName);
myResourceNameToProvider.put(resourceName, resourceBinding);
}
}
resourceBinding.addMethod(foundMethodBinding);
ourLog.debug(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
} else { } else {
ourLog.debug(" * Method: {}#{} is not a handler", theProvider.getClass(), m.getName()); RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(resourceName);
if (myResourceNameToProvider.containsKey(definition.getName())) {
resourceBinding = myResourceNameToProvider.get(definition.getName());
} else {
resourceBinding = new ResourceBinding();
resourceBinding.setResourceName(resourceName);
myResourceNameToProvider.put(resourceName, resourceBinding);
}
} }
resourceBinding.addMethod(foundMethodBinding);
ourLog.debug(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
} }
} }
} }
if (count == 0) {
throw new ConfigurationException("Did not find any annotated RESTful methods on provider class " + theProvider.getClass().getCanonicalName());
}
} }
private void findSystemMethods(Object theSystemProvider) { private void findSystemMethods(Object theSystemProvider) {
@ -472,8 +481,8 @@ public class RestfulServer extends HttpServlet {
String servletContextPath = ""; String servletContextPath = "";
if (theRequest.getServletContext() != null) { if (theRequest.getServletContext() != null) {
servletContextPath = StringUtils.defaultString(theRequest.getServletContext().getContextPath()); servletContextPath = StringUtils.defaultString(theRequest.getServletContext().getContextPath());
// } else { // } else {
//servletContextPath = servletPath; // servletContextPath = servletPath;
} }
if (ourLog.isTraceEnabled()) { if (ourLog.isTraceEnabled()) {

View File

@ -39,6 +39,7 @@ import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Conformance; import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource; import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport; import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
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;
@ -69,6 +70,28 @@ public class JsonParserTest {
assertEquals("{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://foo#bar\"}]}", str); assertEquals("{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://foo#bar\"}]}", str);
} }
@Test
public void testEncodeBinaryResource() {
Binary patient = new Binary();
patient.setContentType("foo");
patient.setContent(new byte[] {1,2,3,4});
String val = ourCtx.newJsonParser().encodeResourceToString(patient);
assertEquals("{\"resourceType\":\"Binary\",\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}",val);
}
@Test
public void testParseBinaryResource() {
Binary val = ourCtx.newJsonParser().parseResource(Binary.class, "{\"resourceType\":\"Binary\",\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}");
assertEquals("foo", val.getContentType());
assertArrayEquals(new byte[] {1,2,3,4}, val.getContent());
}
@Test @Test
public void testTagList() { public void testTagList() {

View File

@ -1,11 +1,7 @@
package ca.uhn.fhir.parser; package ca.uhn.fhir.parser;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.*;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -43,6 +39,7 @@ import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Conformance; import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource; import ca.uhn.fhir.model.dstu.resource.Conformance.RestResource;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport; import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
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;
@ -81,6 +78,28 @@ public class XmlParserTest {
} }
@Test
public void testEncodeBinaryResource() {
Binary patient = new Binary();
patient.setContentType("foo");
patient.setContent(new byte[] {1,2,3,4});
String val = ourCtx.newXmlParser().encodeResourceToString(patient);
assertEquals("<Binary xmlns=\"http://hl7.org/fhir\" contentType=\"foo\">AQIDBA==</Binary>", val);
}
@Test
public void testParseBinaryResource() {
Binary val = ourCtx.newXmlParser().parseResource(Binary.class, "<Binary xmlns=\"http://hl7.org/fhir\" contentType=\"foo\">AQIDBA==</Binary>");
assertEquals("foo", val.getContentType());
assertArrayEquals(new byte[] {1,2,3,4}, val.getContent());
}
@Test @Test
public void testTagList() { public void testTagList() {
@ -92,7 +111,7 @@ public class XmlParserTest {
"</taglist>"; "</taglist>";
//@formatter:on //@formatter:on
TagList tagList = new FhirContext().newXmlParser().parseTagList(tagListStr); TagList tagList = ourCtx.newXmlParser().parseTagList(tagListStr);
assertEquals(3, tagList.size()); assertEquals(3, tagList.size());
assertEquals("term0", tagList.get(0).getTerm()); assertEquals("term0", tagList.get(0).getTerm());
assertEquals("label0", tagList.get(0).getLabel()); assertEquals("label0", tagList.get(0).getLabel());

View File

@ -0,0 +1,170 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import ch.qos.logback.core.util.ContentTypeUtil;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class BinaryTest {
private static CloseableHttpClient ourClient;
private static FhirContext ourCtx = new FhirContext();
private static Binary ourLast;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BinaryTest.class);
private static int ourPort;
private static Server ourServer;
@Before
public void before() {
ourLast=null;
}
@Test
public void testRead() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary/foo");
HttpResponse status = ourClient.execute(httpGet);
byte[] responseContent = IOUtils.toByteArray(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("foo", status.getFirstHeader("content-type").getValue());
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, responseContent);
}
@Test
public void testCreate() throws Exception {
Binary res = new Binary();
res.setContent(new byte[] { 1, 2, 3, 4 });
res.setContentType("text/plain");
String stringContent = ourCtx.newJsonParser().encodeResourceToString(res);
HttpPost http = new HttpPost("http://localhost:" + ourPort + "/Binary");
http.setEntity(new StringEntity(stringContent, ContentType.create(Constants.CT_FHIR_JSON, "UTF-8")));
HttpResponse status = ourClient.execute(http);
assertEquals(201, status.getStatusLine().getStatusCode());
assertEquals("text/plain", ourLast.getContentType());
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, ourLast.getContent());
}
@Test
public void testSearch() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Binary?");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals(Constants.CT_ATOM_XML + "; charset=UTF-8", status.getFirstHeader("content-type").getValue());
ourLog.info(responseContent);
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
Binary bin = (Binary) bundle.getEntries().get(0).getResource();
assertEquals("text/plain", bin.getContentType());
assertArrayEquals(new byte[] { 1, 2, 3, 4 }, bin.getContent());
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(ourPort);
ResourceProvider patientProvider = new ResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class ResourceProvider implements IResourceProvider {
@Create
public MethodOutcome create(@ResourceParam Binary theBinary) {
ourLast = theBinary;
return new MethodOutcome(new IdDt("1"));
}
@Override
public Class<? extends IResource> getResourceType() {
return Binary.class;
}
@Read
public Binary read(@IdParam IdDt theId) {
Binary retVal = new Binary();
retVal.setContent(new byte[] { 1, 2, 3, 4 });
retVal.setContentType(theId.getUnqualifiedId());
return retVal;
}
@Search
public List<Binary> search() {
Binary retVal = new Binary();
retVal.setContent(new byte[] { 1, 2, 3, 4 });
retVal.setContentType("text/plain");
return Collections.singletonList(retVal);
}
}
}

View File

@ -0,0 +1,106 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import javax.servlet.ServletException;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.After;
import org.junit.Test;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Binary;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
public class ResourceProviderWithNoMethodsTest {
private Server ourServer;
@After
public void after() throws Exception {
ourServer.stop();
}
@Test
public void testNoAnnotatedMethods() throws Exception {
int port = RandomServerPortProvider.findFreePort();
ourServer = new Server(port);
ResourceProvider patientProvider = new ResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
try {
ourServer.start();
fail();
} catch (ServletException e) {
assertEquals(e.getCause().getClass(), ConfigurationException.class);
}
}
@Test
public void testNonPublicMethod() throws Exception {
int port = RandomServerPortProvider.findFreePort();
ourServer = new Server(port);
NonPublicMethodProvider patientProvider = new NonPublicMethodProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
try {
ourServer.start();
fail();
} catch (ServletException e) {
assertEquals(e.getCause().getClass(), ConfigurationException.class);
}
}
public static class ResourceProvider implements IResourceProvider {
@Override
public Class<? extends IResource> getResourceType() {
return Binary.class;
}
public Binary read(@IdParam IdDt theId) {
Binary retVal = new Binary();
retVal.setContent(new byte[] { 1, 2, 3, 4 });
retVal.setContentType(theId.getUnqualifiedId());
return retVal;
}
}
public static class NonPublicMethodProvider implements IResourceProvider {
@Override
public Class<? extends IResource> getResourceType() {
return Binary.class;
}
@Read
protected Binary read(@IdParam IdDt theId) {
Binary retVal = new Binary();
retVal.setContent(new byte[] { 1, 2, 3, 4 });
retVal.setContentType(theId.getUnqualifiedId());
return retVal;
}
}
}