More work to get transactions working

This commit is contained in:
jamesagnew 2014-05-26 09:09:54 -04:00
parent de3fbc9023
commit 245533773f
34 changed files with 1518 additions and 583 deletions

View File

@ -36,6 +36,7 @@ public class BundleEntry extends BaseBundle {
//@formatter:on
private TagList myCategories;
private InstantDt myDeletedAt;
private StringDt myLinkAlternate;
private StringDt myLinkSelf;
private InstantDt myPublished;
private IResource myResource;
@ -70,6 +71,13 @@ public class BundleEntry extends BaseBundle {
return myDeletedAt;
}
public StringDt getLinkAlternate() {
if (myLinkAlternate == null) {
myLinkAlternate = new StringDt();
}
return myLinkAlternate;
}
public StringDt getLinkSelf() {
if (myLinkSelf == null) {
myLinkSelf = new StringDt();
@ -113,7 +121,7 @@ public class BundleEntry extends BaseBundle {
public boolean isEmpty() {
//@formatter:off
return super.isEmpty() &&
ElementUtil.isEmpty(myCategories, myDeletedAt, myLinkSelf, myPublished, myResource, mySummary, myTitle, myUpdated);
ElementUtil.isEmpty(myCategories, myDeletedAt, myLinkAlternate, myLinkSelf, myPublished, myResource, mySummary, myTitle, myUpdated);
//@formatter:on
}
@ -124,6 +132,10 @@ public class BundleEntry extends BaseBundle {
myDeletedAt = theDeletedAt;
}
public void setLinkAlternate(StringDt theLinkAlternate) {
myLinkAlternate = theLinkAlternate;
}
public void setLinkSelf(StringDt theLinkSelf) {
if (myLinkSelf == null) {
myLinkSelf = new StringDt();

View File

@ -25,6 +25,27 @@ import ca.uhn.fhir.model.primitive.InstantDt;
public enum ResourceMetadataKeyEnum {
/**
* If present and populated with a date/time (as an instance of {@link InstantDt}),
* this value is an indication that the resource is in the deleted state. This key
* is only used in a limited number of scenarios, such as POSTing transaction bundles
* to a server, or returning resource history.
* <p>
* Values for this key are of type <b>{@link InstantDt}</b>
* </p>
*/
DELETED_AT,
/**
* The value for this key represents a previous ID used to identify
* this resource. This key is currently only used internally during
* transaction method processing.
* <p>
* Values for this key are of type <b>{@link IdDt}</b>
* </p>
*/
PREVIOUS_ID,
/**
* The value for this key is the bundle entry <b>Published</b> time. This is
* defined by FHIR as "Time resource copied into the feed", which is
@ -50,8 +71,9 @@ public enum ResourceMetadataKeyEnum {
*
* @see TagList
*/
TAG_LIST,
TAG_LIST,
/**
* The value for this key is the bundle entry <b>Updated</b> time. This is
* defined by FHIR as "Last Updated for resource". This value is also used
@ -63,8 +85,8 @@ public enum ResourceMetadataKeyEnum {
*
* @see InstantDt
*/
UPDATED,
UPDATED,
/**
* The value for this key is the version ID of the resource object.
* <p>
@ -74,18 +96,6 @@ public enum ResourceMetadataKeyEnum {
* @deprecated The {@link IResource#getId()} resource ID will now be populated with the version ID via the {@link IdDt#getUnqualifiedVersionId()} method
*/
@Deprecated
VERSION_ID,
/**
* If present and populated with a date/time (as an instance of {@link InstantDt}),
* this value is an indication that the resource is in the deleted state. This key
* is only used in a limited number of scenarios, such as POSTing transaction bundles
* to a server, or returning resource history.
* <p>
* Values for this key are of type <b>{@link InstantDt}</b>
* </p>
*/
DELETED_AT;
VERSION_ID;
}

View File

@ -25,11 +25,13 @@ import static org.apache.commons.lang3.StringUtils.*;
import java.math.BigDecimal;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.model.api.BasePrimitive;
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
import ca.uhn.fhir.model.api.annotation.SimpleSetter;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.server.Constants;
/**
* Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs,
@ -94,6 +96,27 @@ public class IdDt extends BasePrimitive<String> {
setValue(theValue);
}
/**
* Constructor
*
* @param theResourceType The resource type (e.g. "Patient")
* @param theId The ID (e.g. "123")
* @param theVersionId The version ID ("e.g. "456")
*/
public IdDt(String theResourceType, String theId, String theVersionId) {
Validate.notBlank(theResourceType, "Resource type must not be blank");
Validate.notBlank(theId, "ID must not be blank");
myResourceType = theResourceType;
myUnqualifiedId = theId;
myUnqualifiedVersionId = StringUtils.defaultIfBlank(theVersionId, null);
if (myUnqualifiedVersionId != null) {
myValue = myResourceType + '/' + myUnqualifiedId + '/' + Constants.PARAM_HISTORY + '/' + myUnqualifiedVersionId;
} else {
myValue = myResourceType + '/' + myUnqualifiedId;
}
}
/**
* Returns the unqualified portion of this ID as a big decimal, or <code>null</code> if the value is null
*
@ -144,8 +167,8 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or
* a simple ID. Use {@link #getUnqualifiedId()} to get just the ID portion.
* Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a
* simple ID. Use {@link #getUnqualifiedId()} to get just the ID portion.
*
* @see #getUnqualifiedId()
*/
@ -200,15 +223,15 @@ public class IdDt extends BasePrimitive<String> {
myUnqualifiedId = theValue.substring(idIndex + 1);
myUnqualifiedVersionId = null;
}
if (idIndex <= 0) {
myResourceType = null;
}else {
} else {
int typeIndex = theValue.lastIndexOf('/', idIndex - 1);
if (typeIndex == -1) {
myResourceType = theValue.substring(0,idIndex);
myResourceType = theValue.substring(0, idIndex);
} else {
myResourceType = theValue.substring(typeIndex+1, idIndex);
myResourceType = theValue.substring(typeIndex + 1, idIndex);
}
}
@ -237,8 +260,8 @@ public class IdDt extends BasePrimitive<String> {
}
/**
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other
* words, it consists only of digits)
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only
* of digits)
*/
public boolean isValidLong() {
String id = getUnqualifiedId();
@ -253,4 +276,33 @@ public class IdDt extends BasePrimitive<String> {
return true;
}
/**
* Returns a view of this ID as a fully qualified URL, given a server base and resource name
* (which will only be used if the ID does not already contain those respective parts). Essentially,
* because IdDt can contain either a complete URL or a partial one (or even jut a simple ID), this
* method may be used to translate into a complete URL.
*
* @param theServerBase The server base (e.g. "http://example.com/fhir")
* @param theResourceType The resource name (e.g. "Patient")
* @return A fully qualified URL for this ID (e.g. "http://example.com/fhir/Patient/1")
*/
public String toQualifiedUrl(String theServerBase, String theResourceType) {
if (getValue().startsWith("http")) {
return getValue();
}
StringBuilder retVal = new StringBuilder();
retVal.append(theServerBase);
if (retVal.charAt(retVal.length()-1) != '/') {
retVal.append('/');
}
if (isNotBlank(getResourceType())) {
retVal.append(getResourceType());
}else {
retVal.append(theResourceType);
}
retVal.append('/');
retVal.append(getUnqualifiedId());
return retVal.toString();
}
}

View File

@ -168,7 +168,8 @@ public class JsonParser extends BaseParser implements IParser {
linkStarted = writeAtomLink(eventWriter, "last", theBundle.getLinkLast(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase(), linkStarted);
if (linkStarted) {
eventWriter.writeEnd();}
eventWriter.writeEnd();
}
writeOptionalTagWithTextNode(eventWriter, "totalResults", theBundle.getTotalResults());
@ -181,11 +182,13 @@ public class JsonParser extends BaseParser implements IParser {
writeTagWithTextNode(eventWriter, "deleted", nextEntry.getDeletedAt());
writeTagWithTextNode(eventWriter, "title", nextEntry.getTitle());
writeTagWithTextNode(eventWriter, "id", nextEntry.getId());
linkStarted = false;
linkStarted=writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf(), linkStarted);
linkStarted = writeAtomLink(eventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted);
if (linkStarted) {
eventWriter.writeEnd();}
eventWriter.writeEnd();
}
writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated());
writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
@ -839,11 +842,11 @@ public class JsonParser extends BaseParser implements IParser {
private boolean writeAtomLink(JsonGenerator theEventWriter, String theRel, StringDt theLink, boolean theStarted) {
boolean retVal = theStarted;
if (isNotBlank(theLink.getValue())) {
if (theStarted==false) {
if (theStarted == false) {
theEventWriter.writeStartArray("link");
retVal=true;
retVal = true;
}
theEventWriter.writeStartObject();
theEventWriter.write("rel", theRel);
theEventWriter.write("href", theLink.getValue());
@ -898,10 +901,10 @@ public class JsonParser extends BaseParser implements IParser {
private void writeTagWithTextNode(JsonGenerator theEventWriter, String theElementName, StringDt theStringDt) {
if (StringUtils.isNotBlank(theStringDt.getValue())) {
theEventWriter.write(theElementName, theStringDt.getValue());
}
// else {
// theEventWriter.writeNull(theElementName);
// }
}
// else {
// theEventWriter.writeNull(theElementName);
// }
}
private class HeldExtension {

View File

@ -345,6 +345,11 @@ class ParserState<T> {
return;
}
IdDt id = myEntry.getId();
if(id!=null && id.isEmpty()==false) {
myEntry.getResource().setId(id);
}
Map<ResourceMetadataKeyEnum, Object> metadata = myEntry.getResource().getResourceMetadata();
if (myEntry.getPublished().isEmpty() == false) {
metadata.put(ResourceMetadataKeyEnum.PUBLISHED, myEntry.getPublished());
@ -361,10 +366,10 @@ class ParserState<T> {
}
if (!myEntry.getLinkSelf().isEmpty()) {
String linkSelfValue = myEntry.getLinkSelf().getValue();
IdDt id = new IdDt(linkSelfValue);
myEntry.getResource().setId(id);
if (isNotBlank(id.getUnqualifiedVersionId())) {
metadata.put(ResourceMetadataKeyEnum.VERSION_ID, id);
IdDt linkSelf = new IdDt(linkSelfValue);
myEntry.getResource().setId(linkSelf);
if (isNotBlank(linkSelf.getUnqualifiedVersionId())) {
metadata.put(ResourceMetadataKeyEnum.VERSION_ID, linkSelf);
}
}
@ -438,7 +443,11 @@ class ParserState<T> {
myInstance.getLinkBase().setValueAsString(myHref);
}
} else {
myEntry.getLinkSelf().setValueAsString(myHref);
if ("self".equals(myRel)) {
myEntry.getLinkSelf().setValueAsString(myHref);
} else if ("alternate".equals(myRel)) {
myEntry.getLinkAlternate().setValueAsString(myHref);
}
}
pop();
}

View File

@ -75,6 +75,7 @@ import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.narrative.INarrativeGenerator;
import ca.uhn.fhir.util.NonPrettyPrintWriterWrapper;
import ca.uhn.fhir.util.PrettyPrintWriterWrapper;
public class XmlParser extends BaseParser implements IParser {
@ -149,7 +150,7 @@ public class XmlParser extends BaseParser implements IParser {
eventWriter.writeNamespace("at", TOMBSTONES_NS);
eventWriter.writeAttribute("ref", nextEntry.getId().getValueAsString());
eventWriter.writeAttribute("when", nextEntry.getDeletedAt().getValueAsString());
}else {
} else {
eventWriter.writeStartElement("entry");
}
@ -174,6 +175,9 @@ public class XmlParser extends BaseParser implements IParser {
writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf());
}
if (!nextEntry.getLinkAlternate().isEmpty()) {
writeAtomLink(eventWriter, "alternate", nextEntry.getLinkAlternate());
}
IResource resource = nextEntry.getResource();
if (resource != null && !resource.isEmpty()) {
@ -302,7 +306,8 @@ public class XmlParser extends BaseParser implements IParser {
PrettyPrintWriterWrapper retVal = new PrettyPrintWriterWrapper(eventWriter);
return retVal;
} else {
return eventWriter;
NonPrettyPrintWriterWrapper retVal = new NonPrettyPrintWriterWrapper(eventWriter);
return retVal;
}
}

View File

@ -177,7 +177,7 @@ public abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding
}
invokeServerMethod(params);
EncodingEnum responseEncoding = determineResponseEncoding(theRequest);
EncodingEnum responseEncoding = RestfulServer.determineResponseEncoding(theRequest);
theResponse.setContentType(responseEncoding.getResourceContentType());
theResponse.setStatus(Constants.STATUS_HTTP_200_OK);

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.rest.method;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.io.Reader;
@ -30,9 +30,7 @@ import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
@ -81,9 +79,11 @@ import ca.uhn.fhir.util.ReflectionUtil;
public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseMethodBinding.class);
private FhirContext myContext;
private Method myMethod;
private List<IParameter> myParameters;
private Object myProvider;
public BaseMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
@ -104,13 +104,17 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
return myMethod;
}
public List<IParameter> getParameters() {
return myParameters;
}
public Object getProvider() {
return myProvider;
}
/**
* Returns the name of the resource this method handles, or
* <code>null</code> if this method is not resource specific
* Returns the name of the resource this method handles, or <code>null</code> if this method is not resource
* specific
*/
public abstract String getResourceName();
@ -118,26 +122,60 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
public abstract RestfulOperationSystemEnum getSystemOperationType();
public abstract boolean incomingServerRequestMatchesMethod(Request theRequest);
public abstract BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException;
public abstract void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException;
public abstract boolean incomingServerRequestMatchesMethod(Request theRequest);
/** For unit tests only */
public void setParameters(List<IParameter> theParameters) {
myParameters = theParameters;
}
protected IParser createAppropriateParserForParsingResponse(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode) {
EncodingEnum encoding = EncodingEnum.forContentType(theResponseMimeType);
if (encoding==null) {
EncodingEnum encoding = EncodingEnum.forContentType(theResponseMimeType);
if (encoding == null) {
NonFhirResponseException ex = NonFhirResponseException.newInstance(theResponseStatusCode, theResponseMimeType, theResponseReader);
populateException(ex, theResponseReader);
throw ex;
}
IParser parser=encoding.newParser(getContext());
IParser parser = encoding.newParser(getContext());
return parser;
}
public List<IParameter> getParameters() {
return myParameters;
protected IParser createAppropriateParserForParsingServerRequest(Request theRequest) {
String contentTypeHeader = theRequest.getServletRequest().getHeader("content-type");
EncodingEnum encoding;
if (isBlank(contentTypeHeader)) {
encoding = EncodingEnum.XML;
} else {
int semicolon = contentTypeHeader.indexOf(';');
if (semicolon != -1) {
contentTypeHeader = contentTypeHeader.substring(0, semicolon);
}
encoding = EncodingEnum.forContentType(contentTypeHeader);
}
if (encoding == null) {
throw new InvalidRequestException("Request contins non-FHIR conent-type header value: " + contentTypeHeader);
}
IParser parser = encoding.newParser(getContext());
return parser;
}
protected Object[] createParametersForServerRequest(Request theRequest, IResource theResource) {
Object[] params = new Object[getParameters().size()];
for (int i = 0; i < getParameters().size(); i++) {
IParameter param = getParameters().get(i);
if (param == null) {
continue;
}
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, theResource);
}
return params;
}
protected Object invokeServerMethod(Object[] theMethodParams) {
@ -155,9 +193,36 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
}
}
/** For unit tests only */
public void setParameters(List<IParameter> theParameters) {
myParameters = theParameters;
protected BaseServerResponseException processNon2xxResponseAndReturnExceptionToThrow(int theStatusCode, String theResponseMimeType, Reader theResponseReader) {
BaseServerResponseException ex;
switch (theStatusCode) {
case Constants.STATUS_HTTP_400_BAD_REQUEST:
ex = new InvalidRequestException("Server responded with HTTP 400");
break;
case Constants.STATUS_HTTP_404_NOT_FOUND:
ex = new ResourceNotFoundException("Server responded with HTTP 404");
break;
case Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED:
ex = new MethodNotAllowedException("Server responded with HTTP 405");
break;
case Constants.STATUS_HTTP_409_CONFLICT:
ex = new ResourceVersionConflictException("Server responded with HTTP 409");
break;
case Constants.STATUS_HTTP_412_PRECONDITION_FAILED:
ex = new ResourceVersionNotSpecifiedException("Server responded with HTTP 412");
break;
case Constants.STATUS_HTTP_422_UNPROCESSABLE_ENTITY:
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theStatusCode);
OperationOutcome operationOutcome = parser.parseResource(OperationOutcome.class, theResponseReader);
ex = new UnprocessableEntityException(operationOutcome);
break;
default:
ex = new UnclassifiedServerFailureException(theStatusCode, "Server responded with HTTP " + theStatusCode);
break;
}
populateException(ex, theResponseReader);
return ex;
}
@SuppressWarnings("unchecked")
@ -175,7 +240,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
DeleteTags deleteTags = theMethod.getAnnotation(DeleteTags.class);
Transaction transaction = theMethod.getAnnotation(Transaction.class);
// ** if you add another annotation above, also add it to the next line:
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags,transaction)) {
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags, transaction)) {
return null;
}
@ -313,50 +378,6 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
// return sm;
}
public static EncodingEnum determineResponseEncoding(Request theReq) {
String[] format = theReq.getParameters().remove(Constants.PARAM_FORMAT);
if (format != null) {
for (String nextFormat : format) {
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextFormat);
if (retVal != null) {
return retVal;
}
}
}
Enumeration<String> acceptValues = theReq.getServletRequest().getHeaders("Accept");
if (acceptValues != null) {
while (acceptValues.hasMoreElements()) {
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(acceptValues.nextElement());
if (retVal != null) {
return retVal;
}
}
}
return EncodingEnum.XML;
}
protected IParser createAppropriateParserForParsingServerRequest(Request theRequest) {
String contentTypeHeader = theRequest.getServletRequest().getHeader("content-type");
EncodingEnum encoding;
if (isBlank(contentTypeHeader)) {
encoding = EncodingEnum.XML;
} else {
int semicolon = contentTypeHeader.indexOf(';');
if (semicolon!=-1) {
contentTypeHeader=contentTypeHeader.substring(0,semicolon);
}
encoding = EncodingEnum.forContentType(contentTypeHeader);
}
if (encoding==null) {
throw new InvalidRequestException("Request contins non-FHIR conent-type header value: " + contentTypeHeader);
}
IParser parser=encoding.newParser(getContext());
return parser;
}
public static boolean verifyMethodHasZeroOrOneOperationAnnotation(Method theNextMethod, Object... theAnnotations) {
Object obj1 = null;
for (Object object : theAnnotations) {
@ -380,6 +401,15 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
return true;
}
private static void populateException(BaseServerResponseException theEx, Reader theResponseReader) {
try {
String responseText = IOUtils.toString(theResponseReader);
theEx.setResponseBody(responseText);
} catch (IOException e) {
ourLog.debug("Failed to read response", e);
}
}
private static String toLogString(Class<?> theType) {
if (theType == null) {
return null;
@ -398,18 +428,6 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
return retVal;
}
protected Object[] createParametersForServerRequest(Request theRequest, IResource theResource) {
Object[] params = new Object[getParameters().size()];
for (int i = 0; i < getParameters().size(); i++) {
IParameter param = getParameters().get(i);
if (param == null) {
continue;
}
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, theResource);
}
return params;
}
protected static List<IResource> toResourceList(Object response) throws InternalErrorException {
if (response == null) {
return Collections.emptyList();
@ -426,59 +444,4 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
}
}
protected static boolean prettyPrintResponse(Request theRequest) {
Map<String, String[]> requestParams = theRequest.getParameters();
String[] pretty = requestParams.remove(Constants.PARAM_PRETTY);
boolean prettyPrint = false;
if (pretty != null && pretty.length > 0) {
if (Constants.PARAM_PRETTY_VALUE_TRUE.equals(pretty[0])) {
prettyPrint = true;
}
}
return prettyPrint;
}
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseMethodBinding.class);
protected BaseServerResponseException processNon2xxResponseAndReturnExceptionToThrow(int theStatusCode, String theResponseMimeType, Reader theResponseReader) {
BaseServerResponseException ex;
switch (theStatusCode) {
case Constants.STATUS_HTTP_400_BAD_REQUEST:
ex = new InvalidRequestException("Server responded with HTTP 400");
break;
case Constants.STATUS_HTTP_404_NOT_FOUND:
ex = new ResourceNotFoundException("Server responded with HTTP 404");
break;
case Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED:
ex = new MethodNotAllowedException("Server responded with HTTP 405");
break;
case Constants.STATUS_HTTP_409_CONFLICT:
ex = new ResourceVersionConflictException("Server responded with HTTP 409");
break;
case Constants.STATUS_HTTP_412_PRECONDITION_FAILED:
ex = new ResourceVersionNotSpecifiedException("Server responded with HTTP 412");
break;
case Constants.STATUS_HTTP_422_UNPROCESSABLE_ENTITY:
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theStatusCode);
OperationOutcome operationOutcome = parser.parseResource(OperationOutcome.class, theResponseReader);
ex = new UnprocessableEntityException(operationOutcome);
break;
default:
ex = new UnclassifiedServerFailureException(theStatusCode, "Server responded with HTTP " + theStatusCode);
break;
}
populateException(ex, theResponseReader);
return ex;
}
private static void populateException(BaseServerResponseException theEx, Reader theResponseReader) {
try {
String responseText = IOUtils.toString(theResponseReader);
theEx.setResponseBody(responseText);
} catch (IOException e) {
ourLog.debug("Failed to read response", e);
}
}
}

View File

@ -112,7 +112,7 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
@Override
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
EncodingEnum encoding = determineResponseEncoding(theRequest);
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest);
IParser parser = encoding.newParser(getContext());
IResource resource;
if (requestContainsResource()) {
@ -174,7 +174,7 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
if (response != null && response.getOperationOutcome() != null) {
theResponse.setContentType(encoding.getResourceContentType());
Writer writer = theResponse.getWriter();
parser.setPrettyPrint(prettyPrintResponse(theRequest));
parser.setPrettyPrint(RestfulServer.prettyPrintResponse(theRequest));
try {
parser.encodeResourceToWriter(response.getOperationOutcome(), writer);
} finally {
@ -361,7 +361,7 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
if (theE.getOperationOutcome() != null) {
theResponse.setContentType(theEncoding.getResourceContentType());
IParser parser = theEncoding.newParser(theServer.getFhirContext());
parser.setPrettyPrint(prettyPrintResponse(theRequest));
parser.setPrettyPrint(RestfulServer.prettyPrintResponse(theRequest));
Writer writer = theResponse.getWriter();
try {
parser.encodeResourceToWriter(theE.getOperationOutcome(), writer);

View File

@ -187,7 +187,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
// Pretty print
boolean prettyPrint = prettyPrintResponse(theRequest);
boolean prettyPrint = RestfulServer.prettyPrintResponse(theRequest);
// Narrative mode
Map<String, String[]> requestParams = theRequest.getParameters();
@ -201,7 +201,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
}
// Determine response encoding
EncodingEnum responseEncoding = determineResponseEncoding(theRequest);
EncodingEnum responseEncoding = RestfulServer.determineResponseEncoding(theRequest);
// Is this request coming from a browser
String uaHeader = theRequest.getServletRequest().getHeader("user-agent");
@ -237,63 +237,6 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
}
}
/**
* Subclasses may override
*/
protected Object parseRequestObject(@SuppressWarnings("unused") Request theRequest) {
return null;
}
protected static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) {
return null;
} else if (retValObj instanceof String) {
if (isNotBlank((String) retValObj)) {
return new IdDt((String) retValObj);
} else {
return null;
}
} else if (retValObj instanceof IdDt) {
if (((IdDt) retValObj).isEmpty()) {
return null;
} else {
return (IdDt) retValObj;
}
}
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + IdDt.class.getCanonicalName());
}
private static InstantDt getInstantFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) {
return null;
} else if (retValObj instanceof Date) {
return new InstantDt((Date) retValObj);
} else if (retValObj instanceof InstantDt) {
if (((InstantDt) retValObj).isEmpty()) {
return null;
} else {
return (InstantDt) retValObj;
}
}
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName());
}
private static TagList getTagListFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) {
return null;
} else if (retValObj instanceof TagList) {
if (((TagList) retValObj).isEmpty()) {
return null;
} else {
return (TagList) retValObj;
}
}
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + TagList.class.getCanonicalName());
}
private IParser getNewParser(EncodingEnum theResponseEncoding, boolean thePrettyPrint, NarrativeModeEnum theNarrativeMode) {
IParser parser;
switch (theResponseEncoding) {
@ -343,96 +286,6 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
}
}
public static Bundle createBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, NarrativeModeEnum theNarrativeMode) {
Bundle bundle = new Bundle();
bundle.getAuthorName().setValue(theAuthor);
bundle.getBundleId().setValue(UUID.randomUUID().toString());
bundle.getPublished().setToCurrentTimeInLocalTimeZone();
bundle.getLinkBase().setValue(theServerBase);
bundle.getLinkSelf().setValue(theCompleteUrl);
for (IResource next : theResult) {
BundleEntry entry = new BundleEntry();
bundle.getEntries().add(entry);
entry.setResource(next);
TagList list = (TagList) next.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
if (list != null) {
for (Tag tag : list) {
if (StringUtils.isNotBlank(tag.getTerm())) {
entry.addCategory().setTerm(tag.getTerm()).setLabel(tag.getLabel()).setScheme(tag.getScheme());
}
}
}
RuntimeResourceDefinition def = theContext.getResourceDefinition(next);
if (next.getId() != null && StringUtils.isNotBlank(next.getId().getValue())) {
entry.getId().setValue(next.getId().getValue());
entry.getTitle().setValue(def.getName() + " " + next.getId().getValue());
StringBuilder b = new StringBuilder();
b.append(theServerBase);
if (b.charAt(b.length()-1) != '/') {
b.append('/');
}
b.append(def.getName());
b.append('/');
String resId = next.getId().getUnqualifiedId();
b.append(resId);
IdDt versionId = getIdFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.VERSION_ID);
if (versionId != null) {
b.append('/');
b.append(Constants.PARAM_HISTORY);
b.append('/');
b.append(versionId.getValue());
}
InstantDt published = getInstantFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.PUBLISHED);
if (published == null) {
entry.getPublished().setToCurrentTimeInLocalTimeZone();
} else {
entry.setPublished(published);
}
InstantDt updated = getInstantFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.UPDATED);
if (updated != null) {
entry.setUpdated(updated);
}
TagList tagList = getTagListFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.TAG_LIST);
if (tagList != null) {
for (Tag nextTag : tagList) {
entry.addCategory(nextTag);
}
}
boolean haveQ = false;
if (thePrettyPrint) {
b.append('?').append(Constants.PARAM_PRETTY).append("=true");
haveQ = true;
}
if (theResponseEncoding == EncodingEnum.JSON) {
if (!haveQ) {
b.append('?');
haveQ = true;
} else {
b.append('&');
}
b.append(Constants.PARAM_FORMAT).append("=json");
}
if (theNarrativeMode != NarrativeModeEnum.NORMAL) {
b.append(Constants.PARAM_NARRATIVE).append("=").append(theNarrativeMode.name().toLowerCase());
}
entry.getLinkSelf().setValue(b.toString());
}
}
bundle.getTotalResults().setValue(theResult.size());
return bundle;
}
private void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
theHttpResponse.setStatus(200);
@ -474,6 +327,171 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Obje
}
/**
* Subclasses may override
*/
protected Object parseRequestObject(@SuppressWarnings("unused") Request theRequest) {
return null;
}
public static Bundle createBundleFromResourceList(FhirContext theContext, String theAuthor, List<IResource> theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, NarrativeModeEnum theNarrativeMode) {
Bundle bundle = new Bundle();
bundle.getAuthorName().setValue(theAuthor);
bundle.getBundleId().setValue(UUID.randomUUID().toString());
bundle.getPublished().setToCurrentTimeInLocalTimeZone();
bundle.getLinkBase().setValue(theServerBase);
bundle.getLinkSelf().setValue(theCompleteUrl);
for (IResource next : theResult) {
BundleEntry entry = new BundleEntry();
bundle.getEntries().add(entry);
entry.setResource(next);
TagList list = (TagList) next.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
if (list != null) {
for (Tag tag : list) {
if (StringUtils.isNotBlank(tag.getTerm())) {
entry.addCategory().setTerm(tag.getTerm()).setLabel(tag.getLabel()).setScheme(tag.getScheme());
}
}
}
RuntimeResourceDefinition def = theContext.getResourceDefinition(next);
if (next.getId() != null && StringUtils.isNotBlank(next.getId().getValue())) {
entry.getTitle().setValue(def.getName() + " " + next.getId().getValue());
StringBuilder b = new StringBuilder();
b.append(theServerBase);
if (b.length() > 0 && b.charAt(b.length() - 1) != '/') {
b.append('/');
}
b.append(def.getName());
b.append('/');
String resId = next.getId().getUnqualifiedId();
b.append(resId);
entry.getId().setValue(b.toString());
if (isNotBlank(next.getId().getUnqualifiedVersionId())) {
b.append('/');
b.append(Constants.PARAM_HISTORY);
b.append('/');
b.append(next.getId().getUnqualifiedVersionId());
} else {
IdDt versionId = getIdFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.VERSION_ID);
if (versionId != null) {
b.append('/');
b.append(Constants.PARAM_HISTORY);
b.append('/');
b.append(versionId.getValue());
}
}
InstantDt published = getInstantFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.PUBLISHED);
if (published == null) {
entry.getPublished().setToCurrentTimeInLocalTimeZone();
} else {
entry.setPublished(published);
}
InstantDt updated = getInstantFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.UPDATED);
if (updated != null) {
entry.setUpdated(updated);
}
InstantDt deleted = getInstantFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.DELETED_AT);
if (deleted != null) {
entry.setDeleted(deleted);
}
IdDt previous = getIdFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.PREVIOUS_ID);
if (previous != null) {
entry.getLinkAlternate().setValue(previous.toQualifiedUrl(theServerBase, def.getName()));
}
TagList tagList = getTagListFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.TAG_LIST);
if (tagList != null) {
for (Tag nextTag : tagList) {
entry.addCategory(nextTag);
}
}
// boolean haveQ = false;
// if (thePrettyPrint) {
// b.append('?').append(Constants.PARAM_PRETTY).append("=true");
// haveQ = true;
// }
// if (theResponseEncoding == EncodingEnum.JSON) {
// if (!haveQ) {
// b.append('?');
// haveQ = true;
// } else {
// b.append('&');
// }
// b.append(Constants.PARAM_FORMAT).append("=json");
// }
// if (theNarrativeMode != NarrativeModeEnum.NORMAL) {
// b.append(Constants.PARAM_NARRATIVE).append("=").append(theNarrativeMode.name().toLowerCase());
// }
entry.getLinkSelf().setValue(b.toString());
}
}
bundle.getTotalResults().setValue(theResult.size());
return bundle;
}
private static InstantDt getInstantFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) {
return null;
} else if (retValObj instanceof Date) {
return new InstantDt((Date) retValObj);
} else if (retValObj instanceof InstantDt) {
if (((InstantDt) retValObj).isEmpty()) {
return null;
} else {
return (InstantDt) retValObj;
}
}
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName());
}
private static TagList getTagListFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) {
return null;
} else if (retValObj instanceof TagList) {
if (((TagList) retValObj).isEmpty()) {
return null;
} else {
return (TagList) retValObj;
}
}
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + TagList.class.getCanonicalName());
}
protected static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
Object retValObj = theResourceMetadata.get(theKey);
if (retValObj == null) {
return null;
} else if (retValObj instanceof String) {
if (isNotBlank((String) retValObj)) {
return new IdDt((String) retValObj);
} else {
return null;
}
} else if (retValObj instanceof IdDt) {
if (((IdDt) retValObj).isEmpty()) {
return null;
} else {
return (IdDt) retValObj;
}
}
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + IdDt.class.getCanonicalName());
}
public enum MethodReturnTypeEnum {
BUNDLE, LIST_OF_RESOURCES, RESOURCE
}

View File

@ -155,7 +155,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
TagList resp = (TagList) invokeServerMethod(params);
EncodingEnum responseEncoding = determineResponseEncoding(theRequest);
EncodingEnum responseEncoding = RestfulServer.determineResponseEncoding(theRequest);
theResponse.setContentType(responseEncoding.getResourceContentType());
theResponse.setStatus(Constants.STATUS_HTTP_200_OK);
@ -164,7 +164,7 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
theServer.addHeadersToResponse(theResponse);
IParser parser = responseEncoding.newParser(getContext());
parser.setPrettyPrint(prettyPrintResponse(theRequest));
parser.setPrettyPrint(RestfulServer.prettyPrintResponse(theRequest));
PrintWriter writer = theResponse.getWriter();
try {
parser.encodeTagListToWriter(resp, writer);

View File

@ -23,42 +23,45 @@ package ca.uhn.fhir.rest.method;
import static org.apache.commons.lang3.StringUtils.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.annotation.TransactionParam;
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
import ca.uhn.fhir.rest.param.TransactionParameter;
import ca.uhn.fhir.rest.param.IParameter;
import ca.uhn.fhir.rest.param.TransactionParameter;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class TransactionMethodBinding extends BaseResourceReturningMethodBinding {
private int myResourceParameterIndex;
private int myTransactionParamIndex;
public TransactionMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
super(null, theMethod, theConetxt, theProvider);
myResourceParameterIndex = -1;
myTransactionParamIndex = -1;
int index=0;
for (IParameter next : getParameters()) {
if (next instanceof TransactionParameter) {
myResourceParameterIndex = index;
myTransactionParamIndex = index;
}
index++;
}
if (myResourceParameterIndex==-1) {
if (myTransactionParamIndex==-1) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getCanonicalName() + " does not have a parameter annotated with the @" + TransactionParam.class + " annotation");
}
}
@ -88,16 +91,42 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
return ReturnTypeEnum.BUNDLE;
}
@SuppressWarnings("unchecked")
@Override
public List<IResource> invokeServer(Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
@SuppressWarnings("unchecked")
List<IResource> resources = (List<IResource>) theMethodParams[myTransactionParamIndex];
List<IdDt> oldIds= new ArrayList<IdDt>();
for (IResource next : resources) {
oldIds.add(next.getId());
}
List<IResource> retVal=(List<IResource>) invokeServerMethod(theMethodParams);
if (retVal.size() != resources.size()) {
throw new InternalErrorException("Transaction bundle contained " + resources.size() + " entries, but server method response contained " + retVal.size() + " entries (must be the same)");
}
for (int i =0; i < resources.size(); i++) {
IdDt oldId = oldIds.get(i);
IResource newRes = retVal.get(i);
if (newRes.getId() == null || newRes.getId().isEmpty()) {
throw new InternalErrorException("Transaction method returned resource at index " + i + " with no id specified - IResource#setId(IdDt)");
}
if (oldId != null && !oldId.isEmpty()) {
if (!oldId.getId().equals(newRes.getId())) {
newRes.getResourceMetadata().put(ResourceMetadataKeyEnum.PREVIOUS_ID, oldId.getId());
}
}
}
return retVal;
}
@Override
protected Object parseRequestObject(Request theRequest) {
EncodingEnum encoding = determineResponseEncoding(theRequest);
EncodingEnum encoding = RestfulServer.determineResponseEncoding(theRequest);
IParser parser = encoding.newParser(getContext());
Bundle bundle = parser.parseBundle(theRequest.getInputReader());
return bundle;
@ -110,7 +139,7 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
@Override
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
List<IResource> resources = (List<IResource>) theArgs[myResourceParameterIndex];
List<IResource> resources = (List<IResource>) theArgs[myTransactionParamIndex];
FhirContext context = getContext();
return new HttpPostClientInvocation(context, resources);

View File

@ -75,6 +75,7 @@ public class Constants {
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 HEADER_ACCEPT = "Accept";
static {
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();

View File

@ -20,12 +20,15 @@ package ca.uhn.fhir.rest.server;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
@ -448,6 +451,13 @@ public class RestfulServer extends HttpServlet {
handleRequest(SearchMethodBinding.RequestType.PUT, request, response);
}
private static String getBaseUrl(HttpServletRequest request) {
if (("http".equals(request.getScheme()) && request.getServerPort() == 80) || ("https".equals(request.getScheme()) && request.getServerPort() == 443))
return request.getScheme() + "://" + request.getServerName() + request.getContextPath();
else
return request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath();
}
protected void handleRequest(SearchMethodBinding.RequestType theRequestType, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException {
try {
@ -457,8 +467,6 @@ public class RestfulServer extends HttpServlet {
String resourceName = null;
String requestFullPath = StringUtils.defaultString(theRequest.getRequestURI());
// String contextPath =
// StringUtils.defaultString(request.getContextPath());
String servletPath = StringUtils.defaultString(theRequest.getServletPath());
StringBuffer requestUrl = theRequest.getRequestURL();
String servletContextPath = "";
@ -475,25 +483,30 @@ public class RestfulServer extends HttpServlet {
ourLog.trace("Context Path: {}", servletContextPath);
}
servletPath = servletContextPath;
IdDt id = null;
IdDt versionId = null;
String operation = null;
String requestPath = requestFullPath.substring(servletPath.length());
String requestPath = requestFullPath.substring(servletContextPath.length() + servletPath.length());
if (requestPath.length() > 0 && requestPath.charAt(0) == '/') {
requestPath = requestPath.substring(1);
}
int contextIndex;
if (servletPath.length() == 0) {
contextIndex = requestUrl.indexOf(requestPath);
if (requestPath.length() == 0) {
contextIndex = requestUrl.length();
} else {
contextIndex = requestUrl.indexOf(requestPath);
}
} else {
contextIndex = requestUrl.indexOf(servletPath);
}
String fhirServerBase = requestUrl.substring(0, contextIndex + servletPath.length());
String fhirServerBase;
int length = contextIndex + servletPath.length();
fhirServerBase = requestUrl.substring(0, length);
if (fhirServerBase.endsWith("/")) {
fhirServerBase = fhirServerBase.substring(0, fhirServerBase.length() - 1);
}
@ -640,6 +653,67 @@ public class RestfulServer extends HttpServlet {
// nothing by default
}
public static boolean prettyPrintResponse(Request theRequest) {
Map<String, String[]> requestParams = theRequest.getParameters();
String[] pretty = requestParams.remove(Constants.PARAM_PRETTY);
boolean prettyPrint;
if (pretty != null && pretty.length > 0) {
if (Constants.PARAM_PRETTY_VALUE_TRUE.equals(pretty[0])) {
prettyPrint = true;
} else {
prettyPrint = false;
}
} else {
prettyPrint = false;
Enumeration<String> acceptValues = theRequest.getServletRequest().getHeaders(Constants.HEADER_ACCEPT);
if (acceptValues != null) {
while (acceptValues.hasMoreElements()) {
String nextAcceptHeaderValue = acceptValues.nextElement();
if (nextAcceptHeaderValue.contains("pretty=true")) {
prettyPrint = true;
}
}
}
}
return prettyPrint;
}
public static EncodingEnum determineResponseEncoding(Request theReq) {
String[] format = theReq.getParameters().remove(Constants.PARAM_FORMAT);
if (format != null) {
for (String nextFormat : format) {
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextFormat);
if (retVal != null) {
return retVal;
}
}
}
Enumeration<String> acceptValues = theReq.getServletRequest().getHeaders(Constants.HEADER_ACCEPT);
if (acceptValues != null) {
while (acceptValues.hasMoreElements()) {
String nextAcceptHeaderValue = acceptValues.nextElement();
if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) {
for (String nextPart : nextAcceptHeaderValue.split(",")) {
int scIdx = nextPart.indexOf(';');
if (scIdx == 0) {
continue;
}
if (scIdx != -1) {
nextPart = nextPart.substring(0, scIdx);
}
nextPart = nextPart.trim();
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart);
if (retVal != null) {
return retVal;
}
}
}
}
}
return EncodingEnum.XML;
}
public enum NarrativeModeEnum {
NORMAL, ONLY, SUPPRESS;

View File

@ -0,0 +1,247 @@
package ca.uhn.fhir.util;
/*
* #%L
* HAPI FHIR Library
* %%
* Copyright (C) 2014 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
public class NonPrettyPrintWriterWrapper implements XMLStreamWriter {
private static final String PRE = "pre";
private XMLStreamWriter myTarget;
private int myInsidePre = 0;
public NonPrettyPrintWriterWrapper(XMLStreamWriter target) {
myTarget = target;
}
@Override
public void flush() throws XMLStreamException {
myTarget.flush();
}
@Override
public void close() throws XMLStreamException {
myTarget.close();
}
@Override
public String getPrefix(String theUri) throws XMLStreamException {
return myTarget.getPrefix(theUri);
}
@Override
public void setPrefix(String thePrefix, String theUri) throws XMLStreamException {
myTarget.setPrefix(thePrefix, theUri);
}
@Override
public void setDefaultNamespace(String theUri) throws XMLStreamException {
myTarget.setDefaultNamespace(theUri);
}
@Override
public void setNamespaceContext(NamespaceContext theContext) throws XMLStreamException {
myTarget.setNamespaceContext(theContext);
}
@Override
public NamespaceContext getNamespaceContext() {
return myTarget.getNamespaceContext();
}
@Override
public void writeStartElement(String theLocalName) throws XMLStreamException {
if (PRE.equals(theLocalName) || myInsidePre > 0) {
myInsidePre++;
}
myTarget.writeStartElement(theLocalName);
}
@Override
public void writeStartElement(String theNamespaceURI, String theLocalName) throws XMLStreamException {
if (PRE.equals(theLocalName) || myInsidePre > 0) {
myInsidePre++;
}
myTarget.writeStartElement(theNamespaceURI, theLocalName);
}
@Override
public void writeStartElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException {
if (PRE.equals(theLocalName) || myInsidePre > 0) {
myInsidePre++;
}
myTarget.writeStartElement(thePrefix, theLocalName, theNamespaceURI);
}
@Override
public void writeEmptyElement(String theNamespaceURI, String theLocalName) throws XMLStreamException {
myTarget.writeEmptyElement(theNamespaceURI, theLocalName);
}
@Override
public void writeEmptyElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException {
myTarget.writeEmptyElement(thePrefix, theLocalName, theNamespaceURI);
}
@Override
public void writeEmptyElement(String theLocalName) throws XMLStreamException {
myTarget.writeEmptyElement(theLocalName);
}
@Override
public void writeEndElement() throws XMLStreamException {
if (myInsidePre > 0) {
myInsidePre--;
}
myTarget.writeEndElement();
}
@Override
public void writeEndDocument() throws XMLStreamException {
myTarget.writeEndDocument();
}
@Override
public void writeAttribute(String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(theLocalName, theValue);
}
@Override
public void writeAttribute(String thePrefix, String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(thePrefix, theNamespaceURI, theLocalName, theValue);
}
@Override
public void writeAttribute(String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(theNamespaceURI, theLocalName, theValue);
}
@Override
public void writeNamespace(String thePrefix, String theNamespaceURI) throws XMLStreamException {
myTarget.writeNamespace(thePrefix, theNamespaceURI);
}
@Override
public void writeDefaultNamespace(String theNamespaceURI) throws XMLStreamException {
myTarget.writeDefaultNamespace(theNamespaceURI);
}
@Override
public void writeComment(String theData) throws XMLStreamException {
myTarget.writeComment(theData);
}
@Override
public void writeProcessingInstruction(String theTarget) throws XMLStreamException {
myTarget.writeProcessingInstruction(theTarget);
}
@Override
public void writeProcessingInstruction(String theTarget, String theData) throws XMLStreamException {
myTarget.writeProcessingInstruction(theTarget, theData);
}
@Override
public void writeCData(String theData) throws XMLStreamException {
myTarget.writeCData(theData);
}
@Override
public void writeDTD(String theDtd) throws XMLStreamException {
myTarget.writeDTD(theDtd);
}
@Override
public void writeEntityRef(String theName) throws XMLStreamException {
myTarget.writeEntityRef(theName);
}
@Override
public void writeStartDocument() throws XMLStreamException {
myTarget.writeStartDocument();
}
@Override
public void writeStartDocument(String theVersion) throws XMLStreamException {
myTarget.writeStartDocument(theVersion);
}
@Override
public void writeStartDocument(String theEncoding, String theVersion) throws XMLStreamException {
myTarget.writeStartDocument(theEncoding, theVersion);
}
@Override
public void writeCharacters(String theText) throws XMLStreamException {
if (myInsidePre > 0) {
myTarget.writeCharacters(theText);
} else {
writeCharacters(theText.toCharArray(), 0, theText.length());
}
}
@Override
public void writeCharacters(char[] theText, int theStart, int theLen) throws XMLStreamException {
writeCharacters(theText, theStart, theLen, myTarget, myInsidePre);
}
static void writeCharacters(char[] theText, int theStart, int theLen, XMLStreamWriter target, int insidePre) throws XMLStreamException {
if (theLen == 0) {
return;
} else {
if (insidePre > 0) {
target.writeCharacters(theText, theStart, theLen);
} else {
int initialEnd = theStart + (theLen - 1);
int start = theStart;
int end = initialEnd;
while (Character.isWhitespace(theText[start]) && start < end) {
start++;
}
while (Character.isWhitespace(theText[end]) && end > start) {
end--;
}
if (start == end) {
if (Character.isWhitespace(theText[start])) {
target.writeCharacters(" ");
return;
}
}
if (start > theStart) {
target.writeCharacters(" ");
}
target.writeCharacters(theText, start, (end - start) + 1);
if (end < initialEnd) {
target.writeCharacters(" ");
}
}
}
}
@Override
public Object getProperty(String theName) throws IllegalArgumentException {
return myTarget.getProperty(theName);
}
}

View File

@ -31,39 +31,43 @@ import org.apache.commons.lang3.StringUtils;
public class PrettyPrintWriterWrapper implements XMLStreamWriter {
private XMLStreamWriter myTarget;
private static final String INDENT_CHAR = " ";
private static final String LINEFEED_CHAR = "\n";
private static final String PRE = "pre";
private int depth = 0;
private Map<Integer, Boolean> hasChildElement = new HashMap<Integer, Boolean>();
private static final String INDENT_CHAR = " ";
private static final String LINEFEED_CHAR = "\n";
private int myInsidePre = 0;
private XMLStreamWriter myTarget;
private boolean myFirstIndent=true;
public PrettyPrintWriterWrapper(XMLStreamWriter target) {
myTarget = target;
}
private String repeat(int d, String s) {
return StringUtils.repeat(s, d * 3);
}
@Override
public void flush() throws XMLStreamException {
myTarget.flush();
}
@Override
public void close() throws XMLStreamException {
myTarget.close();
}
@Override
public void flush() throws XMLStreamException {
myTarget.flush();
}
@Override
public NamespaceContext getNamespaceContext() {
return myTarget.getNamespaceContext();
}
@Override
public String getPrefix(String theUri) throws XMLStreamException {
return myTarget.getPrefix(theUri);
}
@Override
public void setPrefix(String thePrefix, String theUri) throws XMLStreamException {
myTarget.setPrefix(thePrefix, theUri);
public Object getProperty(String theName) throws IllegalArgumentException {
return myTarget.getProperty(theName);
}
@Override
@ -77,44 +81,63 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter {
}
@Override
public NamespaceContext getNamespaceContext() {
return myTarget.getNamespaceContext();
public void setPrefix(String thePrefix, String theUri) throws XMLStreamException {
myTarget.setPrefix(thePrefix, theUri);
}
@Override
public void writeStartElement(String theLocalName) throws XMLStreamException {
indentAndAdd();
myTarget.writeStartElement(theLocalName);
public void writeAttribute(String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(theLocalName, theValue);
}
private void indentAndAdd() throws XMLStreamException {
indent();
@Override
public void writeAttribute(String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(theNamespaceURI, theLocalName, theValue);
}
// update state of parent node
if (depth > 0) {
hasChildElement.put(depth - 1, true);
@Override
public void writeAttribute(String thePrefix, String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(thePrefix, theNamespaceURI, theLocalName, theValue);
}
@Override
public void writeCData(String theData) throws XMLStreamException {
myTarget.writeCData(theData);
}
@Override
public void writeCharacters(char[] theText, int theStart, int theLen) throws XMLStreamException {
NonPrettyPrintWriterWrapper.writeCharacters(theText, theStart, theLen, myTarget, myInsidePre);
}
@Override
public void writeCharacters(String theText) throws XMLStreamException {
if (myInsidePre > 0) {
myTarget.writeCharacters(theText);
} else {
writeCharacters(theText.toCharArray(), 0, theText.length());
}
// reset state of current node
hasChildElement.put(depth, false);
depth++;
}
private void indent() throws XMLStreamException {
myTarget.writeCharacters(LINEFEED_CHAR + repeat(depth, INDENT_CHAR));
}
@Override
public void writeStartElement(String theNamespaceURI, String theLocalName) throws XMLStreamException {
indentAndAdd();
myTarget.writeStartElement(theNamespaceURI, theLocalName);
public void writeComment(String theData) throws XMLStreamException {
myTarget.writeComment(theData);
}
@Override
public void writeStartElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException {
indentAndAdd();
myTarget.writeStartElement(thePrefix, theLocalName, theNamespaceURI);
public void writeDefaultNamespace(String theNamespaceURI) throws XMLStreamException {
myTarget.writeDefaultNamespace(theNamespaceURI);
}
@Override
public void writeDTD(String theDtd) throws XMLStreamException {
myTarget.writeDTD(theDtd);
}
@Override
public void writeEmptyElement(String theLocalName) throws XMLStreamException {
indent();
myTarget.writeEmptyElement(theLocalName);
}
@Override
@ -129,28 +152,6 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter {
myTarget.writeEmptyElement(thePrefix, theLocalName, theNamespaceURI);
}
@Override
public void writeEmptyElement(String theLocalName) throws XMLStreamException {
indent();
myTarget.writeEmptyElement(theLocalName);
}
@Override
public void writeEndElement() throws XMLStreamException {
decrementAndIndent();
myTarget.writeEndElement();
}
private void decrementAndIndent() throws XMLStreamException {
depth--;
if (hasChildElement.get(depth) == true) {
// indent for current depth
myTarget.writeCharacters(LINEFEED_CHAR + repeat(depth, INDENT_CHAR));
}
}
@Override
public void writeEndDocument() throws XMLStreamException {
decrementAndIndent();
@ -158,18 +159,19 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter {
}
@Override
public void writeAttribute(String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(theLocalName, theValue);
public void writeEndElement() throws XMLStreamException {
if (myInsidePre > 0) {
myInsidePre--;
}
decrementAndIndent();
myTarget.writeEndElement();
}
@Override
public void writeAttribute(String thePrefix, String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(thePrefix, theNamespaceURI, theLocalName, theValue);
}
@Override
public void writeAttribute(String theNamespaceURI, String theLocalName, String theValue) throws XMLStreamException {
myTarget.writeAttribute(theNamespaceURI, theLocalName, theValue);
public void writeEntityRef(String theName) throws XMLStreamException {
myTarget.writeEntityRef(theName);
}
@Override
@ -177,16 +179,6 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter {
myTarget.writeNamespace(thePrefix, theNamespaceURI);
}
@Override
public void writeDefaultNamespace(String theNamespaceURI) throws XMLStreamException {
myTarget.writeDefaultNamespace(theNamespaceURI);
}
@Override
public void writeComment(String theData) throws XMLStreamException {
myTarget.writeComment(theData);
}
@Override
public void writeProcessingInstruction(String theTarget) throws XMLStreamException {
myTarget.writeProcessingInstruction(theTarget);
@ -197,49 +189,90 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter {
myTarget.writeProcessingInstruction(theTarget, theData);
}
@Override
public void writeCData(String theData) throws XMLStreamException {
myTarget.writeCData(theData);
}
@Override
public void writeDTD(String theDtd) throws XMLStreamException {
myTarget.writeDTD(theDtd);
}
@Override
public void writeEntityRef(String theName) throws XMLStreamException {
myTarget.writeEntityRef(theName);
}
@Override
public void writeStartDocument() throws XMLStreamException {
myFirstIndent=true;
myTarget.writeStartDocument();
}
@Override
public void writeStartDocument(String theVersion) throws XMLStreamException {
myFirstIndent=true;
myTarget.writeStartDocument(theVersion);
}
@Override
public void writeStartDocument(String theEncoding, String theVersion) throws XMLStreamException {
myFirstIndent=true;
myTarget.writeStartDocument(theEncoding, theVersion);
}
@Override
public void writeCharacters(String theText) throws XMLStreamException {
myTarget.writeCharacters(theText);
public void writeStartElement(String theLocalName) throws XMLStreamException {
indentAndAdd();
myTarget.writeStartElement(theLocalName);
if (PRE.equals(theLocalName) || myInsidePre > 0) {
myInsidePre++;
}
}
@Override
public void writeCharacters(char[] theText, int theStart, int theLen) throws XMLStreamException {
myTarget.writeCharacters(theText, theStart, theLen);
public void writeStartElement(String theNamespaceURI, String theLocalName) throws XMLStreamException {
indentAndAdd();
myTarget.writeStartElement(theNamespaceURI, theLocalName);
if (PRE.equals(theLocalName) || myInsidePre > 0) {
myInsidePre++;
}
}
@Override
public Object getProperty(String theName) throws IllegalArgumentException {
return myTarget.getProperty(theName);
public void writeStartElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException {
indentAndAdd();
myTarget.writeStartElement(thePrefix, theLocalName, theNamespaceURI);
if (PRE.equals(theLocalName) || myInsidePre > 0) {
myInsidePre++;
}
}
private void decrementAndIndent() throws XMLStreamException {
if (myInsidePre > 0) {
return;
}
depth--;
if (hasChildElement.get(depth) == true) {
// indent for current depth
myTarget.writeCharacters(LINEFEED_CHAR + repeat(depth, INDENT_CHAR));
}
}
private void indent() throws XMLStreamException {
if (myFirstIndent) {
myFirstIndent = false;
return;
}
myTarget.writeCharacters(LINEFEED_CHAR + repeat(depth, INDENT_CHAR));
}
private void indentAndAdd() throws XMLStreamException {
if (myInsidePre > 0) {
return;
}
indent();
// update state of parent node
if (depth > 0) {
hasChildElement.put(depth - 1, true);
}
// reset state of current node
hasChildElement.put(depth, false);
depth++;
}
private String repeat(int d, String s) {
return StringUtils.repeat(s, d * 3);
}
}

View File

@ -759,13 +759,24 @@ public List<IResource> transaction(@TransactionParam List<IResource> theResource
}
}
ArrayList<IResource> retVal = new ArrayList<IResource>();
/*
* According to the specification, a bundle must be returned. This bundle will contain
* all of the created/updated/deleted resources, including their new/updated identities.
*
* Implementing this will depend on your specific application
* The returned list must be the exact same size as the list of resources
* passed in, and it is acceptable to return the same list instance that was
* passed in.
*/
List<IResource> retVal = theResources;
for (IResource next : theResources) {
/*
* Populate each returned resource with the new ID for that resource,
* including the new version if the server supports versioning.
*/
IdDt newId = new IdDt("Patient", "1", "2");
next.setId(newId);
}
return retVal;
}
//END SNIPPET: transaction

View File

@ -1,29 +0,0 @@
package ca.uhn.fhir.model.dstu.composite;
import static org.junit.Assert.*;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
public class ResourceReferenceDtTest {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceReferenceDtTest.class);
@Test
public void testParseValueAbsolute() {
}
@BeforeClass
public static void beforeClass() {
ourCtx = new FhirContext();
}
}

View File

@ -10,6 +10,8 @@ import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.sf.json.JSON;
@ -18,6 +20,7 @@ import net.sf.json.JSONSerializer;
import org.apache.commons.io.IOUtils;
import org.hamcrest.core.IsNot;
import org.hamcrest.core.StringContains;
import org.hamcrest.text.StringContainsInOrder;
import org.junit.BeforeClass;
import org.junit.Test;
@ -47,6 +50,7 @@ import ca.uhn.fhir.model.dstu.resource.ValueSet.DefineConcept;
import ca.uhn.fhir.model.dstu.valueset.AddressUseEnum;
import ca.uhn.fhir.model.dstu.valueset.NarrativeStatusEnum;
import ca.uhn.fhir.model.primitive.DecimalDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
@ -541,6 +545,38 @@ public class JsonParserTest {
}
@Test
public void testEncodeBundle() {
Bundle b= new Bundle();
Patient p1 = new Patient();
p1.addName().addFamily("Family1");
BundleEntry entry = b.addEntry();
entry.getId().setValue("1");
entry.setResource(p1);
Patient p2 = new Patient();
p2.addName().addFamily("Family2");
entry = b.addEntry();
entry.getId().setValue("2");
entry.setLinkAlternate(new StringDt("http://foo/bar"));
entry.setResource(p2);
BundleEntry deletedEntry = b.addEntry();
deletedEntry.setId(new IdDt("Patient/3"));
InstantDt nowDt = InstantDt.withCurrentTime();
deletedEntry.setDeleted(nowDt);
String bundleString = ourCtx.newJsonParser().setPrettyPrint(true).encodeBundleToString(b);
ourLog.info(bundleString);
List<String> strings = new ArrayList<String>();
strings.addAll(Arrays.asList("\"id\":\"1\""));
strings.addAll(Arrays.asList("\"id\":\"2\"", "\"rel\":\"alternate\"", "\"href\":\"http://foo/bar\""));
strings.addAll(Arrays.asList("\"deleted\":\""+nowDt.getValueAsString()+"\"", "\"id\":\"Patient/3\""));
assertThat(bundleString, StringContainsInOrder.stringContainsInOrder(strings));
}
@Test
public void testSimpleBundleEncode() throws IOException {

View File

@ -269,6 +269,7 @@ public class XmlParserTest {
p2.addName().addFamily("Family2");
entry = b.addEntry();
entry.getId().setValue("2");
entry.setLinkAlternate(new StringDt("http://foo/bar"));
entry.setResource(p2);
BundleEntry deletedEntry = b.addEntry();
@ -280,7 +281,7 @@ public class XmlParserTest {
List<String> strings = new ArrayList<String>();
strings.addAll(Arrays.asList("<entry>", "<id>1</id>", "</entry>"));
strings.addAll(Arrays.asList("<entry>", "<id>2</id>", "</entry>"));
strings.addAll(Arrays.asList("<entry>", "<id>2</id>", "<link rel=\"alternate\" href=\"http://foo/bar\"/>", "</entry>"));
strings.addAll(Arrays.asList("<at:deleted-entry", "ref=\"Patient/3", "/>"));
assertThat(bundleString, StringContainsInOrder.stringContainsInOrder(strings));
@ -327,24 +328,31 @@ public class XmlParserTest {
public void testEncodePrettyPrint() throws DataFormatException {
Patient patient = new Patient();
patient.getText().getDiv().setValueAsString("<div>\n <i> hello </i>\n</div>");
patient.getText().getDiv().setValueAsString("<div>\n <i> hello <pre>\n LINE1\n LINE2</pre></i>\n\n\n\n</div>");
patient.addName().addFamily("Family").addGiven("Given");
//@formatter:off
String encoded = new FhirContext().newXmlParser().setPrettyPrint(false).encodeResourceToString(patient);
ourLog.info(encoded);
String expected = "<Patient xmlns=\"http://hl7.org/fhir\"><text><div xmlns=\"http://www.w3.org/1999/xhtml\">\n" +
" <i> hello </i>\n" +
"</div></text><name><family value=\"Family\"/><given value=\"Given\"/></name></Patient>";
/*
* Note at least one space is placed where any whitespace was, as
* it is hard to tell what whitespace had no purpose
*/
String expected = "<Patient xmlns=\"http://hl7.org/fhir\"><text><div xmlns=\"http://www.w3.org/1999/xhtml\">"
+ " <i> hello "
+ "<pre>\n LINE1\n LINE2</pre>"
+ "</i> </div></text><name><family value=\"Family\"/><given value=\"Given\"/></name></Patient>";
assertEquals(expected, encoded);
encoded = new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(patient);
ourLog.info(encoded);
expected = "<Patient xmlns=\"http://hl7.org/fhir\">\n"
+ " <text>\n"
+ " <div xmlns=\"http://www.w3.org/1999/xhtml\">\n"
+ " <i> hello </i>\n"
+ "</div>\n"
+ " <div xmlns=\"http://www.w3.org/1999/xhtml\"> \n"
+ " <i> hello \n"
+ " <pre>\n LINE1\n LINE2</pre>\n"
+ " </i> \n"
+ " </div>\n"
+ " </text>\n"
+ " <name>\n"
+ " <family value=\"Family\"/>\n"
@ -645,6 +653,7 @@ public class XmlParserTest {
assertThat(str, StringContains.containsString("<Patient xmlns=\"http://hl7.org/fhir\">"));
}
@SuppressWarnings("deprecation")
@Test
public void testParseBundle() {
@ -664,6 +673,7 @@ public class XmlParserTest {
" <title>Valueset &quot;256a5231-a2bb-49bd-9fea-f349d428b70d&quot; to support automated processing</title>\n" +
" <id>http://hl7.org/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d</id>\n" +
" <link href=\"http://hl7.org/implement/standards/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d\" rel=\"self\"/>\n" +
" <link href=\"http://hl7.org/foo\" rel=\"alternate\"/>\n" +
" <updated>2014-02-10T04:10:46.987-00:00</updated>\n" +
" <author>\n" +
" <name>HL7, Inc (FHIR Project)</name>\n" +
@ -718,6 +728,7 @@ public class XmlParserTest {
BundleEntry entry = bundle.getEntries().get(0);
assertEquals("HL7, Inc (FHIR Project)", entry.getAuthorName().getValue());
assertEquals("http://hl7.org/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d", entry.getId().getValue());
assertEquals("http://hl7.org/foo", entry.getLinkAlternate().getValue());
assertEquals(1, entry.getCategories().size());
assertEquals("term", entry.getCategories().get(0).getTerm());
assertEquals("label", entry.getCategories().get(0).getLabel());
@ -744,6 +755,7 @@ public class XmlParserTest {
}
@SuppressWarnings("deprecation")
@Test
public void testParseBundleDeletedEntry() {

View File

@ -1,8 +1,7 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.StringReader;
import java.nio.charset.Charset;
@ -29,7 +28,6 @@ import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.Constants;
public class ReferenceClientTest {

View File

@ -1,32 +0,0 @@
package ca.uhn.fhir.rest.client;
import java.io.IOException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
public class Tester {
public static final void main(String[] args) throws DataFormatException, IOException {
try {
FhirContext ctx = new FhirContext(Patient.class);
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://spark.furore.com/fhir/");
Patient patient = client.getPatientById(new IdDt("1"));
System.out.println(ctx.newXmlParser().encodeResourceToString(patient));
// Patient patient2 = client.findPatientByMrn(new IdentifierDt("http://orionhealth.com/mrn", "PRP1660"));
// System.out.println(ctx.newXmlParser().encodeResourceToString(patient2));
} catch (NonFhirResponseException e) {
e.printStackTrace();
System.out.println(e.getResponseBody());
}
}
}

View File

@ -507,30 +507,7 @@ public class ResfulServerMethodTest {
}
@Test
public void testPrettyPrint() throws Exception {
// HttpPost httpPost = new HttpPost("http://localhost:" + ourPort +
// "/Patient/1");
// httpPost.setEntity(new StringEntity("test",
// ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_pretty=false");
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_pretty=true");
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, IsNot.not(StringContains.containsString("<identifier><use")));
}
@Test
public void testReadOnTypeThatDoesntSupportRead() throws Exception {

View File

@ -0,0 +1,179 @@
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.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.ServletHolder;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class ServerBaseTest {
private static CloseableHttpClient ourClient;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerBaseTest.class);
private Server myServer;
private static FhirContext ourCtx = new FhirContext();
@Test
public void testTransaction() throws Exception {
}
@After
public void after() throws Exception {
if (myServer != null) {
myServer.stop();
}
}
@Test
public void testWithContextPath() throws Exception {
int port = RandomServerPortProvider.findFreePort();
myServer = new Server(port);
DummyProvider patientProvider = new DummyProvider();
RestfulServer server = new RestfulServer(ourCtx);
server.setProviders(patientProvider);
org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler();
proxyHandler.setContextPath("/ctx");
ServletHolder handler = new ServletHolder();
handler.setServlet(server);
proxyHandler.addServlet(handler, "/*");
myServer.setHandler(proxyHandler);
myServer.start();
// test
HttpGet httpPost = new HttpGet("http://localhost:" + port + "/ctx/Patient?_pretty=true");
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
BundleEntry entry = bundle.getEntries().get(0);
assertEquals("http://localhost:" + port + "/ctx/Patient/123", entry.getId().getValue());
assertEquals("http://localhost:" + port + "/ctx/Patient/123/_history/456", entry.getLinkSelf().getValue());
}
@Test
public void testWithContextAndServletPath() throws Exception {
int port = RandomServerPortProvider.findFreePort();
myServer = new Server(port);
DummyProvider patientProvider = new DummyProvider();
RestfulServer server = new RestfulServer(ourCtx);
server.setProviders(patientProvider);
org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler();
proxyHandler.setContextPath("/ctx");
ServletHolder handler = new ServletHolder();
handler.setServlet(server);
proxyHandler.addServlet(handler, "/servlet/*");
myServer.setHandler(proxyHandler);
myServer.start();
// test
HttpGet httpPost = new HttpGet("http://localhost:" + port + "/ctx/servlet/Patient?_pretty=true");
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
BundleEntry entry = bundle.getEntries().get(0);
assertEquals("http://localhost:" + port + "/ctx/servlet/Patient/123", entry.getId().getValue());
assertEquals("http://localhost:" + port + "/ctx/servlet/Patient/123/_history/456", entry.getLinkSelf().getValue());
}
@Test
public void testWithNoPath() throws Exception {
int port = RandomServerPortProvider.findFreePort();
myServer = new Server(port);
DummyProvider patientProvider = new DummyProvider();
RestfulServer server = new RestfulServer(ourCtx);
server.setProviders(patientProvider);
org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler();
proxyHandler.setContextPath("/");
ServletHolder handler = new ServletHolder();
handler.setServlet(server);
proxyHandler.addServlet(handler, "/*");
myServer.setHandler(proxyHandler);
myServer.start();
// test
HttpGet httpPost = new HttpGet("http://localhost:" + port + "/Patient?_pretty=true");
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent);
BundleEntry entry = bundle.getEntries().get(0);
assertEquals("http://localhost:" + port + "/Patient/123", entry.getId().getValue());
assertEquals("http://localhost:" + port + "/Patient/123/_history/456", entry.getLinkSelf().getValue());
}
@BeforeClass
public static void beforeClass() {
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 DummyProvider implements IResourceProvider {
@Search
public List<Patient> search() {
Patient p = new Patient();
p.setId(new IdDt("Patient", "123", "456"));
p.addIdentifier("urn:system", "12345");
return Collections.singletonList(p);
}
@Override
public Class<? extends IResource> getResourceType() {
return Patient.class;
}
}
}

View File

@ -0,0 +1,240 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
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.hamcrest.core.IsNot;
import org.hamcrest.core.StringContains;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class ServerFeaturesTest {
private static CloseableHttpClient ourClient;
private static int ourPort;
private static Server ourServer;
@Test
public void testPrettyPrint() throws Exception {
/*
* Not specified
*/
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
/*
* Disabled
*/
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_pretty=false");
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
/*
* Enabled
*/
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_pretty=true");
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, IsNot.not(StringContains.containsString("<identifier><use")));
}
@Test
public void testAcceptHeader() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_FHIR_XML);
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_ATOM_XML);
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_FHIR_JSON);
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("\"identifier\":"));
}
@Test
public void testAcceptHeaderWithMultiple() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", "text/plain, " + Constants.CT_FHIR_XML);
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", "text/plain, " + Constants.CT_ATOM_XML);
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", "text/plain, " + Constants.CT_FHIR_JSON);
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("\"identifier\":"));
}
@Test
public void testAcceptHeaderNonFhirTypes() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_XML);
CloseableHttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier><use"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_JSON);
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("\"identifier\":"));
}
@Test
public void testAcceptHeaderWithPrettyPrint() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_FHIR_XML+ "; pretty=true");
CloseableHttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("<identifier>\n "));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
httpGet.addHeader("Accept", Constants.CT_FHIR_JSON+ "; pretty=true");
status = ourClient.execute(httpGet);
responseContent = IOUtils.toString(status.getEntity().getContent());
assertThat(responseContent, StringContains.containsString("\",\n"));
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
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 DummyPatientResourceProvider implements IResourceProvider {
public Map<String, Patient> getIdToPatient() {
Map<String, Patient> idToPatient = new HashMap<String, Patient>();
{
Patient patient = createPatient1();
idToPatient.put("1", patient);
}
{
Patient patient = new Patient();
patient.getIdentifier().add(new IdentifierDt());
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
patient.getIdentifier().get(0).setValue("00002");
patient.getName().add(new HumanNameDt());
patient.getName().get(0).addFamily("Test");
patient.getName().get(0).addGiven("PatientTwo");
patient.getGender().setText("F");
patient.getId().setValue("2");
idToPatient.put("2", patient);
}
return idToPatient;
}
/**
* Retrieve the resource by its identifier
*
* @param theId
* The resource identity
* @return The resource
*/
@Read()
public Patient getResourceById(@IdParam IdDt theId) {
return getIdToPatient().get(theId.getValue());
}
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
private Patient createPatient1() {
Patient patient = new Patient();
patient.addIdentifier();
patient.getIdentifier().get(0).setUse(IdentifierUseEnum.OFFICIAL);
patient.getIdentifier().get(0).setSystem(new UriDt("urn:hapitest:mrns"));
patient.getIdentifier().get(0).setValue("00001");
patient.addName();
patient.getName().get(0).addFamily("Test");
patient.getName().get(0).addGiven("PatientOne");
patient.getGender().setText("M");
patient.getId().setValue("1");
return patient;
}
}
}

View File

@ -14,7 +14,6 @@ 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.BeforeClass;
@ -24,6 +23,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
@ -45,6 +45,7 @@ public class TransactionTest {
@Test
public void testTransaction() throws Exception {
Bundle b = new Bundle();
InstantDt nowInstant = InstantDt.withCurrentTime();
Patient p1 = new Patient();
p1.addName().addFamily("Family1");
@ -60,20 +61,38 @@ public class TransactionTest {
BundleEntry deletedEntry = b.addEntry();
deletedEntry.setId(new IdDt("Patient/3"));
deletedEntry.setDeleted(InstantDt.withCurrentTime());
deletedEntry.setDeleted(nowInstant);
String bundleString = ourCtx.newXmlParser().setPrettyPrint(true).encodeBundleToString(b);
ourLog.info(bundleString);
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/");
httpPost.addHeader("Accept", Constants.CT_ATOM_XML + "; pretty=true");
httpPost.setEntity(new StringEntity(bundleString, ContentType.create(Constants.CT_ATOM_XML, "UTF-8")));
HttpResponse status = ourClient.execute(httpPost);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
ourLog.info(responseContent);
Bundle bundle = new FhirContext().newXmlParser().parseBundle(responseContent);
assertEquals(3, bundle.size());
}
BundleEntry entry0 = bundle.getEntries().get(0);
assertEquals("http://localhost:" + ourPort + "/Patient/81", entry0.getId().getValue());
assertEquals("http://localhost:" + ourPort + "/Patient/81/_history/91", entry0.getLinkSelf().getValue());
assertEquals("http://localhost:" + ourPort + "/Patient/1", entry0.getLinkAlternate().getValue());
BundleEntry entry1 = bundle.getEntries().get(1);
assertEquals("http://localhost:" + ourPort + "/Patient/82", entry1.getId().getValue());
assertEquals("http://localhost:" + ourPort + "/Patient/82/_history/92", entry1.getLinkSelf().getValue());
assertEquals("http://localhost:" + ourPort + "/Patient/2", entry1.getLinkAlternate().getValue());
BundleEntry entry2 = bundle.getEntries().get(2);
assertEquals("http://localhost:" + ourPort + "/Patient/3", entry2.getId().getValue());
assertEquals("http://localhost:" + ourPort + "/Patient/3/_history/93", entry2.getLinkSelf().getValue());
assertEquals(nowInstant.getValueAsString(), entry2.getDeletedAt().getValueAsString());
}
@AfterClass
public static void afterClass() throws Exception {
@ -86,15 +105,19 @@ public class TransactionTest {
ourServer = new Server(ourPort);
DummyProvider patientProvider = new DummyProvider();
RestfulServer server = new RestfulServer();
server.setProviders(patientProvider);
org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler();
proxyHandler.setContextPath("/");
ServletHolder handler = new ServletHolder();
handler.setServlet(server);
proxyHandler.addServlet(handler, "/*");
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet.setProviders(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);
@ -109,6 +132,15 @@ public class TransactionTest {
@Transaction
public List<IResource> transaction(@TransactionParam List<IResource> theResources) {
int index=1;
for (IResource next : theResources) {
String newId = "8"+Integer.toString(index);
if (next.getResourceMetadata().containsKey(ResourceMetadataKeyEnum.DELETED_AT)) {
newId = next.getId().getUnqualifiedId();
}
next.setId(new IdDt("Patient", newId, "9"+Integer.toString(index)));
index++;
}
return theResources;
}

View File

@ -43,6 +43,7 @@ import ca.uhn.fhir.model.dstu.composite.QuantityDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum;
import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -65,18 +66,19 @@ public abstract class BaseFhirDao {
myContext = theContext;
}
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory) {
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory) {
if (entity.getPublished() == null) {
entity.setPublished(new Date());
}
if (theUpdateHistory) {
final ResourceHistoryTable historyEntry = entity.toHistory(getContext());
myEntityManager.persist(historyEntry);
}
entity.setVersion(entity.getVersion()+1);
entity.setVersion(entity.getVersion() + 1);
theResource.setId(new IdDt(entity.getResourceType(), entity.getId().toString(), Long.toString(entity.getVersion())));
final List<ResourceIndexedSearchParamString> stringParams = extractSearchParamStrings(entity, theResource);
final List<ResourceIndexedSearchParamToken> tokenParams = extractSearchParamTokens(entity, theResource);
final List<ResourceIndexedSearchParamNumber> numberParams = extractSearchParamNumber(entity, theResource);
@ -86,9 +88,9 @@ public abstract class BaseFhirDao {
populateResourceIntoEntity(theResource, entity);
entity.setUpdated(new Date());
if (entity.getId() == null) {
myEntityManager.persist(entity);
myEntityManager.persist(entity);
} else {
entity = myEntityManager.merge(entity);
}
@ -290,8 +292,7 @@ public abstract class BaseFhirDao {
if (nextObject instanceof QuantityDt) {
QuantityDt nextValue = (QuantityDt) nextObject;
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue().getValue(), nextValue.getSystem().getValueAsString(),
nextValue.getUnits().getValue());
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue().getValue(), nextValue.getSystem().getValueAsString(), nextValue.getUnits().getValue());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
} else {
@ -374,8 +375,7 @@ public abstract class BaseFhirDao {
} else if (nextObject instanceof ContactDt) {
ContactDt nextContact = (ContactDt) nextObject;
if (nextContact.getValue().isEmpty() == false) {
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextContact.getValue().getValueAsString()), nextContact
.getValue().getValueAsString());
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextContact.getValue().getValueAsString()), nextContact.getValue().getValueAsString());
nextEntity.setResource(theEntity);
retVal.add(nextEntity);
}

View File

@ -1,7 +1,5 @@
package ca.uhn.fhir.jpa.dao;
import static org.apache.commons.lang3.StringUtils.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -15,11 +13,6 @@ import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.entity.ResourceLink;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
@ -36,7 +29,7 @@ public class FhirSystemDao extends BaseFhirDao implements IFhirSystemDao {
@Transactional(propagation = Propagation.REQUIRED)
@Override
public List<IResource> transaction(List<IResource> theResources) {
public void transaction(List<IResource> theResources) {
ourLog.info("Beginning transaction with {} resources", theResources.size());
FhirTerser terser = myContext.newTerser();
@ -95,7 +88,6 @@ public class FhirSystemDao extends BaseFhirDao implements IFhirSystemDao {
updateEntity(resource, table, table.getId() != null);
}
return null;
}
}

View File

@ -6,6 +6,6 @@ import ca.uhn.fhir.model.api.IResource;
public interface IFhirSystemDao {
List<IResource> transaction(List<IResource> theResources);
void transaction(List<IResource> theResources);
}

View File

@ -1,9 +1,12 @@
package ca.uhn.fhir.jpa.dao;
package ca.uhn.fhir.jpa.provider;
import java.util.HashMap;
import java.util.List;
import org.springframework.beans.factory.annotation.Required;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt;
@ -50,6 +53,7 @@ public abstract class BaseJpaResourceProvider<T extends IResource> implements IR
return myDao.search(new HashMap<String, IQueryParameterType>());
}
@Required
public void setDao(IFhirResourceDao<T> theDao) {
myDao = theDao;
}

View File

@ -0,0 +1,27 @@
package ca.uhn.fhir.jpa.provider;
import java.util.List;
import org.springframework.beans.factory.annotation.Required;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.rest.annotation.Transaction;
import ca.uhn.fhir.rest.annotation.TransactionParam;
public class SystemProvider {
private IFhirSystemDao myDao;
@Required
public void setDao(IFhirSystemDao theDao) {
myDao = theDao;
}
@Transaction
public List<IResource> transaction(@TransactionParam List<IResource> theResources) {
myDao.transaction(theResources);
return theResources;
}
}

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.dao;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.Arrays;
@ -46,20 +47,51 @@ public class FhirSystemDaoTest {
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO01");
obs.setSubject(new ResourceReferenceDt("Patient/testPersistWithSimpleLinkP01"));
List<IResource> response = ourSystemDao.transaction(Arrays.asList((IResource)patient, obs));
ourSystemDao.transaction(Arrays.asList((IResource) patient, obs));
long patientId = Long.parseLong(patient.getId().getUnqualifiedId());
long patientVersion = Long.parseLong(patient.getId().getUnqualifiedVersionId());
long obsId = Long.parseLong(obs.getId().getUnqualifiedId());
long obsVersion = Long.parseLong(obs.getId().getUnqualifiedVersionId());
assertThat(patientId, greaterThan(0L));
assertEquals(patientVersion, 1L);
assertThat(obsId, greaterThan(patientId));
assertEquals(obsVersion, 1L);
// Try to search
List<Observation> obsResults = ourObservationDao.search(Observation.SP_NAME, new IdentifierDt("urn:system","testPersistWithSimpleLinkO01"));
List<Observation> obsResults = ourObservationDao.search(Observation.SP_NAME, new IdentifierDt("urn:system", "testPersistWithSimpleLinkO01"));
assertEquals(1, obsResults.size());
List<Patient> patResults = ourPatientDao.search(Patient.SP_IDENTIFIER, new IdentifierDt("urn:system","testPersistWithSimpleLinkP01"));
List<Patient> patResults = ourPatientDao.search(Patient.SP_IDENTIFIER, new IdentifierDt("urn:system", "testPersistWithSimpleLinkP01"));
assertEquals(1, obsResults.size());
IdDt patientId = patResults.get(0).getId();
IdDt foundPatientId = patResults.get(0).getId();
ResourceReferenceDt subject = obs.getSubject();
assertEquals(patientId.getUnqualifiedId(), subject.getResourceId().getUnqualifiedId());
assertEquals(foundPatientId.getUnqualifiedId(), subject.getResourceId().getUnqualifiedId());
// Update
patient = patResults.get(0);
obs = obsResults.get(0);
patient.addIdentifier("urn:system", "testPersistWithSimpleLinkP02");
obs.getName().addCoding().setSystem("urn:system").setCode("testPersistWithSimpleLinkO02");
ourSystemDao.transaction(Arrays.asList((IResource) patient, obs));
long patientId2 = Long.parseLong(patient.getId().getUnqualifiedId());
long patientVersion2 = Long.parseLong(patient.getId().getUnqualifiedVersionId());
long obsId2 = Long.parseLong(obs.getId().getUnqualifiedId());
long obsVersion2 = Long.parseLong(obs.getId().getUnqualifiedVersionId());
assertEquals(patientId, patientId2);
assertEquals(patientVersion2, 2L);
assertEquals(obsId, obsId2);
assertEquals(obsVersion2, 2L);
}
@AfterClass
public static void afterClass() {
ourCtx.close();

View File

@ -1,6 +1,6 @@
package ca.uhn.fhir.jpa.test;
import ca.uhn.fhir.jpa.dao.BaseJpaResourceProvider;
import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Questionnaire;

View File

@ -5,7 +5,7 @@ import java.util.*;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.jpa.dao.BaseJpaResourceProvider;
import ca.uhn.fhir.jpa.provider.BaseJpaResourceProvider;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.api.annotation.*;