Merge branch 'master' of github.com:jamesagnew/hapi-fhir

This commit is contained in:
James Agnew 2015-09-24 10:01:03 -04:00
commit 3c6299a157
93 changed files with 2992 additions and 928 deletions

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -18,22 +18,22 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId> <artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId> <artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -18,7 +18,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<exclusions> <exclusions>
<!-- <!--
<exclusion> <exclusion>
@ -39,13 +39,13 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId> <artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -34,7 +34,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>woodstox-core-asl</artifactId> <artifactId>woodstox-core-asl</artifactId>
@ -49,7 +49,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId> <artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>woodstox-core-asl</artifactId> <artifactId>woodstox-core-asl</artifactId>
@ -60,7 +60,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>woodstox-core-asl</artifactId> <artifactId>woodstox-core-asl</artifactId>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -40,7 +40,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>woodstox-core-asl</artifactId> <artifactId>woodstox-core-asl</artifactId>
@ -55,7 +55,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId> <artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>woodstox-core-asl</artifactId> <artifactId>woodstox-core-asl</artifactId>
@ -66,7 +66,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>woodstox-core-asl</artifactId> <artifactId>woodstox-core-asl</artifactId>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.model.api; package ca.uhn.fhir.model.api;
import net.sourceforge.cobertura.CoverageIgnore;
/* /*
* #%L * #%L
* HAPI FHIR - Core Library * HAPI FHIR - Core Library
@ -31,6 +33,7 @@ package ca.uhn.fhir.model.api;
* *
* @deprecated {@link Include} should be used instead * @deprecated {@link Include} should be used instead
*/ */
@CoverageIgnore
@Deprecated @Deprecated
public class PathSpecification extends Include { public class PathSpecification extends Include {

View File

@ -1014,7 +1014,7 @@ public class JsonParser extends BaseParser implements IParser {
if ("resourceType".equals(nextName)) { if ("resourceType".equals(nextName)) {
continue; continue;
} else if ("entry".equals(nextName)) { } else if ("entry".equals(nextName)) {
JsonArray entries = theObject.getJsonArray(nextName); JsonArray entries = grabJsonArray(theObject, nextName, "entry");
for (JsonValue jsonValue : entries) { for (JsonValue jsonValue : entries) {
theState.enteringNewElement(null, "entry"); theState.enteringNewElement(null, "entry");
parseBundleChildren((JsonObject) jsonValue, theState); parseBundleChildren((JsonObject) jsonValue, theState);
@ -1023,7 +1023,7 @@ public class JsonParser extends BaseParser implements IParser {
continue; continue;
} else if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) { } else if (myContext.getVersion().getVersion() == FhirVersionEnum.DSTU1) {
if ("link".equals(nextName)) { if ("link".equals(nextName)) {
JsonArray entries = theObject.getJsonArray(nextName); JsonArray entries = grabJsonArray(theObject, nextName, "link");
for (JsonValue jsonValue : entries) { for (JsonValue jsonValue : entries) {
theState.enteringNewElement(null, "link"); theState.enteringNewElement(null, "link");
JsonObject linkObj = (JsonObject) jsonValue; JsonObject linkObj = (JsonObject) jsonValue;
@ -1042,7 +1042,7 @@ public class JsonParser extends BaseParser implements IParser {
} }
} else { } else {
if ("link".equals(nextName)) { if ("link".equals(nextName)) {
JsonArray entries = theObject.getJsonArray(nextName); JsonArray entries = grabJsonArray(theObject, nextName, "link");
for (JsonValue jsonValue : entries) { for (JsonValue jsonValue : entries) {
theState.enteringNewElement(null, "link"); theState.enteringNewElement(null, "link");
JsonObject linkObj = (JsonObject) jsonValue; JsonObject linkObj = (JsonObject) jsonValue;
@ -1106,11 +1106,11 @@ public class JsonParser extends BaseParser implements IParser {
} }
continue; continue;
} else if ("extension".equals(nextName)) { } else if ("extension".equals(nextName)) {
JsonArray array = theObject.getJsonArray(nextName); JsonArray array = grabJsonArray(theObject, nextName, "extension");
parseExtension(theState, array, false); parseExtension(theState, array, false);
continue; continue;
} else if ("modifierExtension".equals(nextName)) { } else if ("modifierExtension".equals(nextName)) {
JsonArray array = theObject.getJsonArray(nextName); JsonArray array = grabJsonArray(theObject, nextName, "modifierExtension");
parseExtension(theState, array, true); parseExtension(theState, array, true);
continue; continue;
} else if (nextName.charAt(0) == '_') { } else if (nextName.charAt(0) == '_') {
@ -1135,6 +1135,17 @@ public class JsonParser extends BaseParser implements IParser {
} }
} }
private JsonArray grabJsonArray(JsonObject theObject, String nextName, String thePosition) {
JsonValue object = theObject.get(nextName);
if (object == null) {
return null;
}
if (object.getValueType() != ValueType.ARRAY) {
throw new DataFormatException("Syntax error parsing JSON FHIR structure: Expected ARRAY at element '" + thePosition + "', found '" + object.getValueType().name() + "'");
}
return (JsonArray) object;
}
private void parseChildren(ParserState<?> theState, String theName, JsonValue theJsonVal, JsonValue theAlternateVal, String theAlternateName) { private void parseChildren(ParserState<?> theState, String theName, JsonValue theJsonVal, JsonValue theAlternateVal, String theAlternateName) {
switch (theJsonVal.getValueType()) { switch (theJsonVal.getValueType()) {
case ARRAY: { case ARRAY: {

View File

@ -24,8 +24,11 @@ import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import org.apache.commons.lang3.Validate;
import ca.uhn.fhir.model.api.IQueryParameterOr; import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
@ -35,8 +38,8 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DateParam extends DateTimeDt implements IQueryParameterType, IQueryParameterOr<DateParam> { public class DateParam extends DateTimeDt implements IQueryParameterType, IQueryParameterOr<DateParam> {
private QuantityCompararatorEnum myComparator;
private BaseParam myBase=new BaseParam.ComposableBaseParam(); private BaseParam myBase=new BaseParam.ComposableBaseParam();
private QuantityCompararatorEnum myComparator;
/** /**
* Constructor * Constructor
@ -55,17 +58,26 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
/** /**
* Constructor * Constructor
*/ */
public DateParam(QuantityCompararatorEnum theComparator, String theDate) { public DateParam(QuantityCompararatorEnum theComparator, DateTimeDt theDate) {
myComparator = theComparator; myComparator = theComparator;
setValueAsString(theDate); setValueAsString(theDate != null ? theDate.getValueAsString() : null);
} }
/** /**
* Constructor * Constructor
*/ */
public DateParam(QuantityCompararatorEnum theComparator, DateTimeDt theDate) { public DateParam(QuantityCompararatorEnum theComparator, long theDate) {
Validate.inclusiveBetween(1, Long.MAX_VALUE, theDate, "theDate must not be 0 or negative");
myComparator = theComparator; myComparator = theComparator;
setValueAsString(theDate != null ? theDate.getValueAsString() : null); setValue(new Date(theDate));
}
/**
* Constructor
*/
public DateParam(QuantityCompararatorEnum theComparator, String theDate) {
myComparator = theComparator;
setValueAsString(theDate);
} }
/** /**
@ -85,6 +97,11 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
return myComparator; return myComparator;
} }
@Override
public Boolean getMissing() {
return myBase.getMissing();
}
@Override @Override
public String getQueryParameterQualifier() { public String getQueryParameterQualifier() {
if (myBase.getMissing()!=null) { if (myBase.getMissing()!=null) {
@ -93,6 +110,14 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
return null; return null;
} }
public DateTimeDt getValueAsDateTimeDt() {
return new DateTimeDt(getValueAsString());
}
public InstantDt getValueAsInstantDt() {
return new InstantDt(getValue());
}
@Override @Override
public String getValueAsQueryToken() { public String getValueAsQueryToken() {
if (myBase.getMissing()!=null) { if (myBase.getMissing()!=null) {
@ -125,6 +150,17 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
myComparator = theComparator; myComparator = theComparator;
} }
@Override
public void setMissing(Boolean theMissing) {
myBase.setMissing(theMissing);
}
@Override
public DateParam setValue(Date theValue) {
super.setValue(theValue, TemporalPrecisionEnum.MILLI);
return this;
}
@Override @Override
public void setValueAsQueryToken(String theQualifier, String theValue) { public void setValueAsQueryToken(String theQualifier, String theValue) {
myBase.setValueAsQueryToken(theQualifier, theValue); myBase.setValueAsQueryToken(theQualifier, theValue);
@ -191,22 +227,4 @@ public class DateParam extends DateTimeDt implements IQueryParameterType, IQuery
return b.toString(); return b.toString();
} }
public InstantDt getValueAsInstantDt() {
return new InstantDt(getValue());
}
public DateTimeDt getValueAsDateTimeDt() {
return new DateTimeDt(getValueAsString());
}
@Override
public Boolean getMissing() {
return myBase.getMissing();
}
@Override
public void setMissing(Boolean theMissing) {
myBase.setMissing(theMissing);
}
} }

View File

@ -214,8 +214,16 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
* "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null. * "2011-02-22" or "2011-02-22T13:12:00Z". Will be treated inclusively. Either theLowerBound or theUpperBound may both be populated, or one may be null, but it is not valid for both to be null.
*/ */
public void setRangeFromDatesInclusive(DateTimeDt theLowerBound, DateTimeDt theUpperBound) { public void setRangeFromDatesInclusive(DateTimeDt theLowerBound, DateTimeDt theUpperBound) {
myLowerBound = theLowerBound != null ? new DateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, theLowerBound) : null; if (theLowerBound instanceof DateParam) {
myUpperBound = theUpperBound != null ? new DateParam(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, theUpperBound) : null; myLowerBound = (DateParam) theLowerBound;
} else {
myLowerBound = theLowerBound != null ? new DateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, theLowerBound) : null;
}
if (theUpperBound instanceof DateParam) {
myUpperBound = (DateParam) theUpperBound;
} else {
myUpperBound = theUpperBound != null ? new DateParam(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, theUpperBound) : null;
}
validateAndThrowDataFormatExceptionIfInvalid(); validateAndThrowDataFormatExceptionIfInvalid();
} }
@ -304,8 +312,13 @@ public class DateRangeParam implements IQueryParameterAnd<DateParam> {
boolean haveLowerBound = haveLowerBound(); boolean haveLowerBound = haveLowerBound();
boolean haveUpperBound = haveUpperBound(); boolean haveUpperBound = haveUpperBound();
if (haveLowerBound && haveUpperBound) { if (haveLowerBound && haveUpperBound) {
if (myLowerBound.getValue().after(myUpperBound.getValue())) { if (myLowerBound.getValue().getTime() > myUpperBound.getValue().getTime()) {
throw new DataFormatException("Lower bound of " + myLowerBound.getValueAsString() + " is after upper bound of " + myUpperBound.getValueAsString()); StringBuilder b = new StringBuilder();
b.append("Lower bound of ");
b.append(myLowerBound.getValueAsString());
b.append(" is after upper bound of ");
b.append(myUpperBound.getValueAsString());
throw new DataFormatException(b.toString());
} }
} }

View File

@ -23,11 +23,13 @@ package ca.uhn.fhir.rest.param;
import java.util.Date; import java.util.Date;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum; import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import net.sourceforge.cobertura.CoverageIgnore;
/** /**
* @deprecated Use {@link DateParam} instead (this class is identical, but was renamed to be less confusing) * @deprecated Use {@link DateParam} instead (this class is identical, but was renamed to be less confusing)
*/ */
@Deprecated @Deprecated
@CoverageIgnore
public class QualifiedDateParam extends DateParam { public class QualifiedDateParam extends DateParam {
/** /**
* Constructor * Constructor

View File

@ -46,6 +46,7 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.BaseMethodBinding; import ca.uhn.fhir.rest.method.BaseMethodBinding;
import ca.uhn.fhir.rest.method.IParameter; import ca.uhn.fhir.rest.method.IParameter;
import ca.uhn.fhir.rest.method.MethodUtil; import ca.uhn.fhir.rest.method.MethodUtil;
@ -145,12 +146,15 @@ public class ResourceParameter implements IParameter {
return charset; return charset;
} }
public static IBaseResource loadResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<? extends IBaseResource> theResourceType) { @SuppressWarnings("unchecked")
public static <T extends IBaseResource> T loadResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<T> theResourceType) {
FhirContext ctx = theRequest.getServer().getFhirContext(); FhirContext ctx = theRequest.getServer().getFhirContext();
final Charset charset = determineRequestCharset(theRequest); final Charset charset = determineRequestCharset(theRequest);
Reader requestReader = createRequestReader(theRequest.getRawRequest(), charset); Reader requestReader = createRequestReader(theRequest.getRawRequest(), charset);
RestOperationTypeEnum restOperationType = theMethodBinding != null ? theMethodBinding.getRestOperationType() : null;
EncodingEnum encoding = RestfulServerUtils.determineRequestEncodingNoDefault(theRequest); EncodingEnum encoding = RestfulServerUtils.determineRequestEncodingNoDefault(theRequest);
if (encoding == null) { if (encoding == null) {
String ctValue = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE); String ctValue = theRequest.getServletRequest().getHeader(Constants.HEADER_CONTENT_TYPE);
@ -173,24 +177,24 @@ public class ResourceParameter implements IParameter {
} }
encoding = MethodUtil.detectEncodingNoDefault(body); encoding = MethodUtil.detectEncodingNoDefault(body);
if (encoding == null) { if (encoding == null) {
String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "noContentTypeInRequest", theMethodBinding.getRestOperationType()); String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "noContentTypeInRequest", restOperationType);
throw new InvalidRequestException(msg); throw new InvalidRequestException(msg);
} else { } else {
requestReader = new InputStreamReader(new ByteArrayInputStream(theRequest.getRawRequest()), charset); requestReader = new InputStreamReader(new ByteArrayInputStream(theRequest.getRawRequest()), charset);
} }
} else { } else {
String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, theMethodBinding.getRestOperationType()); String msg = ctx.getLocalizer().getMessage(ResourceParameter.class, "invalidContentTypeInRequest", ctValue, restOperationType);
throw new InvalidRequestException(msg); throw new InvalidRequestException(msg);
} }
} }
IParser parser = encoding.newParser(ctx); IParser parser = encoding.newParser(ctx);
IBaseResource retVal; T retVal;
if (theResourceType != null) { if (theResourceType != null) {
retVal = parser.parseResource(theResourceType, requestReader); retVal = parser.parseResource(theResourceType, requestReader);
} else { } else {
retVal = parser.parseResource(requestReader); retVal = (T) parser.parseResource(requestReader);
} }
if (theRequest.getId() != null && theRequest.getId().hasIdPart()) { if (theRequest.getId() != null && theRequest.getId().hasIdPart()) {

View File

@ -106,10 +106,11 @@ public interface IServerInterceptor {
* The incoming servlet request as provided by the servlet container * The incoming servlet request as provided by the servlet container
* @param theOperation * @param theOperation
* The type of operation that the FHIR server has determined that the client is trying to invoke * The type of operation that the FHIR server has determined that the client is trying to invoke
* @param theRequestDetails * @param theProcessedRequest
* An object which will be populated with any relevant details about the incoming request (this includes the HttpServletRequest) * An object which will be populated with the details which were extracted from the raw request by the server,
* e.g. the FHIR operation type and the parsed resource body (if any).
*/ */
void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theRequestDetails); void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest);
/** /**
* This method is called before any other processing takes place for each incoming request. It may be used to provide alternate handling for some requests, or to screen requests before they are * This method is called before any other processing takes place for each incoming request. It may be used to provide alternate handling for some requests, or to screen requests before they are

View File

@ -42,7 +42,8 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
public class InterceptorAdapter implements IServerInterceptor { public class InterceptorAdapter implements IServerInterceptor {
@Override @Override
public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) { public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws ServletException,
IOException {
return true; return true;
} }
@ -52,7 +53,12 @@ public class InterceptorAdapter implements IServerInterceptor {
} }
@Override @Override
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException { public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest) {
// nothing
}
@Override
public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) {
return true; return true;
} }
@ -62,12 +68,17 @@ public class InterceptorAdapter implements IServerInterceptor {
} }
@Override @Override
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException { public boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
return true; return true;
} }
@Override @Override
public boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException { public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
return true;
}
@Override
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
return true; return true;
} }
@ -75,16 +86,5 @@ public class InterceptorAdapter implements IServerInterceptor {
public BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException { public BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException {
return null; return null;
} }
@Override
public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws ServletException,
IOException {
return true;
}
@Override
public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theRequestDetails) {
// nothing
}
} }

View File

@ -6,6 +6,9 @@ import java.net.URL;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.net.URLEncoder; import java.net.URLEncoder;
import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
/* /*
* #%L * #%L
* HAPI FHIR - Core Library * HAPI FHIR - Core Library
@ -158,5 +161,92 @@ public class UrlUtil {
throw new Error("UTF-8 not supported on this platform"); throw new Error("UTF-8 not supported on this platform");
} }
} }
//@formatter:off
/**
* Parse a URL in one of the following forms:
* <ul>
* <li>[Resource Type]?[Search Params]
* <li>[Resource Type]/[Resource ID]
* <li>[Resource Type]/[Resource ID]/_history/[Version ID]
* </ul>
*/
//@formatter:on
public static UrlParts parseUrl(String theUrl) {
UrlParts retVal = new UrlParts();
int nextStart = 0;
boolean nextIsHistory = false;
for (int idx = 0; idx < theUrl.length(); idx++) {
char nextChar = theUrl.charAt(idx);
boolean atEnd = (idx + 1) == theUrl.length();
if (nextChar == '?' || nextChar == '/' || atEnd) {
int endIdx = atEnd ? idx + 1 : idx;
String nextSubstring = theUrl.substring(nextStart, endIdx);
if (retVal.getResourceType() == null) {
retVal.setResourceType(nextSubstring);
} else if (retVal.getResourceId() == null) {
retVal.setResourceId(nextSubstring);
} else if (nextIsHistory) {
retVal.setVersionId(nextSubstring);
} else {
if (nextSubstring.equals(Constants.URL_TOKEN_HISTORY)) {
nextIsHistory = true;
} else {
throw new InvalidRequestException("Invalid FHIR resource URL: " + theUrl);
}
}
if (nextChar == '?') {
if (theUrl.length() > idx + 1) {
retVal.setParams(theUrl.substring(idx + 1, theUrl.length()));
}
break;
}
nextStart = idx + 1;
}
}
return retVal;
}
public static class UrlParts {
private String myParams;
private String myResourceId;
private String myResourceType;
private String myVersionId;
public String getParams() {
return myParams;
}
public String getResourceId() {
return myResourceId;
}
public String getResourceType() {
return myResourceType;
}
public String getVersionId() {
return myVersionId;
}
public void setParams(String theParams) {
myParams = theParams;
}
public void setResourceId(String theResourceId) {
myResourceId = theResourceId;
}
public void setResourceType(String theResourceType) {
myResourceType = theResourceType;
}
public void setVersionId(String theVersionId) {
myVersionId = theVersionId;
}
}
} }

View File

@ -11,7 +11,7 @@
<tr th:each="issue : ${resource.issue}"> <tr th:each="issue : ${resource.issue}">
<td th:text="${issue.severityElement.value}" style="font-weight: bold;"></td> <td th:text="${issue.severityElement.value}" style="font-weight: bold;"></td>
<td th:text="${issue.location}"></td> <td th:text="${issue.location}"></td>
<td><pre th:text="${issue.details}"/></td> <td><pre th:text="${issue.diagnostics}"/></td>
</tr> </tr>
</table> </table>
</div> </div>

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -22,33 +22,33 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-example</artifactId> <artifactId>hapi-fhir-jpaserver-example</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<type>war</type> <type>war</type>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId> <artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId> <artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId> <artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
@ -143,7 +143,7 @@
<artifactItem> <artifactItem>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-example</artifactId> <artifactId>hapi-fhir-jpaserver-example</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<type>war</type> <type>war</type>
<overWrite>true</overWrite> <overWrite>true</overWrite>
<outputDirectory>target/classes</outputDirectory> <outputDirectory>target/classes</outputDirectory>

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>
@ -19,32 +19,32 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId> <artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId> <artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId> <artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId> <artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -18,17 +18,17 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId> <artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>

View File

@ -1,11 +1,10 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>
@ -31,7 +30,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>commons-logging</artifactId> <artifactId>commons-logging</artifactId>
@ -42,22 +41,22 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId> <artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId> <artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId> <artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
@ -93,18 +92,13 @@
<artifactId>jscience</artifactId> <artifactId>jscience</artifactId>
</dependency> </dependency>
<!-- FHIR RI is pulled in for UCUM support, but we don't want any of its <!-- FHIR RI is pulled in for UCUM support, but we don't want any of its dependencies. -->
dependencies. --> <!-- <dependency> <groupId>me.fhir</groupId> <artifactId>fhir-dstu1</artifactId> <version>0.0.81.2489</version> <exclusions> <exclusion> <artifactId>Saxon-HE</artifactId>
<!-- <dependency> <groupId>me.fhir</groupId> <artifactId>fhir-dstu1</artifactId> <groupId>net.sf.saxon</groupId> </exclusion> <exclusion> <artifactId>commons-discovery</artifactId> <groupId>commons-discovery</groupId> </exclusion> <exclusion>
<version>0.0.81.2489</version> <exclusions> <exclusion> <artifactId>Saxon-HE</artifactId> <artifactId>commons-codec</artifactId> <groupId>commons-codec</groupId> </exclusion> <exclusion> <artifactId>commons-logging</artifactId> <groupId>commons-logging</groupId>
<groupId>net.sf.saxon</groupId> </exclusion> <exclusion> <artifactId>commons-discovery</artifactId> </exclusion> <exclusion> <artifactId>xpp3</artifactId> <groupId>xpp3</groupId> </exclusion> <exclusion> <artifactId>junit</artifactId> <groupId>junit</groupId> </exclusion>
<groupId>commons-discovery</groupId> </exclusion> <exclusion> <artifactId>commons-codec</artifactId> <exclusion> <artifactId>jdom</artifactId> <groupId>org.jdom</groupId> </exclusion> <exclusion> <artifactId>gson</artifactId> <groupId>com.google.code.gson</groupId>
<groupId>commons-codec</groupId> </exclusion> <exclusion> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> -->
<groupId>commons-logging</groupId> </exclusion> <exclusion> <artifactId>xpp3</artifactId>
<groupId>xpp3</groupId> </exclusion> <exclusion> <artifactId>junit</artifactId>
<groupId>junit</groupId> </exclusion> <exclusion> <artifactId>jdom</artifactId>
<groupId>org.jdom</groupId> </exclusion> <exclusion> <artifactId>gson</artifactId>
<groupId>com.google.code.gson</groupId> </exclusion> </exclusions> </dependency> -->
<!-- Test Database --> <!-- Test Database -->
@ -126,8 +120,7 @@
</dependency> </dependency>
<!-- <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <!-- <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.3.2</version> </dependency> -->
<version>2.3.2</version> </dependency> -->
<!-- Spring --> <!-- Spring -->
<dependency> <dependency>
@ -162,6 +155,10 @@
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId> <artifactId>spring-beans</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId> <artifactId>spring-tx</artifactId>
@ -226,7 +223,7 @@
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId> <artifactId>jetty-servlets</artifactId>
@ -252,7 +249,7 @@
<artifactId>spring-test</artifactId> <artifactId>spring-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@ -277,84 +274,14 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<configuration> <configuration>
<!-- <!-- These tests all use a shared database, so it's easier if they run in a predictable order -->
These tests all use a shared database, so it's easier if
they run in a predictable order
-->
<runOrder>alphabetical</runOrder> <runOrder>alphabetical</runOrder>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>de.juplo</groupId>
<artifactId>hibernate4-maven-plugin</artifactId>
<version>1.0.5</version>
<configuration>
<force>true</force>
<target>SCRIPT</target>
<skip>${skip-hib4}</skip>
</configuration>
<!--
This needs to be uncommented in order for this plugin to work with
Hibernate 4.3+ (as of hibernate4-maven-plugin version 1.0.5)
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate_version}</version>
</dependency>
</dependencies>
-->
<executions>
<execution>
<id>o10g</id>
<goals>
<goal>export</goal>
</goals>
<phase>test</phase>
<configuration>
<hibernateDialect>org.hibernate.dialect.Oracle10gDialect</hibernateDialect>
<outputFile>${project.build.directory}/schema_oracle_10g.sql</outputFile>
</configuration>
</execution>
<execution>
<id>derby</id>
<goals>
<goal>export</goal>
</goals>
<phase>test</phase>
<configuration>
<hibernateDialect>org.hibernate.dialect.DerbyTenSevenDialect</hibernateDialect>
<outputFile>${project.build.directory}/schema_derby.sql</outputFile>
</configuration>
</execution>
<execution>
<id>hsql</id>
<goals>
<goal>export</goal>
</goals>
<phase>test</phase>
<configuration>
<hibernateDialect>org.hibernate.dialect.HSQLDialect</hibernateDialect>
<outputFile>${project.build.directory}/schema_hsql.sql</outputFile>
</configuration>
</execution>
<execution>
<id>mysql5</id>
<goals>
<goal>export</goal>
</goals>
<phase>test</phase>
<configuration>
<hibernateDialect>org.hibernate.dialect.MySQL5Dialect</hibernateDialect>
<outputFile>${project.build.directory}/schema_mysql_5.sql</outputFile>
</configuration>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId> <artifactId>hapi-tinder-plugin</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<executions> <executions>
<execution> <execution>
<id>build_dstu1</id> <id>build_dstu1</id>
@ -389,12 +316,12 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId> <artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
</dependencies> </dependencies>
</plugin> </plugin>
@ -413,11 +340,10 @@
</build> </build>
<reporting> <reporting>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId> <artifactId>maven-jxr-plugin</artifactId>
<version>${maven_jxr_plugin_version}</version>
</plugin> </plugin>
</plugins> </plugins>
</reporting> </reporting>
@ -429,6 +355,70 @@
<skip-hib4>true</skip-hib4> <skip-hib4>true</skip-hib4>
</properties> </properties>
</profile> </profile>
<profile>
<id>DIST</id>
<build>
<plugins>
<plugin>
<groupId>de.juplo</groupId>
<artifactId>hibernate4-maven-plugin</artifactId>
<configuration>
<force>true</force>
<target>SCRIPT</target>
<skip>${skip-hib4}</skip>
</configuration>
<!-- This needs to be uncommented in order for this plugin to work with Hibernate 4.3+ (as of hibernate4-maven-plugin version 1.0.5) <dependencies> <dependency>
<groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate_version}</version> </dependency> </dependencies> -->
<executions>
<execution>
<id>o10g</id>
<goals>
<goal>export</goal>
</goals>
<phase>test</phase>
<configuration>
<hibernateDialect>org.hibernate.dialect.Oracle10gDialect</hibernateDialect>
<outputFile>${project.build.directory}/schema_oracle_10g.sql</outputFile>
</configuration>
</execution>
<execution>
<id>derby</id>
<goals>
<goal>export</goal>
</goals>
<phase>test</phase>
<configuration>
<hibernateDialect>org.hibernate.dialect.DerbyTenSevenDialect</hibernateDialect>
<outputFile>${project.build.directory}/schema_derby.sql</outputFile>
</configuration>
</execution>
<execution>
<id>hsql</id>
<goals>
<goal>export</goal>
</goals>
<phase>test</phase>
<configuration>
<hibernateDialect>org.hibernate.dialect.HSQLDialect</hibernateDialect>
<outputFile>${project.build.directory}/schema_hsql.sql</outputFile>
</configuration>
</execution>
<execution>
<id>mysql5</id>
<goals>
<goal>export</goal>
</goals>
<phase>test</phase>
<configuration>
<hibernateDialect>org.hibernate.dialect.MySQL5Dialect</hibernateDialect>
<outputFile>${project.build.directory}/schema_mysql_5.sql</outputFile>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles> </profiles>
</project> </project>

View File

@ -166,8 +166,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return InstantDt.withCurrentTime(); return InstantDt.withCurrentTime();
} }
protected List<ResourceLink> extractResourceLinks(ResourceTable theEntity, IResource theResource) { protected Set<ResourceLink> extractResourceLinks(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceLink> retVal = new ArrayList<ResourceLink>(); Set<ResourceLink> retVal = new HashSet<ResourceLink>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
@ -185,73 +185,76 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
multiType = true; multiType = true;
} }
for (Object nextObject : extractValues(nextPathsUnsplit, theResource)) { String[] nextPathsSplit = nextPathsUnsplit.split("\\|");
if (nextObject == null) { for (String nextPath : nextPathsSplit) {
continue; for (Object nextObject : extractValues(nextPath, theResource)) {
} if (nextObject == null) {
ResourceLink nextEntity;
if (nextObject instanceof BaseResourceReferenceDt) {
BaseResourceReferenceDt nextValue = (BaseResourceReferenceDt) nextObject;
if (nextValue.isEmpty()) {
continue;
}
if (nextValue.getReference().isEmpty() || nextValue.getReference().getValue().startsWith("#")) {
// This is a blank or contained resource reference
continue; continue;
} }
String typeString = nextValue.getReference().getResourceType(); ResourceLink nextEntity;
if (isBlank(typeString)) { if (nextObject instanceof BaseResourceReferenceDt) {
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource type - " + nextValue.getReference().getValue()); BaseResourceReferenceDt nextValue = (BaseResourceReferenceDt) nextObject;
} if (nextValue.isEmpty()) {
RuntimeResourceDefinition resourceDefinition; continue;
try { }
resourceDefinition = getContext().getResourceDefinition(typeString); if (nextValue.getReference().isEmpty() || nextValue.getReference().getValue().startsWith("#")) {
} catch (DataFormatException e) { // This is a blank or contained resource reference
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextValue.getReference().getValue()); continue;
} }
Class<? extends IBaseResource> type = resourceDefinition.getImplementingClass(); String typeString = nextValue.getReference().getResourceType();
String id = nextValue.getReference().getIdPart(); if (isBlank(typeString)) {
if (StringUtils.isBlank(id)) { throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource type - " + nextValue.getReference().getValue());
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource ID - " + nextValue.getReference().getValue()); }
} RuntimeResourceDefinition resourceDefinition;
try {
resourceDefinition = getContext().getResourceDefinition(typeString);
} catch (DataFormatException e) {
throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Resource type is unknown or not supported on this server - " + nextValue.getReference().getValue());
}
IFhirResourceDao<?> dao = getDao(type); Class<? extends IBaseResource> type = resourceDefinition.getImplementingClass();
if (dao == null) { String id = nextValue.getReference().getIdPart();
StringBuilder b = new StringBuilder(); if (StringUtils.isBlank(id)) {
b.append("This server (version "); throw new InvalidRequestException("Invalid resource reference found at path[" + nextPathsUnsplit + "] - Does not contain resource ID - " + nextValue.getReference().getValue());
b.append(myContext.getVersion().getVersion()); }
b.append(") is not able to handle resources of type[");
b.append(nextValue.getReference().getResourceType());
b.append("] - Valid resource types for this server: ");
b.append(myResourceTypeToDao.keySet().toString());
throw new InvalidRequestException(b.toString()); IFhirResourceDao<?> dao = getDao(type);
} if (dao == null) {
Long valueOf; StringBuilder b = new StringBuilder();
try { b.append("This server (version ");
valueOf = translateForcedIdToPid(nextValue.getReference()); b.append(myContext.getVersion().getVersion());
} catch (ResourceNotFoundException e) { b.append(") is not able to handle resources of type[");
String resName = getContext().getResourceDefinition(type).getName(); b.append(nextValue.getReference().getResourceType());
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit); b.append("] - Valid resource types for this server: ");
} b.append(myResourceTypeToDao.keySet().toString());
ResourceTable target = myEntityManager.find(ResourceTable.class, valueOf);
if (target == null) { throw new InvalidRequestException(b.toString());
String resName = getContext().getResourceDefinition(type).getName(); }
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit); Long valueOf;
} try {
nextEntity = new ResourceLink(nextPathsUnsplit, theEntity, target); valueOf = translateForcedIdToPid(nextValue.getReference());
} else { } catch (ResourceNotFoundException e) {
if (!multiType) { String resName = getContext().getResourceDefinition(type).getName();
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass()); throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit);
}
ResourceTable target = myEntityManager.find(ResourceTable.class, valueOf);
if (target == null) {
String resName = getContext().getResourceDefinition(type).getName();
throw new InvalidRequestException("Resource " + resName + "/" + id + " not found, specified in path: " + nextPathsUnsplit);
}
nextEntity = new ResourceLink(nextPath, theEntity, target);
} else { } else {
continue; if (!multiType) {
throw new ConfigurationException("Search param " + nextSpDef.getName() + " is of unexpected datatype: " + nextObject.getClass());
} else {
continue;
}
}
if (nextEntity != null) {
retVal.add(nextEntity);
} }
}
if (nextEntity != null) {
retVal.add(nextEntity);
} }
} }
} }
@ -261,46 +264,43 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return retVal; return retVal;
} }
protected List<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource) { protected Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource) {
return mySearchParamExtractor.extractSearchParamDates(theEntity, theResource); return mySearchParamExtractor.extractSearchParamDates(theEntity, theResource);
} }
protected List<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IResource theResource) { protected Set<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IResource theResource) {
return mySearchParamExtractor.extractSearchParamNumber(theEntity, theResource); return mySearchParamExtractor.extractSearchParamNumber(theEntity, theResource);
} }
protected List<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IResource theResource) { protected Set<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IResource theResource) {
return mySearchParamExtractor.extractSearchParamUri(theEntity, theResource); return mySearchParamExtractor.extractSearchParamUri(theEntity, theResource);
} }
protected List<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IResource theResource) { protected Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IResource theResource) {
return mySearchParamExtractor.extractSearchParamCoords(theEntity, theResource); return mySearchParamExtractor.extractSearchParamCoords(theEntity, theResource);
} }
protected List<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IResource theResource) { protected Set<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IResource theResource) {
return mySearchParamExtractor.extractSearchParamQuantity(theEntity, theResource); return mySearchParamExtractor.extractSearchParamQuantity(theEntity, theResource);
} }
protected List<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IResource theResource) { protected Set<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IResource theResource) {
return mySearchParamExtractor.extractSearchParamStrings(theEntity, theResource); return mySearchParamExtractor.extractSearchParamStrings(theEntity, theResource);
} }
protected List<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IResource theResource) { protected Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IResource theResource) {
return mySearchParamExtractor.extractSearchParamTokens(theEntity, theResource); return mySearchParamExtractor.extractSearchParamTokens(theEntity, theResource);
} }
private List<Object> extractValues(String thePaths, IResource theResource) { private List<Object> extractValues(String thePath, IResource theResource) {
List<Object> values = new ArrayList<Object>(); List<Object> values = new ArrayList<Object>();
String[] nextPathsSplit = thePaths.split("\\|");
FhirTerser t = getContext().newTerser(); FhirTerser t = getContext().newTerser();
for (String nextPath : nextPathsSplit) { String nextPathTrimmed = thePath.trim();
String nextPathTrimmed = nextPath.trim(); try {
try { values.addAll(t.getValues(theResource, nextPathTrimmed));
values.addAll(t.getValues(theResource, nextPathTrimmed)); } catch (Exception e) {
} catch (Exception e) { RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource); ourLog.warn("Failed to index values from path[{}] in resource type[{}]: ", new Object[] { nextPathTrimmed, def.getName(), e.toString() });
ourLog.warn("Failed to index values from path[{}] in resource type[{}]: ", new Object[] { nextPathTrimmed, def.getName(), e.toString() });
}
} }
return values; return values;
} }
@ -520,7 +520,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} }
throw e; throw e;
} }
IResource resource = (IResource) toResource(type.getImplementingClass(), next); IResource resource = (IResource) toResource(type.getImplementingClass(), next, true);
retVal.add(resource); retVal.add(resource);
} }
return retVal; return retVal;
@ -558,12 +558,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} }
protected void populateResourceIntoEntity(IResource theResource, ResourceTable theEntity) { protected void populateResourceIntoEntity(IResource theResource, ResourceTable theEntity) {
if (theEntity.getPublished().isEmpty()) {
theEntity.setPublished(new Date());
}
theEntity.setUpdated(new Date());
theEntity.setResourceType(toResourceName(theResource)); theEntity.setResourceType(toResourceName(theResource));
List<BaseResourceReferenceDt> refs = myContext.newTerser().getAllPopulatedChildElementsOfType(theResource, BaseResourceReferenceDt.class); List<BaseResourceReferenceDt> refs = myContext.newTerser().getAllPopulatedChildElementsOfType(theResource, BaseResourceReferenceDt.class);
@ -936,13 +930,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return retVal; return retVal;
} }
protected IBaseResource toResource(BaseHasResource theEntity) { protected IBaseResource toResource(BaseHasResource theEntity, boolean theForHistoryOperation) {
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType()); RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
return toResource(type.getImplementingClass(), theEntity); return toResource(type.getImplementingClass(), theEntity, theForHistoryOperation);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity) { protected <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation) {
String resourceText = null; String resourceText = null;
switch (theEntity.getEncoding()) { switch (theEntity.getEncoding()) {
case JSON: case JSON:
@ -983,14 +977,29 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
res = (IResource) myContext.getResourceDefinition(theResourceType).newInstance(); res = (IResource) myContext.getResourceDefinition(theResourceType).newInstance();
retVal = (R) res; retVal = (R) res;
ResourceMetadataKeyEnum.DELETED_AT.put(res, new InstantDt(theEntity.getDeleted())); ResourceMetadataKeyEnum.DELETED_AT.put(res, new InstantDt(theEntity.getDeleted()));
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.DELETE); if (theForHistoryOperation) {
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.DELETE);
}
} else if (theForHistoryOperation) {
/*
* If the create and update times match, this was when the resource was created
* so we should mark it as a POST. Otherwise, it's a PUT.
*/
Date published = theEntity.getPublished().getValue();
Date updated = theEntity.getUpdated().getValue();
if (published.equals(updated)) {
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.POST);
} else {
ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.put(res, BundleEntryTransactionMethodEnum.PUT);
}
} }
res.setId(theEntity.getIdDt()); res.setId(theEntity.getIdDt());
ResourceMetadataKeyEnum.VERSION.put(res, Long.toString(theEntity.getVersion())); ResourceMetadataKeyEnum.VERSION.put(res, Long.toString(theEntity.getVersion()));
ResourceMetadataKeyEnum.PUBLISHED.put(res, theEntity.getPublished()); ResourceMetadataKeyEnum.PUBLISHED.put(res, theEntity.getPublished());
ResourceMetadataKeyEnum.UPDATED.put(res, theEntity.getUpdated()); ResourceMetadataKeyEnum.UPDATED.put(res, theEntity.getUpdated());
IDao.RESOURCE_PID.put(res, theEntity.getId());
if (theEntity.getTitle() != null) { if (theEntity.getTitle() != null) {
ResourceMetadataKeyEnum.TITLE.put(res, theEntity.getTitle()); ResourceMetadataKeyEnum.TITLE.put(res, theEntity.getTitle());
@ -1063,25 +1072,28 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} }
} }
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, Date theDeletedTimestampOrNull) { protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, Date theUpdateTime) {
return updateEntity(theResource, entity, theUpdateHistory, theDeletedTimestampOrNull, true, true); return updateEntity(theResource, entity, theUpdateHistory, theDeletedTimestampOrNull, true, true, theUpdateTime);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected ResourceTable updateEntity(final IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion) { protected ResourceTable updateEntity(final IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime) {
if (theEntity.getPublished() == null) { /*
theEntity.setPublished(new Date()); * This should be the very first thing..
} */
if (theResource != null) { if (theResource != null) {
validateResourceForStorage((T) theResource); validateResourceForStorage((T) theResource, theEntity);
String resourceType = myContext.getResourceDefinition(theResource).getName(); String resourceType = myContext.getResourceDefinition(theResource).getName();
if (isNotBlank(theEntity.getResourceType()) && !theEntity.getResourceType().equals(resourceType)) { if (isNotBlank(theEntity.getResourceType()) && !theEntity.getResourceType().equals(resourceType)) {
throw new UnprocessableEntityException("Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]"); throw new UnprocessableEntityException("Existing resource ID[" + theEntity.getIdDt().toUnqualifiedVersionless() + "] is of type[" + theEntity.getResourceType() + "] - Cannot update with [" + resourceType + "]");
} }
} }
if (theEntity.getPublished() == null) {
theEntity.setPublished(theUpdateTime);
}
if (theUpdateHistory) { if (theUpdateHistory) {
final ResourceHistoryTable historyEntry = theEntity.toHistory(); final ResourceHistoryTable historyEntry = theEntity.toHistory();
myEntityManager.persist(historyEntry); myEntityManager.persist(historyEntry);
@ -1109,25 +1121,25 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
Collection<ResourceIndexedSearchParamCoords> paramsCoords = new ArrayList<ResourceIndexedSearchParamCoords>(theEntity.getParamsCoords()); Collection<ResourceIndexedSearchParamCoords> paramsCoords = new ArrayList<ResourceIndexedSearchParamCoords>(theEntity.getParamsCoords());
Collection<ResourceLink> resourceLinks = new ArrayList<ResourceLink>(theEntity.getResourceLinks()); Collection<ResourceLink> resourceLinks = new ArrayList<ResourceLink>(theEntity.getResourceLinks());
List<ResourceIndexedSearchParamString> stringParams = null; Set<ResourceIndexedSearchParamString> stringParams = null;
List<ResourceIndexedSearchParamToken> tokenParams = null; Set<ResourceIndexedSearchParamToken> tokenParams = null;
List<ResourceIndexedSearchParamNumber> numberParams = null; Set<ResourceIndexedSearchParamNumber> numberParams = null;
List<ResourceIndexedSearchParamQuantity> quantityParams = null; Set<ResourceIndexedSearchParamQuantity> quantityParams = null;
List<ResourceIndexedSearchParamDate> dateParams = null; Set<ResourceIndexedSearchParamDate> dateParams = null;
List<ResourceIndexedSearchParamUri> uriParams = null; Set<ResourceIndexedSearchParamUri> uriParams = null;
List<ResourceIndexedSearchParamCoords> coordsParams = null; Set<ResourceIndexedSearchParamCoords> coordsParams = null;
List<ResourceLink> links = null; Set<ResourceLink> links = null;
if (theDeletedTimestampOrNull != null) { if (theDeletedTimestampOrNull != null) {
stringParams = Collections.emptyList(); stringParams = Collections.emptySet();
tokenParams = Collections.emptyList(); tokenParams = Collections.emptySet();
numberParams = Collections.emptyList(); numberParams = Collections.emptySet();
quantityParams = Collections.emptyList(); quantityParams = Collections.emptySet();
dateParams = Collections.emptyList(); dateParams = Collections.emptySet();
uriParams = Collections.emptyList(); uriParams = Collections.emptySet();
coordsParams = Collections.emptyList(); coordsParams = Collections.emptySet();
links = Collections.emptyList(); links = Collections.emptySet();
theEntity.setDeleted(theDeletedTimestampOrNull); theEntity.setDeleted(theDeletedTimestampOrNull);
theEntity.setUpdated(theDeletedTimestampOrNull); theEntity.setUpdated(theDeletedTimestampOrNull);
@ -1147,7 +1159,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
// ourLog.info("Indexing resource: {}", entity.getId()); // ourLog.info("Indexing resource: {}", entity.getId());
ourLog.trace("Storing string indexes: {}", stringParams); ourLog.trace("Storing string indexes: {}", stringParams);
tokenParams = new ArrayList<ResourceIndexedSearchParamToken>(); tokenParams = new HashSet<ResourceIndexedSearchParamToken>();
for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(theEntity, theResource)) { for (BaseResourceIndexedSearchParam next : extractSearchParamTokens(theEntity, theResource)) {
if (next instanceof ResourceIndexedSearchParamToken) { if (next instanceof ResourceIndexedSearchParamToken) {
tokenParams.add((ResourceIndexedSearchParamToken) next); tokenParams.add((ResourceIndexedSearchParamToken) next);
@ -1159,7 +1171,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
links = extractResourceLinks(theEntity, theResource); links = extractResourceLinks(theEntity, theResource);
populateResourceIntoEntity(theResource, theEntity); populateResourceIntoEntity(theResource, theEntity);
theEntity.setUpdated(new Date()); theEntity.setUpdated(theUpdateTime);
theEntity.setLanguage(theResource.getLanguage().getValue()); theEntity.setLanguage(theResource.getLanguage().getValue());
theEntity.setParamsString(stringParams); theEntity.setParamsString(stringParams);
theEntity.setParamsStringPopulated(stringParams.isEmpty() == false); theEntity.setParamsStringPopulated(stringParams.isEmpty() == false);
@ -1178,11 +1190,11 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
theEntity.setResourceLinks(links); theEntity.setResourceLinks(links);
theEntity.setHasLinks(links.isEmpty() == false); theEntity.setHasLinks(links.isEmpty() == false);
theEntity.setIndexStatus(INDEX_STATUS_INDEXED); theEntity.setIndexStatus(INDEX_STATUS_INDEXED);
} else { } else {
populateResourceIntoEntity(theResource, theEntity); populateResourceIntoEntity(theResource, theEntity);
theEntity.setUpdated(new Date()); theEntity.setUpdated(theUpdateTime);
theEntity.setLanguage(theResource.getLanguage().getValue()); theEntity.setLanguage(theResource.getLanguage().getValue());
theEntity.setIndexStatus(null); theEntity.setIndexStatus(null);
@ -1197,8 +1209,12 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
myEntityManager.persist(theEntity.getForcedId()); myEntityManager.persist(theEntity.getForcedId());
} }
postPersist(theEntity, (T) theResource);
} else { } else {
theEntity = myEntityManager.merge(theEntity); theEntity = myEntityManager.merge(theEntity);
postUpdate(theEntity, (T) theResource);
} }
if (thePerformIndexing) { if (thePerformIndexing) {
@ -1289,6 +1305,30 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return theEntity; return theEntity;
} }
/**
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the
* first time.
*
* @param theEntity
* The resource
* @param theResource The resource being persisted
*/
protected void postUpdate(ResourceTable theEntity, T theResource) {
// nothing
}
/**
* Subclasses may override to provide behaviour. Called when a resource has been inserved into the database for the
* first time.
*
* @param theEntity
* The resource
* @param theResource The resource being persisted
*/
protected void postPersist(ResourceTable theEntity, T theResource) {
// nothing
}
/** /**
* This method is invoked immediately before storing a new resource, or an update to an existing resource to allow * This method is invoked immediately before storing a new resource, or an update to an existing resource to allow
* the DAO to ensure that it is valid for persistence. By default, checks for the "subsetted" tag and rejects * the DAO to ensure that it is valid for persistence. By default, checks for the "subsetted" tag and rejects
@ -1296,8 +1336,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
* *
* @param theResource * @param theResource
* The resource that is about to be persisted * The resource that is about to be persisted
* @param theEntityToSave TODO
*/ */
protected void validateResourceForStorage(T theResource) { protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) {
IResource res = (IResource) theResource; IResource res = (IResource) theResource;
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(res); TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(res);
if (tagList != null) { if (tagList != null) {

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.jpa.dao; package ca.uhn.fhir.jpa.dao;
import static org.apache.commons.lang3.StringUtils.INDEX_NOT_FOUND;
/* /*
* #%L * #%L
* HAPI FHIR JPA Server * HAPI FHIR JPA Server
@ -143,7 +142,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirResourceDao.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirResourceDao.class);
@PersistenceContext(type = PersistenceContextType.TRANSACTION) @PersistenceContext(type = PersistenceContextType.TRANSACTION)
private EntityManager myEntityManager; protected EntityManager myEntityManager;
@Autowired @Autowired
private PlatformTransactionManager myPlatformTransactionManager; private PlatformTransactionManager myPlatformTransactionManager;
@ -246,9 +245,10 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
HashSet<Long> found = new HashSet<Long>(q.getResultList()); HashSet<Long> found = new HashSet<Long>(q.getResultList());
if (!theExistingPids.isEmpty()) { if (!theExistingPids.isEmpty()) {
theExistingPids.retainAll(found); theExistingPids.retainAll(found);
return theExistingPids;
} else {
return found;
} }
return found;
} }
// private Set<Long> addPredicateComposite(String theParamName, Set<Long> thePids, List<? extends // private Set<Long> addPredicateComposite(String theParamName, Set<Long> thePids, List<? extends
@ -256,49 +256,54 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
// } // }
private Set<Long> addPredicateLanguage(Set<Long> thePids, List<List<? extends IQueryParameterType>> theList) { private Set<Long> addPredicateLanguage(Set<Long> thePids, List<List<? extends IQueryParameterType>> theList) {
Set<Long> retVal = thePids;
if (theList == null || theList.isEmpty()) { if (theList == null || theList.isEmpty()) {
return thePids; return retVal;
}
if (theList.size() > 1) {
throw new InvalidRequestException("Language parameter can not have more than one AND value, found " + theList.size());
} }
for (List<? extends IQueryParameterType> nextList : theList) {
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class); CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTable> from = cq.from(ResourceTable.class); Root<ResourceTable> from = cq.from(ResourceTable.class);
cq.select(from.get("myId").as(Long.class)); cq.select(from.get("myId").as(Long.class));
Set<String> values = new HashSet<String>(); Set<String> values = new HashSet<String>();
for (IQueryParameterType next : theList.get(0)) { for (IQueryParameterType next : nextList) {
if (next instanceof StringParam) { if (next instanceof StringParam) {
String nextValue = ((StringParam) next).getValue(); String nextValue = ((StringParam) next).getValue();
if (isBlank(nextValue)) { if (isBlank(nextValue)) {
continue; continue;
}
values.add(nextValue);
} else {
throw new InternalErrorException("Lanugage parameter must be of type " + StringParam.class.getCanonicalName() + " - Got " + next.getClass().getCanonicalName());
} }
values.add(nextValue); }
if (values.isEmpty()) {
return retVal;
}
Predicate typePredicate = builder.equal(from.get("myResourceType"), myResourceName);
Predicate langPredicate = from.get("myLanguage").as(String.class).in(values);
Predicate masterCodePredicate = builder.and(typePredicate, langPredicate);
Predicate notDeletedPredicate = builder.isNull(from.get("myDeleted"));
if (retVal.size() > 0) {
Predicate inPids = (from.get("myId").in(retVal));
cq.where(builder.and(masterCodePredicate, inPids, notDeletedPredicate));
} else { } else {
throw new InternalErrorException("Lanugage parameter must be of type " + StringParam.class.getCanonicalName() + " - Got " + next.getClass().getCanonicalName()); cq.where(builder.and(masterCodePredicate, notDeletedPredicate));
}
TypedQuery<Long> q = myEntityManager.createQuery(cq);
retVal = new HashSet<Long>(q.getResultList());
if (retVal.isEmpty()) {
return retVal;
} }
} }
if (values.isEmpty()) { return retVal;
return thePids;
}
Predicate typePredicate = builder.equal(from.get("myResourceType"), myResourceName);
Predicate langPredicate = from.get("myLanguage").as(String.class).in(values);
Predicate masterCodePredicate = builder.and(typePredicate, langPredicate);
Predicate notDeletedPredicate = builder.isNull(from.get("myDeleted"));
if (thePids.size() > 0) {
Predicate inPids = (from.get("myId").in(thePids));
cq.where(builder.and(masterCodePredicate, inPids, notDeletedPredicate));
} else {
cq.where(builder.and(masterCodePredicate, notDeletedPredicate));
}
TypedQuery<Long> q = myEntityManager.createQuery(cq);
return new HashSet<Long>(q.getResultList());
} }
private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root<? extends BaseResourceIndexedSearchParam> from, List<Predicate> codePredicates, IQueryParameterType nextOr) { private boolean addPredicateMissingFalseIfPresent(CriteriaBuilder theBuilder, String theParamName, Root<? extends BaseResourceIndexedSearchParam> from, List<Predicate> codePredicates, IQueryParameterType nextOr) {
@ -1005,7 +1010,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
} }
} }
return doCreate(theResource, theIfNoneExist, thePerformIndexing); return doCreate(theResource, theIfNoneExist, thePerformIndexing, new Date());
} }
private Predicate createCompositeParamPart(CriteriaBuilder builder, Root<ResourceTable> from, RuntimeSearchParam left, IQueryParameterType leftValue) { private Predicate createCompositeParamPart(CriteriaBuilder builder, Root<ResourceTable> from, RuntimeSearchParam left, IQueryParameterType leftValue) {
@ -1269,7 +1274,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
ActionRequestDetails requestDetails = new ActionRequestDetails(theId, theId.getResourceType()); ActionRequestDetails requestDetails = new ActionRequestDetails(theId, theId.getResourceType());
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails); notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
ResourceTable savedEntity = updateEntity(null, entity, true, new Date()); Date updateTime = new Date();
ResourceTable savedEntity = updateEntity(null, entity, true, updateTime, updateTime);
notifyWriteCompleted(); notifyWriteCompleted();
@ -1299,14 +1305,15 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails); notifyInterceptors(RestOperationTypeEnum.DELETE, requestDetails);
// Perform delete // Perform delete
ResourceTable savedEntity = updateEntity(null, entity, true, new Date()); Date updateTime = new Date();
ResourceTable savedEntity = updateEntity(null, entity, true, updateTime, updateTime);
notifyWriteCompleted(); notifyWriteCompleted();
ourLog.info("Processed delete on {} in {}ms", theUrl, w.getMillisAndRestart()); ourLog.info("Processed delete on {} in {}ms", theUrl, w.getMillisAndRestart());
return toMethodOutcome(savedEntity, null); return toMethodOutcome(savedEntity, null);
} }
private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing) { private DaoMethodOutcome doCreate(T theResource, String theIfNoneExist, boolean thePerformIndexing, Date theUpdateTime) {
StopWatch w = new StopWatch(); StopWatch w = new StopWatch();
preProcessResourceForStorage(theResource); preProcessResourceForStorage(theResource);
@ -1347,7 +1354,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
ActionRequestDetails requestDetails = new ActionRequestDetails(theResource.getId(), toResourceName(theResource), theResource); ActionRequestDetails requestDetails = new ActionRequestDetails(theResource.getId(), toResourceName(theResource), theResource);
notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails); notifyInterceptors(RestOperationTypeEnum.CREATE, requestDetails);
updateEntity(theResource, entity, false, null, thePerformIndexing, true); updateEntity(theResource, entity, false, null, thePerformIndexing, true, theUpdateTime);
DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(true); DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(true);
@ -1420,7 +1427,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
try { try {
BaseHasResource entity = readEntity(theId.toVersionless(), false); BaseHasResource entity = readEntity(theId.toVersionless(), false);
validateResourceType(entity); validateResourceType(entity);
currentTmp = toResource(myResourceType, entity); currentTmp = toResource(myResourceType, entity, true);
if (ResourceMetadataKeyEnum.UPDATED.get(currentTmp).after(end.getValue())) { if (ResourceMetadataKeyEnum.UPDATED.get(currentTmp).after(end.getValue())) {
currentTmp = null; currentTmp = null;
} }
@ -1497,7 +1504,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
if (retVal.size() == maxResults) { if (retVal.size() == maxResults) {
break; break;
} }
retVal.add(toResource(myResourceType, next)); retVal.add(toResource(myResourceType, next, true));
} }
return retVal; return retVal;
@ -1528,7 +1535,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
return retVal; return retVal;
} }
private void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids) { private void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation) {
if (theIncludePids.isEmpty()) { if (theIncludePids.isEmpty()) {
return; return;
} }
@ -1547,7 +1554,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
for (ResourceTable next : q.getResultList()) { for (ResourceTable next : q.getResultList()) {
Class<? extends IBaseResource> resourceType = getContext().getResourceDefinition(next.getResourceType()).getImplementingClass(); Class<? extends IBaseResource> resourceType = getContext().getResourceDefinition(next.getResourceType()).getImplementingClass();
IResource resource = (IResource) toResource(resourceType, next); IResource resource = (IResource) toResource(resourceType, next, theForHistoryOperation);
Integer index = position.get(next.getId()); Integer index = position.get(next.getId());
if (index == null) { if (index == null) {
ourLog.warn("Got back unexpected resource PID {}", next.getId()); ourLog.warn("Got back unexpected resource PID {}", next.getId());
@ -1828,7 +1835,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
BaseHasResource entity = readEntity(theId); BaseHasResource entity = readEntity(theId);
validateResourceType(entity); validateResourceType(entity);
T retVal = toResource(myResourceType, entity); T retVal = toResource(myResourceType, entity, false);
InstantDt deleted = ResourceMetadataKeyEnum.DELETED_AT.get(retVal); InstantDt deleted = ResourceMetadataKeyEnum.DELETED_AT.get(retVal);
if (deleted != null && !deleted.isEmpty()) { if (deleted != null && !deleted.isEmpty()) {
@ -2066,7 +2073,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
// Execute the query and make sure we return distinct results // Execute the query and make sure we return distinct results
List<IBaseResource> retVal = new ArrayList<IBaseResource>(); List<IBaseResource> retVal = new ArrayList<IBaseResource>();
loadResourcesByPid(pidsSubList, retVal, revIncludedPids); loadResourcesByPid(pidsSubList, retVal, revIncludedPids, false);
return retVal; return retVal;
} }
@ -2126,42 +2133,41 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
if (nextParamEntry.getValue().isEmpty()) { if (nextParamEntry.getValue().isEmpty()) {
continue; continue;
} else if (nextParamEntry.getValue().size() > 1) {
throw new InvalidRequestException("AND queries not supported for _id (Multiple instances of this param found)");
} else { } else {
Set<Long> joinPids = new HashSet<Long>(); for (List<? extends IQueryParameterType> nextValue : nextParamEntry.getValue()) {
List<? extends IQueryParameterType> nextValue = nextParamEntry.getValue().get(0); Set<Long> joinPids = new HashSet<Long>();
if (nextValue == null || nextValue.size() == 0) { if (nextValue == null || nextValue.size() == 0) {
continue; continue;
} else { } else {
for (IQueryParameterType next : nextValue) { for (IQueryParameterType next : nextValue) {
String value = next.getValueAsQueryToken(); String value = next.getValueAsQueryToken();
IIdType valueId = new IdDt(value); IIdType valueId = new IdDt(value);
try { try {
BaseHasResource entity = readEntity(valueId); BaseHasResource entity = readEntity(valueId);
if (entity.getDeleted() != null) { if (entity.getDeleted() != null) {
continue; continue;
}
joinPids.add(entity.getId());
} catch (ResourceNotFoundException e) {
// This isn't an error, just means no result found
} }
joinPids.add(entity.getId()); }
} catch (ResourceNotFoundException e) { if (joinPids.isEmpty()) {
// This isn't an error, just means no result found return new HashSet<Long>();
} }
} }
if (joinPids.isEmpty()) {
pids = addPredicateId(pids, joinPids);
if (pids.isEmpty()) {
return new HashSet<Long>(); return new HashSet<Long>();
} }
}
pids = addPredicateId(pids, joinPids); if (pids.isEmpty()) {
if (pids.isEmpty()) { pids.addAll(joinPids);
return new HashSet<Long>(); } else {
} pids.retainAll(joinPids);
}
if (pids.isEmpty()) {
pids.addAll(joinPids);
} else {
pids.retainAll(joinPids);
} }
} }
@ -2382,7 +2388,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
if (resourceId.isIdPartValidLong()) { if (resourceId.isIdPartValidLong()) {
throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getId().getIdPart())); throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getId().getIdPart()));
} }
return doCreate(theResource, null, thePerformIndexing); return doCreate(theResource, null, thePerformIndexing, new Date());
} }
} }
@ -2399,7 +2405,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IResource> extends BaseH
notifyInterceptors(RestOperationTypeEnum.UPDATE, requestDetails); notifyInterceptors(RestOperationTypeEnum.UPDATE, requestDetails);
// Perform update // Perform update
ResourceTable savedEntity = updateEntity(theResource, entity, true, null, thePerformIndexing, true); ResourceTable savedEntity = updateEntity(theResource, entity, true, null, thePerformIndexing, true, new Date());
notifyWriteCompleted(); notifyWriteCompleted();

View File

@ -101,7 +101,7 @@ public abstract class BaseHapiFhirSystemDao<T> extends BaseHapiFhirDao<IBaseReso
for (ResourceTable resourceTable : resources) { for (ResourceTable resourceTable : resources) {
final IBaseResource resource; final IBaseResource resource;
try { try {
resource = toResource(resourceTable); resource = toResource(resourceTable, false);
} catch (DataFormatException e) { } catch (DataFormatException e) {
ourLog.warn("Failure parsing resource: {}", e.toString()); ourLog.warn("Failure parsing resource: {}", e.toString());
throw new UnprocessableEntityException(Long.toString(resourceTable.getId())); throw new UnprocessableEntityException(Long.toString(resourceTable.getId()));

View File

@ -35,6 +35,8 @@ public class DaoConfig {
private int myIncludeLimit = 2000; private int myIncludeLimit = 2000;
private List<IServerInterceptor> myInterceptors; private List<IServerInterceptor> myInterceptors;
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC; private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
private boolean mySubscriptionEnabled;
private long mySubscriptionPollDelay = 1000;
/** /**
* See {@link #setIncludeLimit(int)} * See {@link #setIncludeLimit(int)}
@ -64,6 +66,17 @@ public class DaoConfig {
return myResourceEncoding; return myResourceEncoding;
} }
public long getSubscriptionPollDelay() {
return mySubscriptionPollDelay;
}
/**
* See {@link #setSubscriptionEnabled(boolean)}
*/
public boolean isSubscriptionEnabled() {
return mySubscriptionEnabled;
}
public void setHardSearchLimit(int theHardSearchLimit) { public void setHardSearchLimit(int theHardSearchLimit) {
myHardSearchLimit = theHardSearchLimit; myHardSearchLimit = theHardSearchLimit;
} }
@ -90,12 +103,11 @@ public class DaoConfig {
* ID). * ID).
* </p> * </p>
*/ */
public void setInterceptors(List<IServerInterceptor> theInterceptors) { public void setInterceptors(IServerInterceptor... theInterceptor) {
myInterceptors = theInterceptors; setInterceptors(new ArrayList<IServerInterceptor>());
} if (theInterceptor != null && theInterceptor.length != 0) {
getInterceptors().addAll(Arrays.asList(theInterceptor));
public void setResourceEncoding(ResourceEncodingEnum theResourceEncoding) { }
myResourceEncoding = theResourceEncoding;
} }
/** /**
@ -107,12 +119,27 @@ public class DaoConfig {
* ID). * ID).
* </p> * </p>
*/ */
public void setInterceptors(IServerInterceptor... theInterceptor) { public void setInterceptors(List<IServerInterceptor> theInterceptors) {
if (theInterceptor == null || theInterceptor.length==0){ myInterceptors = theInterceptors;
setInterceptors(new ArrayList<IServerInterceptor>()); }
} else {
setInterceptors(Arrays.asList(theInterceptor)); public void setResourceEncoding(ResourceEncodingEnum theResourceEncoding) {
} myResourceEncoding = theResourceEncoding;
}
/**
* Does this server support subscription? If set to true, the server
* will enable the subscription monitoring mode, which adds a bit of
* overhead. Note that if this is enabled, you must also include
* Spring task scanning to your XML config for the scheduled tasks
* used by the subscription module.
*/
public void setSubscriptionEnabled(boolean theSubscriptionEnabled) {
mySubscriptionEnabled = theSubscriptionEnabled;
}
public void setSubscriptionPollDelay(long theSubscriptionPollDelay) {
mySubscriptionPollDelay = theSubscriptionPollDelay;
} }
} }

View File

@ -27,6 +27,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.Questionnaire; import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse; import ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse;
@ -58,8 +59,8 @@ public class FhirResourceDaoQuestionnaireResponseDstu2 extends FhirResourceDaoDs
} }
@Override @Override
protected void validateResourceForStorage(QuestionnaireResponse theResource) { protected void validateResourceForStorage(QuestionnaireResponse theResource, ResourceTable theEntityToSave) {
super.validateResourceForStorage(theResource); super.validateResourceForStorage(theResource, theEntityToSave);
if (!myValidateResponses) { if (!myValidateResponses) {
return; return;
} }

View File

@ -0,0 +1,183 @@
package ca.uhn.fhir.jpa.dao;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.jpa.dao.data.ISubscriptionFlaggedResourceDataDao;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource;
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.dstu2.resource.Subscription;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
public class FhirResourceDaoSubscriptionDstu2 extends FhirResourceDaoDstu2<Subscription>implements IFhirResourceDaoSubscription<Subscription> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSubscriptionDstu2.class);
@Autowired
private ISubscriptionFlaggedResourceDataDao mySubscriptionFlaggedResourceDataDao;
private void createSubscriptionTable(ResourceTable theEntity, Subscription theSubscription) {
SubscriptionTable subscriptionEntity = new SubscriptionTable();
subscriptionEntity.setSubscriptionResource(theEntity);
subscriptionEntity.setNextCheck(theEntity.getPublished().getValue());
subscriptionEntity.setMostRecentMatch(theEntity.getPublished().getValue());
subscriptionEntity.setStatus(theSubscription.getStatusElement().getValueAsEnum());
myEntityManager.persist(subscriptionEntity);
}
@Scheduled(fixedDelay = 10 * DateUtils.MILLIS_PER_SECOND)
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public void pollForNewUndeliveredResources() {
if (getConfig().isSubscriptionEnabled() == false) {
return;
}
ourLog.trace("Beginning pollForNewUndeliveredResources()");
// SubscriptionCandidateResource
TypedQuery<SubscriptionTable> q = myEntityManager.createNamedQuery("Q_HFJ_SUBSCRIPTION_NEXT_CHECK", SubscriptionTable.class);
q.setParameter("next_check", new Date());
q.setParameter("status", SubscriptionStatusEnum.ACTIVE);
List<SubscriptionTable> subscriptions = q.getResultList();
for (SubscriptionTable nextSubscriptionTable : subscriptions) {
pollForNewUndeliveredResources(nextSubscriptionTable);
}
}
private void pollForNewUndeliveredResources(SubscriptionTable theSubscriptionTable) {
Subscription subscription = toResource(Subscription.class, theSubscriptionTable.getSubscriptionResource(), false);
RuntimeResourceDefinition resourceDef = validateCriteriaAndReturnResourceDefinition(subscription);
SearchParameterMap criteriaUrl = translateMatchUrl(subscription.getCriteria(), resourceDef);
criteriaUrl = new SearchParameterMap();//TODO:remove
long start = theSubscriptionTable.getMostRecentMatch().getTime();
long end = System.currentTimeMillis() - getConfig().getSubscriptionPollDelay();
if (end <= start) {
ourLog.trace("Skipping search for subscription");
return;
}
ourLog.info("Subscription search from {} to {}", start, end);
DateRangeParam range = new DateRangeParam();
range.setLowerBound(new DateParam(QuantityCompararatorEnum.GREATERTHAN, start));
range.setUpperBound(new DateParam(QuantityCompararatorEnum.LESSTHAN, end));
criteriaUrl.setLastUpdated(range);
IFhirResourceDao<? extends IBaseResource> dao = getDao(resourceDef.getImplementingClass());
IBundleProvider results = dao.search(criteriaUrl);
if (results.size() == 0) {
return;
}
ourLog.info("Found {} new results for Subscription {}", results.size(), subscription.getId().getIdPart());
List<SubscriptionFlaggedResource> flags = new ArrayList<SubscriptionFlaggedResource>();
for (IBaseResource next : results.getResources(0, results.size())) {
SubscriptionFlaggedResource nextFlag = new SubscriptionFlaggedResource();
// nextFlag.setResource();
}
}
@Override
protected void postPersist(ResourceTable theEntity, Subscription theSubscription) {
super.postPersist(theEntity, theSubscription);
createSubscriptionTable(theEntity, theSubscription);
}
@Override
protected ResourceTable updateEntity(IResource theResource, ResourceTable theEntity, boolean theUpdateHistory, Date theDeletedTimestampOrNull, boolean thePerformIndexing, boolean theUpdateVersion, Date theUpdateTime) {
ResourceTable retVal = super.updateEntity(theResource, theEntity, theUpdateHistory, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime);
Subscription resource = (Subscription) theResource;
Long resourceId = theEntity.getId();
if (theDeletedTimestampOrNull != null) {
Query q = myEntityManager.createNamedQuery("Q_HFJ_SUBSCRIPTION_DELETE");
q.setParameter("res_id", resourceId);
q.executeUpdate();
} else {
Query q = myEntityManager.createNamedQuery("Q_HFJ_SUBSCRIPTION_SET_STATUS");
q.setParameter("res_id", resourceId);
q.setParameter("status", resource.getStatusElement().getValueAsEnum());
if (q.executeUpdate() > 0) {
ourLog.info("Updated subscription status for subscription {} to {}", resourceId, resource.getStatusElement().getValueAsEnum());
} else {
createSubscriptionTable(retVal, resource);
}
}
return retVal;
}
@Override
protected void validateResourceForStorage(Subscription theResource, ResourceTable theEntityToSave) {
super.validateResourceForStorage(theResource, theEntityToSave);
RuntimeResourceDefinition resDef = validateCriteriaAndReturnResourceDefinition(theResource);
IFhirResourceDao<? extends IBaseResource> dao = getDao(resDef.getImplementingClass());
if (dao == null) {
throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resDef);
}
if (theResource.getChannel().getType() == null) {
throw new UnprocessableEntityException("Subscription.channel.type must be populated on this server");
}
SubscriptionStatusEnum status = theResource.getStatusElement().getValueAsEnum();
if (status == null) {
throw new UnprocessableEntityException("Subscription.status must be populated on this server");
}
}
private RuntimeResourceDefinition validateCriteriaAndReturnResourceDefinition(Subscription theResource) {
String query = theResource.getCriteria();
if (isBlank(query)) {
throw new UnprocessableEntityException("Subscription.criteria must be populated");
}
int sep = query.indexOf('?');
if (sep <= 1) {
throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
}
String resType = query.substring(0, sep);
if (resType.contains("/")) {
throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
}
RuntimeResourceDefinition resDef;
try {
resDef = getContext().getResourceDefinition(resType);
} catch (DataFormatException e) {
throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resType);
}
return resDef;
}
}

View File

@ -60,7 +60,7 @@ public class FhirResourceDaoValueSetDstu2 extends FhirResourceDaoDstu2<ValueSet>
if (sourceEntity == null) { if (sourceEntity == null) {
throw new ResourceNotFoundException(theId); throw new ResourceNotFoundException(theId);
} }
ValueSet source = (ValueSet) toResource(sourceEntity); ValueSet source = (ValueSet) toResource(sourceEntity, false);
/* /*
* Add composed concepts * Add composed concepts

View File

@ -104,6 +104,7 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao<List<IResource>> {
OperationOutcome oo = new OperationOutcome(); OperationOutcome oo = new OperationOutcome();
retVal.add(oo); retVal.add(oo);
Date updateTime = new Date();
for (int resourceIdx = 0; resourceIdx < theResources.size(); resourceIdx++) { for (int resourceIdx = 0; resourceIdx < theResources.size(); resourceIdx++) {
IResource nextResource = theResources.get(resourceIdx); IResource nextResource = theResources.get(resourceIdx);
@ -160,6 +161,8 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao<List<IResource>> {
if (entity == null) { if (entity == null) {
nextResouceOperationOut = BundleEntryTransactionMethodEnum.POST; nextResouceOperationOut = BundleEntryTransactionMethodEnum.POST;
entity = toEntity(nextResource); entity = toEntity(nextResource);
entity.setUpdated(updateTime);
entity.setPublished(updateTime);
if (nextId.isEmpty() == false && "cid:".equals(nextId.getBaseUrl())) { if (nextId.isEmpty() == false && "cid:".equals(nextId.getBaseUrl())) {
ourLog.debug("Resource in transaction has ID[{}], will replace with server assigned ID", nextId.getIdPart()); ourLog.debug("Resource in transaction has ID[{}], will replace with server assigned ID", nextId.getIdPart());
} else if (nextResouceOperationIn == BundleEntryTransactionMethodEnum.POST) { } else if (nextResouceOperationIn == BundleEntryTransactionMethodEnum.POST) {
@ -170,7 +173,7 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao<List<IResource>> {
if (candidateMatches.size() == 1) { if (candidateMatches.size() == 1) {
ourLog.debug("Resource with match URL [{}] already exists, will be NOOP", matchUrl); ourLog.debug("Resource with match URL [{}] already exists, will be NOOP", matchUrl);
BaseHasResource existingEntity = loadFirstEntityFromCandidateMatches(candidateMatches); BaseHasResource existingEntity = loadFirstEntityFromCandidateMatches(candidateMatches);
IResource existing = (IResource) toResource(existingEntity); IResource existing = (IResource) toResource(existingEntity, false);
persistedResources.add(null); persistedResources.add(null);
retVal.add(existing); retVal.add(existing);
continue; continue;
@ -262,11 +265,11 @@ public class FhirSystemDaoDstu1 extends BaseHapiFhirSystemDao<List<IResource>> {
InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(resource); InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(resource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
if (deletedInstantOrNull == null && ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(resource) == BundleEntryTransactionMethodEnum.DELETE) { if (deletedInstantOrNull == null && ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(resource) == BundleEntryTransactionMethodEnum.DELETE) {
deletedTimestampOrNull = new Date(); deletedTimestampOrNull = updateTime;
ResourceMetadataKeyEnum.DELETED_AT.put(resource, new InstantDt(deletedTimestampOrNull)); ResourceMetadataKeyEnum.DELETED_AT.put(resource, new InstantDt(deletedTimestampOrNull));
} }
updateEntity(resource, table, table.getId() != null, deletedTimestampOrNull); updateEntity(resource, table, table.getId() != null, deletedTimestampOrNull, updateTime);
} }
long delay = System.currentTimeMillis() - start; long delay = System.currentTimeMillis() - start;

View File

@ -19,7 +19,9 @@ package ca.uhn.fhir.jpa.dao;
* limitations under the License. * limitations under the License.
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.*; import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
@ -64,6 +66,8 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.FhirTerser;
import ca.uhn.fhir.util.UrlUtil;
import ca.uhn.fhir.util.UrlUtil.UrlParts;
public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> { public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2.class);
@ -92,10 +96,10 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
resp.addEntry().setResource(ooResp); resp.addEntry().setResource(ooResp);
/* /*
* For batch, we handle each entry as a mini-transaction in its own * For batch, we handle each entry as a mini-transaction in its own database transaction so that if one fails, it
* database transaction so that if one fails, it doesn't prevent others * doesn't prevent others
*/ */
for (final Entry nextRequestEntry : theRequest.getEntry()) { for (final Entry nextRequestEntry : theRequest.getEntry()) {
TransactionCallback<Bundle> callback = new TransactionCallback<Bundle>() { TransactionCallback<Bundle> callback = new TransactionCallback<Bundle>() {
@ -118,13 +122,13 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
Entry subResponseEntry = nextResponseBundle.getEntry().get(0); Entry subResponseEntry = nextResponseBundle.getEntry().get(0);
resp.addEntry(subResponseEntry); resp.addEntry(subResponseEntry);
/* /*
* If the individual entry didn't have a resource in its response, bring the * If the individual entry didn't have a resource in its response, bring the sub-transaction's
* sub-transaction's OperationOutcome across so the client can see it * OperationOutcome across so the client can see it
*/ */
if (subResponseEntry.getResource() == null) { if (subResponseEntry.getResource() == null) {
subResponseEntry.setResource(nextResponseBundle.getEntry().get(0).getResource()); subResponseEntry.setResource(nextResponseBundle.getEntry().get(0).getResource());
} }
} catch (BaseServerResponseException e) { } catch (BaseServerResponseException e) {
caughtEx = e; caughtEx = e;
} catch (Throwable t) { } catch (Throwable t) {
@ -167,75 +171,6 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
return retVal; return retVal;
} }
private UrlParts parseUrl(String theAction, String theUrl) {
UrlParts retVal = new UrlParts();
//@formatter:off
/*
* We assume that the URL passed in is in one of the following forms:
* [Resource Type]?[Search Params]
* [Resource Type]/[Resource ID]
* [Resource Type]/[Resource ID]/_history/[Version ID]
*/
//@formatter:on
int nextStart = 0;
boolean nextIsHistory = false;
for (int idx = 0; idx < theUrl.length(); idx++) {
char nextChar = theUrl.charAt(idx);
boolean atEnd = (idx + 1) == theUrl.length();
if (nextChar == '?' || nextChar == '/' || atEnd) {
int endIdx = atEnd ? idx + 1 : idx;
String nextSubstring = theUrl.substring(nextStart, endIdx);
if (retVal.getResourceType() == null) {
retVal.setResourceType(nextSubstring);
} else if (retVal.getResourceId() == null) {
retVal.setResourceId(nextSubstring);
} else if (nextIsHistory) {
retVal.setVersionId(nextSubstring);
} else {
if (nextSubstring.equals(Constants.URL_TOKEN_HISTORY)) {
nextIsHistory = true;
} else {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl);
throw new InvalidRequestException(msg);
}
}
if (nextChar == '?') {
if (theUrl.length() > idx + 1) {
retVal.setParams(theUrl.substring(idx + 1, theUrl.length()));
}
break;
}
nextStart = idx + 1;
}
}
RuntimeResourceDefinition resType;
try {
resType = getContext().getResourceDefinition(retVal.getResourceType());
} catch (DataFormatException e) {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl);
throw new InvalidRequestException(msg);
}
IFhirResourceDao<? extends IBaseResource> dao = null;
if (resType != null) {
dao = getDao(resType.getImplementingClass());
}
if (dao == null) {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl);
throw new InvalidRequestException(msg);
}
retVal.setDao(dao);
if (retVal.getResourceId() == null && retVal.getParams() == null) {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theAction, theUrl);
throw new InvalidRequestException(msg);
}
return retVal;
}
@Transactional(propagation = Propagation.REQUIRED) @Transactional(propagation = Propagation.REQUIRED)
@Override @Override
public Bundle transaction(Bundle theRequest) { public Bundle transaction(Bundle theRequest) {
@ -265,6 +200,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
ourLog.info("Beginning {} with {} resources", theActionName, theRequest.getEntry().size()); ourLog.info("Beginning {} with {} resources", theActionName, theRequest.getEntry().size());
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
Date updateTime = new Date();
Set<IdDt> allIds = new LinkedHashSet<IdDt>(); Set<IdDt> allIds = new LinkedHashSet<IdDt>();
Map<IdDt, IdDt> idSubstitutions = new HashMap<IdDt, IdDt>(); Map<IdDt, IdDt> idSubstitutions = new HashMap<IdDt, IdDt>();
@ -275,11 +211,11 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
// TODO: process verbs in the correct order // TODO: process verbs in the correct order
for (int i = 0; i < theRequest.getEntry().size(); i++) { for (int i = 0; i < theRequest.getEntry().size(); i++) {
if (i % 100 == 0) { if (i % 100 == 0) {
ourLog.info("Processed {} entries out of {}", i, theRequest.getEntry().size()); ourLog.info("Processed {} entries out of {}", i, theRequest.getEntry().size());
} }
Entry nextEntry = theRequest.getEntry().get(i); Entry nextEntry = theRequest.getEntry().get(i);
IResource res = nextEntry.getResource(); IResource res = nextEntry.getResource();
IdDt nextResourceId = null; IdDt nextResourceId = null;
@ -330,11 +266,12 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
// DELETE // DELETE
Entry newEntry = response.addEntry(); Entry newEntry = response.addEntry();
String url = extractTransactionUrlOrThrowException(nextEntry, verb); String url = extractTransactionUrlOrThrowException(nextEntry, verb);
UrlParts parts = parseUrl(verb.getCode(), url); UrlParts parts = UrlUtil.parseUrl(url);
ca.uhn.fhir.jpa.dao.IFhirResourceDao<? extends IBaseResource> dao = toDao(parts, verb.getCode(), url);
if (parts.getResourceId() != null) { if (parts.getResourceId() != null) {
parts.getDao().delete(new IdDt(parts.getResourceType(), parts.getResourceId())); dao.delete(new IdDt(parts.getResourceType(), parts.getResourceId()));
} else { } else {
parts.getDao().deleteByUrl(parts.getResourceType() + '?' + parts.getParams()); dao.deleteByUrl(parts.getResourceType() + '?' + parts.getParams());
} }
newEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_204_NO_CONTENT)); newEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_204_NO_CONTENT));
@ -350,7 +287,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
String url = extractTransactionUrlOrThrowException(nextEntry, verb); String url = extractTransactionUrlOrThrowException(nextEntry, verb);
UrlParts parts = parseUrl(verb.getCode(), url); UrlParts parts = UrlUtil.parseUrl(url);
if (isNotBlank(parts.getResourceId())) { if (isNotBlank(parts.getResourceId())) {
res.setId(new IdDt(parts.getResourceType(), parts.getResourceId())); res.setId(new IdDt(parts.getResourceType(), parts.getResourceId()));
outcome = resourceDao.update(res, null, false); outcome = resourceDao.update(res, null, false);
@ -365,10 +302,10 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
case GET: { case GET: {
// SEARCH/READ/VREAD // SEARCH/READ/VREAD
String url = extractTransactionUrlOrThrowException(nextEntry, verb); String url = extractTransactionUrlOrThrowException(nextEntry, verb);
UrlParts parts = parseUrl(verb.getCode(), url); UrlParts parts = UrlUtil.parseUrl(url);
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
IFhirResourceDao resourceDao = parts.getDao(); IFhirResourceDao dao = toDao(parts, verb.getCode(), url);
String ifNoneMatch = nextEntry.getRequest().getIfNoneMatch(); String ifNoneMatch = nextEntry.getRequest().getIfNoneMatch();
if (isNotBlank(ifNoneMatch)) { if (isNotBlank(ifNoneMatch)) {
@ -382,9 +319,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
if (isNotBlank(ifNoneMatch)) { if (isNotBlank(ifNoneMatch)) {
throw new InvalidRequestException("Unable to perform vread on '" + url + "' with ifNoneMatch also set. Do not include a version in the URL to perform a conditional read."); throw new InvalidRequestException("Unable to perform vread on '" + url + "' with ifNoneMatch also set. Do not include a version in the URL to perform a conditional read.");
} }
found = (IResource) resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId(), parts.getVersionId())); found = (IResource) dao.read(new IdDt(parts.getResourceType(), parts.getResourceId(), parts.getVersionId()));
} else { } else {
found = (IResource) resourceDao.read(new IdDt(parts.getResourceType(), parts.getResourceId())); found = (IResource) dao.read(new IdDt(parts.getResourceType(), parts.getResourceId()));
if (isNotBlank(ifNoneMatch) && ifNoneMatch.equals(found.getId().getVersionIdPart())) { if (isNotBlank(ifNoneMatch) && ifNoneMatch.equals(found.getId().getVersionIdPart())) {
notChanged = true; notChanged = true;
} }
@ -402,9 +339,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
resp.setStatus(toStatusString(Constants.STATUS_HTTP_304_NOT_MODIFIED)); resp.setStatus(toStatusString(Constants.STATUS_HTTP_304_NOT_MODIFIED));
} }
} else if (parts.getParams() != null) { } else if (parts.getParams() != null) {
RuntimeResourceDefinition def = getContext().getResourceDefinition(parts.getDao().getResourceType()); RuntimeResourceDefinition def = getContext().getResourceDefinition(dao.getResourceType());
SearchParameterMap params = translateMatchUrl(url, def); SearchParameterMap params = translateMatchUrl(url, def);
IBundleProvider bundle = parts.getDao().search(params); IBundleProvider bundle = dao.search(params);
Bundle searchBundle = new Bundle(); Bundle searchBundle = new Bundle();
searchBundle.setTotal(bundle.size()); searchBundle.setTotal(bundle.size());
@ -453,7 +390,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource); InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
updateEntity(nextResource, nextOutcome.getEntity(), false, deletedTimestampOrNull, true, false); updateEntity(nextResource, nextOutcome.getEntity(), false, deletedTimestampOrNull, true, false, updateTime);
} }
myEntityManager.flush(); myEntityManager.flush();
@ -468,8 +405,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
IFhirResourceDao<?> resourceDao = getDao(nextEntry.getResource().getClass()); IFhirResourceDao<?> resourceDao = getDao(nextEntry.getResource().getClass());
Set<Long> val = resourceDao.processMatchUrl(matchUrl); Set<Long> val = resourceDao.processMatchUrl(matchUrl);
if (val.size() > 1) { if (val.size() > 1) {
throw new InvalidRequestException( throw new InvalidRequestException("Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?");
"Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?");
} }
} }
} }
@ -488,13 +424,38 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
long delay = System.currentTimeMillis() - start; long delay = System.currentTimeMillis() - start;
ourLog.info(theActionName + " completed in {}ms", new Object[] { delay }); ourLog.info(theActionName + " completed in {}ms", new Object[] { delay });
notifyWriteCompleted(); notifyWriteCompleted();
response.setType(BundleTypeEnum.TRANSACTION_RESPONSE); response.setType(BundleTypeEnum.TRANSACTION_RESPONSE);
return response; return response;
} }
private ca.uhn.fhir.jpa.dao.IFhirResourceDao<? extends IBaseResource> toDao(UrlParts theParts, String theVerb, String theUrl) {
RuntimeResourceDefinition resType;
try {
resType = getContext().getResourceDefinition(theParts.getResourceType());
} catch (DataFormatException e) {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
throw new InvalidRequestException(msg);
}
IFhirResourceDao<? extends IBaseResource> dao = null;
if (resType != null) {
dao = getDao(resType.getImplementingClass());
}
if (dao == null) {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
throw new InvalidRequestException(msg);
}
if (theParts.getResourceId() == null && theParts.getParams() == null) {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl);
throw new InvalidRequestException(msg);
}
return dao;
}
private IFhirResourceDao<?> getDaoOrThrowException(Class<? extends IResource> theClass) { private IFhirResourceDao<?> getDaoOrThrowException(Class<? extends IResource> theClass) {
IFhirResourceDao<? extends IResource> retVal = getDao(theClass); IFhirResourceDao<? extends IResource> retVal = getDao(theClass);
if (retVal == null) { if (retVal == null) {
@ -503,15 +464,15 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
return retVal; return retVal;
} }
private static void handleTransactionCreateOrUpdateOutcome(Map<IdDt, IdDt> idSubstitutions, Map<IdDt, DaoMethodOutcome> idToPersistedOutcome, IdDt nextResourceId, DaoMethodOutcome outcome, private static void handleTransactionCreateOrUpdateOutcome(Map<IdDt, IdDt> idSubstitutions, Map<IdDt, DaoMethodOutcome> idToPersistedOutcome, IdDt nextResourceId, DaoMethodOutcome outcome, Entry newEntry, String theResourceType, IResource theRes) {
Entry newEntry, String theResourceType, IResource theRes) {
IdDt newId = (IdDt) outcome.getId().toUnqualifiedVersionless(); IdDt newId = (IdDt) outcome.getId().toUnqualifiedVersionless();
IdDt resourceId = isPlaceholder(nextResourceId) ? nextResourceId : nextResourceId.toUnqualifiedVersionless(); IdDt resourceId = isPlaceholder(nextResourceId) ? nextResourceId : nextResourceId.toUnqualifiedVersionless();
if (newId.equals(resourceId) == false) { if (newId.equals(resourceId) == false) {
idSubstitutions.put(resourceId, newId); idSubstitutions.put(resourceId, newId);
if (isPlaceholder(resourceId)) { if (isPlaceholder(resourceId)) {
/* /*
* The correct way for substitution IDs to be is to be with no resource type, but we'll accept the qualified kind too just to be lenient. * The correct way for substitution IDs to be is to be with no resource type, but we'll accept the qualified
* kind too just to be lenient.
*/ */
idSubstitutions.put(new IdDt(theResourceType + '/' + resourceId.getValue()), newId); idSubstitutions.put(new IdDt(theResourceType + '/' + resourceId.getValue()), newId);
} }
@ -538,52 +499,4 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle> {
return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode)); return Integer.toString(theStatusCode) + " " + defaultString(Constants.HTTP_STATUS_NAMES.get(theStatusCode));
} }
private static class UrlParts {
private IFhirResourceDao<? extends IBaseResource> myDao;
private String myParams;
private String myResourceId;
private String myResourceType;
private String myVersionId;
public IFhirResourceDao<? extends IBaseResource> getDao() {
return myDao;
}
public String getParams() {
return myParams;
}
public String getResourceId() {
return myResourceId;
}
public String getResourceType() {
return myResourceType;
}
public String getVersionId() {
return myVersionId;
}
public void setDao(IFhirResourceDao<? extends IBaseResource> theDao) {
myDao = theDao;
}
public void setParams(String theParams) {
myParams = theParams;
}
public void setResourceId(String theResourceId) {
myResourceId = theResourceId;
}
public void setResourceType(String theResourceType) {
myResourceType = theResourceType;
}
public void setVersionId(String theVersionId) {
myVersionId = theVersionId;
}
}
} }

View File

@ -1,5 +1,8 @@
package ca.uhn.fhir.jpa.dao; package ca.uhn.fhir.jpa.dao;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
/* /*
* #%L * #%L
* HAPI FHIR JPA Server * HAPI FHIR JPA Server
@ -22,6 +25,21 @@ package ca.uhn.fhir.jpa.dao;
public interface IDao { public interface IDao {
void registerDaoListener(IDaoListener theListener); public static final ResourceMetadataKeyEnum<Long> RESOURCE_PID = new ResourceMetadataKeyEnum<Long>("RESOURCE_PID") {
private static final long serialVersionUID = 1L;
@Override
public Long get(IResource theResource) {
return (Long) theResource.getResourceMetadata().get(RESOURCE_PID);
}
@Override
public void put(IResource theResource, Long theObject) {
theResource.getResourceMetadata().put(RESOURCE_PID, theObject);
}
};
void registerDaoListener(IDaoListener theListener);
} }

View File

@ -0,0 +1,29 @@
package ca.uhn.fhir.jpa.dao;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2015 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 org.hl7.fhir.instance.model.api.IBaseResource;
public interface IFhirResourceDaoSubscription<T extends IBaseResource> extends IFhirResourceDao<T> {
void pollForNewUndeliveredResources();
}

View File

@ -1,27 +1,6 @@
package ca.uhn.fhir.jpa.dao; package ca.uhn.fhir.jpa.dao;
/* import java.util.Set;
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2015 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 java.util.ArrayList;
import java.util.List;
import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam; import ca.uhn.fhir.jpa.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords;
@ -35,18 +14,18 @@ import ca.uhn.fhir.model.api.IResource;
interface ISearchParamExtractor { interface ISearchParamExtractor {
public abstract List<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IResource theResource); public abstract Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IResource theResource);
public abstract List<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource); public abstract Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource);
public abstract ArrayList<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IResource theResource); public abstract Set<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IResource theResource);
public abstract List<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IResource theResource); public abstract Set<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IResource theResource);
public abstract List<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IResource theResource); public abstract Set<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IResource theResource);
public abstract List<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IResource theResource); public abstract Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IResource theResource);
public abstract List<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IResource theResource); public abstract Set<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IResource theResource);
} }

View File

@ -75,13 +75,13 @@ public class SearchParamExtractorDstu1 extends BaseSearchParamExtractor implemen
} }
@Override @Override
public List<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IResource theResource) { public Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IResource theResource) {
return Collections.emptyList(); return Collections.emptySet();
} }
@Override @Override
public List<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource) { public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceIndexedSearchParamDate> retVal = new ArrayList<ResourceIndexedSearchParamDate>(); HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<ResourceIndexedSearchParamDate>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
@ -135,8 +135,8 @@ public class SearchParamExtractorDstu1 extends BaseSearchParamExtractor implemen
} }
@Override @Override
public ArrayList<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IResource theResource) { public HashSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceIndexedSearchParamNumber> retVal = new ArrayList<ResourceIndexedSearchParamNumber>(); HashSet<ResourceIndexedSearchParamNumber> retVal = new HashSet<ResourceIndexedSearchParamNumber>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
@ -230,8 +230,8 @@ public class SearchParamExtractorDstu1 extends BaseSearchParamExtractor implemen
} }
@Override @Override
public List<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IResource theResource) { public Set<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceIndexedSearchParamQuantity> retVal = new ArrayList<ResourceIndexedSearchParamQuantity>(); HashSet<ResourceIndexedSearchParamQuantity> retVal = new HashSet<ResourceIndexedSearchParamQuantity>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
@ -278,8 +278,8 @@ public class SearchParamExtractorDstu1 extends BaseSearchParamExtractor implemen
} }
@Override @Override
public List<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IResource theResource) { public Set<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceIndexedSearchParamString> retVal = new ArrayList<ResourceIndexedSearchParamString>(); HashSet<ResourceIndexedSearchParamString> retVal = new HashSet<ResourceIndexedSearchParamString>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
@ -364,8 +364,8 @@ public class SearchParamExtractorDstu1 extends BaseSearchParamExtractor implemen
} }
@Override @Override
public List<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IResource theResource) { public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IResource theResource) {
ArrayList<BaseResourceIndexedSearchParam> retVal = new ArrayList<BaseResourceIndexedSearchParam>(); HashSet<BaseResourceIndexedSearchParam> retVal = new HashSet<BaseResourceIndexedSearchParam>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
@ -472,8 +472,8 @@ public class SearchParamExtractorDstu1 extends BaseSearchParamExtractor implemen
} }
@Override @Override
public List<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IResource theResource) { public Set<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IResource theResource) {
return Collections.emptyList(); return Collections.emptySet();
} }
} }

View File

@ -84,7 +84,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
super(theContext); super(theContext);
} }
private void addSearchTerm(ResourceTable theEntity, ArrayList<ResourceIndexedSearchParamString> retVal, String resourceName, String searchTerm) { private void addSearchTerm(ResourceTable theEntity, Set<ResourceIndexedSearchParamString> retVal, String resourceName, String searchTerm) {
if (isBlank(searchTerm)) { if (isBlank(searchTerm)) {
return; return;
} }
@ -97,7 +97,7 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
retVal.add(nextEntity); retVal.add(nextEntity);
} }
private void addStringParam(ResourceTable theEntity, ArrayList<BaseResourceIndexedSearchParam> retVal, RuntimeSearchParam nextSpDef, String value) { private void addStringParam(ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> retVal, RuntimeSearchParam nextSpDef, String value) {
if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) { if (value.length() > ResourceIndexedSearchParamString.MAX_LENGTH) {
value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH); value = value.substring(0, ResourceIndexedSearchParamString.MAX_LENGTH);
} }
@ -107,9 +107,9 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
} }
@Override @Override
public List<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IResource theResource) { public Set<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IResource theResource) {
// TODO: implement // TODO: implement
return Collections.emptyList(); return Collections.emptySet();
} }
/* /*
@ -118,8 +118,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamDates(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource) * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamDates(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public List<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource) { public Set<ResourceIndexedSearchParamDate> extractSearchParamDates(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceIndexedSearchParamDate> retVal = new ArrayList<ResourceIndexedSearchParamDate>(); HashSet<ResourceIndexedSearchParamDate> retVal = new HashSet<ResourceIndexedSearchParamDate>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
@ -178,8 +178,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamNumber(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource) * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamNumber(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public ArrayList<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IResource theResource) { public HashSet<ResourceIndexedSearchParamNumber> extractSearchParamNumber(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceIndexedSearchParamNumber> retVal = new ArrayList<ResourceIndexedSearchParamNumber>(); HashSet<ResourceIndexedSearchParamNumber> retVal = new HashSet<ResourceIndexedSearchParamNumber>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
@ -272,8 +272,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamQuantity(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource) * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamQuantity(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public List<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IResource theResource) { public Set<ResourceIndexedSearchParamQuantity> extractSearchParamQuantity(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceIndexedSearchParamQuantity> retVal = new ArrayList<ResourceIndexedSearchParamQuantity>(); HashSet<ResourceIndexedSearchParamQuantity> retVal = new HashSet<ResourceIndexedSearchParamQuantity>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
@ -326,8 +326,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamStrings(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource) * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamStrings(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public List<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IResource theResource) { public Set<ResourceIndexedSearchParamString> extractSearchParamStrings(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceIndexedSearchParamString> retVal = new ArrayList<ResourceIndexedSearchParamString>(); HashSet<ResourceIndexedSearchParamString> retVal = new HashSet<ResourceIndexedSearchParamString>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
@ -407,8 +407,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
* @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamTokens(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource) * @see ca.uhn.fhir.jpa.dao.ISearchParamExtractor#extractSearchParamTokens(ca.uhn.fhir.jpa.entity.ResourceTable, ca.uhn.fhir.model.api.IResource)
*/ */
@Override @Override
public List<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IResource theResource) { public Set<BaseResourceIndexedSearchParam> extractSearchParamTokens(ResourceTable theEntity, IResource theResource) {
ArrayList<BaseResourceIndexedSearchParam> retVal = new ArrayList<BaseResourceIndexedSearchParam>(); HashSet<BaseResourceIndexedSearchParam> retVal = new HashSet<BaseResourceIndexedSearchParam>();
String useSystem = null; String useSystem = null;
if (theResource instanceof ValueSet) { if (theResource instanceof ValueSet) {
@ -556,8 +556,8 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
} }
@Override @Override
public List<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IResource theResource) { public Set<ResourceIndexedSearchParamUri> extractSearchParamUri(ResourceTable theEntity, IResource theResource) {
ArrayList<ResourceIndexedSearchParamUri> retVal = new ArrayList<ResourceIndexedSearchParamUri>(); HashSet<ResourceIndexedSearchParamUri> retVal = new HashSet<ResourceIndexedSearchParamUri>();
RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource); RuntimeResourceDefinition def = getContext().getResourceDefinition(theResource);
for (RuntimeSearchParam nextSpDef : def.getSearchParams()) { for (RuntimeSearchParam nextSpDef : def.getSearchParams()) {
@ -608,13 +608,13 @@ public class SearchParamExtractorDstu2 extends BaseSearchParamExtractor implemen
private void extractTokensFromCodeableConcept(List<String> theSystems, List<String> theCodes, CodeableConceptDt theCodeableConcept, ResourceTable theEntity, private void extractTokensFromCodeableConcept(List<String> theSystems, List<String> theCodes, CodeableConceptDt theCodeableConcept, ResourceTable theEntity,
ArrayList<BaseResourceIndexedSearchParam> theListToPopulate, RuntimeSearchParam theParameterDef) { Set<BaseResourceIndexedSearchParam> theListToPopulate, RuntimeSearchParam theParameterDef) {
for (CodingDt nextCoding : theCodeableConcept.getCoding()) { for (CodingDt nextCoding : theCodeableConcept.getCoding()) {
extractTokensFromCoding(theSystems, theCodes, theEntity, theListToPopulate, theParameterDef, nextCoding); extractTokensFromCoding(theSystems, theCodes, theEntity, theListToPopulate, theParameterDef, nextCoding);
} }
} }
private void extractTokensFromCoding(List<String> theSystems, List<String> theCodes, ResourceTable theEntity, ArrayList<BaseResourceIndexedSearchParam> theListToPopulate, private void extractTokensFromCoding(List<String> theSystems, List<String> theCodes, ResourceTable theEntity, Set<BaseResourceIndexedSearchParam> theListToPopulate,
RuntimeSearchParam theParameterDef, CodingDt nextCoding) { RuntimeSearchParam theParameterDef, CodingDt nextCoding) {
if (nextCoding != null && !nextCoding.isEmpty()) { if (nextCoding != null && !nextCoding.isEmpty()) {

View File

@ -21,8 +21,8 @@ package ca.uhn.fhir.jpa.dao;
*/ */
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -36,7 +36,7 @@ import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.DateRangeParam;
public class SearchParameterMap extends HashMap<String, List<List<? extends IQueryParameterType>>> { public class SearchParameterMap extends LinkedHashMap<String, List<List<? extends IQueryParameterType>>> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;

View File

@ -0,0 +1,9 @@
package ca.uhn.fhir.jpa.dao.data;
import org.springframework.data.repository.CrudRepository;
import ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource;
public interface ISubscriptionFlaggedResourceDataDao extends CrudRepository<SubscriptionFlaggedResource, Long> {
}

View File

@ -98,7 +98,11 @@ public abstract class BaseHasResource {
public abstract IdDt getIdDt(); public abstract IdDt getIdDt();
public InstantDt getPublished() { public InstantDt getPublished() {
return new InstantDt(myPublished); if (myPublished != null) {
return new InstantDt(myPublished);
} else {
return null;
}
} }
public byte[] getResource() { public byte[] getResource() {

View File

@ -24,6 +24,11 @@ import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
//@formatter:off //@formatter:off
@Entity @Entity
@Table(name = "HFJ_SPIDX_COORDS" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */) @Table(name = "HFJ_SPIDX_COORDS" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */)
@ -52,20 +57,59 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP
setLongitude(theLongitude); setLongitude(theLongitude);
} }
public double getLatitude() { @Override
return myLatitude; public boolean equals(Object theObj) {
if (this == theObj) {
return true;
}
if (theObj == null) {
return false;
}
if (!(theObj instanceof ResourceIndexedSearchParamCoords)) {
return false;
}
ResourceIndexedSearchParamCoords obj = (ResourceIndexedSearchParamCoords) theObj;
EqualsBuilder b = new EqualsBuilder();
b.append(getParamName(), obj.getParamName());
b.append(getResource(), obj.getResource());
b.append(getLatitude(), obj.getLatitude());
b.append(getLongitude(), obj.getLongitude());
return b.isEquals();
} }
public void setLatitude(double theLatitude) { public double getLatitude() {
myLatitude = theLatitude; return myLatitude;
} }
public double getLongitude() { public double getLongitude() {
return myLongitude; return myLongitude;
} }
@Override
public int hashCode() {
HashCodeBuilder b = new HashCodeBuilder();
b.append(getParamName());
b.append(getResource());
b.append(getLatitude());
b.append(getLongitude());
return b.toHashCode();
}
public void setLatitude(double theLatitude) {
myLatitude = theLatitude;
}
public void setLongitude(double theLongitude) { public void setLongitude(double theLongitude) {
myLongitude = theLongitude; myLongitude = theLongitude;
} }
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
b.append("paramName", getParamName());
b.append("resourceId", getResource().getId()); // TODO: add a field so we don't need to resolve this
b.append("lat", getLatitude());
b.append("lon", getLongitude());
return b.build();
}
} }

View File

@ -28,6 +28,11 @@ import javax.persistence.Table;
import javax.persistence.Temporal; import javax.persistence.Temporal;
import javax.persistence.TemporalType; import javax.persistence.TemporalType;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
//@formatter:off //@formatter:off
@Entity @Entity
@Table(name = "HFJ_SPIDX_DATE" /*, indexes= {@Index(name="IDX_SP_DATE", columnList= "SP_VALUE_LOW,SP_VALUE_HIGH")}*/) @Table(name = "HFJ_SPIDX_DATE" /*, indexes= {@Index(name="IDX_SP_DATE", columnList= "SP_VALUE_LOW,SP_VALUE_HIGH")}*/)
@ -47,18 +52,34 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
@Temporal(TemporalType.TIMESTAMP) @Temporal(TemporalType.TIMESTAMP)
public Date myValueLow; public Date myValueLow;
public ResourceIndexedSearchParamDate() { public ResourceIndexedSearchParamDate() {
} }
public ResourceIndexedSearchParamDate(String theName, Date theLow, Date theHigh) { public ResourceIndexedSearchParamDate(String theName, Date theLow, Date theHigh) {
setParamName(theName); setParamName(theName);
setValueLow(theLow); setValueLow(theLow);
setValueHigh(theHigh); setValueHigh(theHigh);
} }
@Override
public boolean equals(Object theObj) {
if (this == theObj) {
return true;
}
if (theObj == null) {
return false;
}
if (!(theObj instanceof ResourceIndexedSearchParamDate)) {
return false;
}
ResourceIndexedSearchParamDate obj = (ResourceIndexedSearchParamDate) theObj;
EqualsBuilder b = new EqualsBuilder();
b.append(getParamName(), obj.getParamName());
b.append(getResource(), obj.getResource());
b.append(getValueHigh(), obj.getValueHigh());
b.append(getValueLow(), obj.getValueLow());
return b.isEquals();
}
public Date getValueHigh() { public Date getValueHigh() {
return myValueHigh; return myValueHigh;
@ -68,6 +89,16 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
return myValueLow; return myValueLow;
} }
@Override
public int hashCode() {
HashCodeBuilder b = new HashCodeBuilder();
b.append(getParamName());
b.append(getResource());
b.append(getValueHigh());
b.append(getValueLow());
return b.toHashCode();
}
public void setValueHigh(Date theValueHigh) { public void setValueHigh(Date theValueHigh) {
myValueHigh = theValueHigh; myValueHigh = theValueHigh;
} }
@ -76,6 +107,13 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
myValueLow = theValueLow; myValueLow = theValueLow;
} }
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
b.append("paramName", getParamName());
b.append("resourceId", getResource().getId()); // TODO: add a field so we don't need to resolve this
b.append("valueLow", getValueLow());
b.append("valueHigh", getValueHigh());
return b.build();
}
} }

View File

@ -26,6 +26,11 @@ import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
//@formatter:off //@formatter:off
@Entity @Entity
@Table(name = "HFJ_SPIDX_NUMBER" /*, indexes= {@Index(name="IDX_SP_NUMBER", columnList="SP_VALUE")}*/ ) @Table(name = "HFJ_SPIDX_NUMBER" /*, indexes= {@Index(name="IDX_SP_NUMBER", columnList="SP_VALUE")}*/ )
@ -39,21 +44,57 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
@Column(name = "SP_VALUE", nullable = true) @Column(name = "SP_VALUE", nullable = true)
public BigDecimal myValue; public BigDecimal myValue;
public ResourceIndexedSearchParamNumber() { public ResourceIndexedSearchParamNumber() {
} }
public ResourceIndexedSearchParamNumber(String theParamName, BigDecimal theValue) { public ResourceIndexedSearchParamNumber(String theParamName, BigDecimal theValue) {
setParamName(theParamName); setParamName(theParamName);
setValue(theValue); setValue(theValue);
} }
@Override
public boolean equals(Object theObj) {
if (this == theObj) {
return true;
}
if (theObj == null) {
return false;
}
if (!(theObj instanceof ResourceIndexedSearchParamNumber)) {
return false;
}
ResourceIndexedSearchParamNumber obj = (ResourceIndexedSearchParamNumber) theObj;
EqualsBuilder b = new EqualsBuilder();
b.append(getParamName(), obj.getParamName());
b.append(getResource(), obj.getResource());
b.append(getValue(), obj.getValue());
return b.isEquals();
}
public BigDecimal getValue() { public BigDecimal getValue() {
return myValue; return myValue;
} }
@Override
public int hashCode() {
HashCodeBuilder b = new HashCodeBuilder();
b.append(getParamName());
b.append(getResource());
b.append(getValue());
return b.toHashCode();
}
public void setValue(BigDecimal theValue) { public void setValue(BigDecimal theValue) {
myValue = theValue; myValue = theValue;
} }
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
b.append("paramName", getParamName());
b.append("resourceId", getResource().getId()); // TODO: add a field so we don't need to resolve this
b.append("value", getValue());
return b.build();
}
} }

View File

@ -26,6 +26,11 @@ import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
//@formatter:off //@formatter:off
@Entity @Entity
@Table(name = "HFJ_SPIDX_QUANTITY" /*, indexes= {@Index(name="IDX_SP_NUMBER", columnList="SP_VALUE")}*/ ) @Table(name = "HFJ_SPIDX_QUANTITY" /*, indexes= {@Index(name="IDX_SP_NUMBER", columnList="SP_VALUE")}*/ )
@ -47,9 +52,9 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
public BigDecimal myValue; public BigDecimal myValue;
public ResourceIndexedSearchParamQuantity() { public ResourceIndexedSearchParamQuantity() {
//nothing // nothing
} }
public ResourceIndexedSearchParamQuantity(String theParamName, BigDecimal theValue, String theSystem, String theUnits) { public ResourceIndexedSearchParamQuantity(String theParamName, BigDecimal theValue, String theSystem, String theUnits) {
setParamName(theParamName); setParamName(theParamName);
setSystem(theSystem); setSystem(theSystem);
@ -57,6 +62,27 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
setUnits(theUnits); setUnits(theUnits);
} }
@Override
public boolean equals(Object theObj) {
if (this == theObj) {
return true;
}
if (theObj == null) {
return false;
}
if (!(theObj instanceof ResourceIndexedSearchParamQuantity)) {
return false;
}
ResourceIndexedSearchParamQuantity obj = (ResourceIndexedSearchParamQuantity) theObj;
EqualsBuilder b = new EqualsBuilder();
b.append(getParamName(), obj.getParamName());
b.append(getResource(), obj.getResource());
b.append(getSystem(), obj.getSystem());
b.append(getUnits(), obj.getUnits());
b.append(getValue(), obj.getValue());
return b.isEquals();
}
public String getSystem() { public String getSystem() {
return mySystem; return mySystem;
} }
@ -69,6 +95,16 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
return myValue; return myValue;
} }
@Override
public int hashCode() {
HashCodeBuilder b = new HashCodeBuilder();
b.append(getParamName());
b.append(getResource());
b.append(getSystem());
b.append(getUnits());
b.append(getValue());
return b.toHashCode();
}
public void setSystem(String theSystem) { public void setSystem(String theSystem) {
mySystem = theSystem; mySystem = theSystem;
@ -82,4 +118,15 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
myValue = theValue; myValue = theValue;
} }
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
b.append("paramName", getParamName());
b.append("resourceId", getResource().getId()); // TODO: add a field so we don't need to resolve this
b.append("system", getSystem());
b.append("units", getUnits());
b.append("value", getValue());
return b.build();
}
} }

View File

@ -25,25 +25,26 @@ import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
@Entity @Entity
@Table(name = "HFJ_SPIDX_STRING"/*, indexes= {@Index(name="IDX_SP_STRING", columnList="SP_VALUE_NORMALIZED")}*/) @Table(name = "HFJ_SPIDX_STRING"/* , indexes= {@Index(name="IDX_SP_STRING", columnList="SP_VALUE_NORMALIZED")} */)
@org.hibernate.annotations.Table(appliesTo="HFJ_SPIDX_STRING",indexes= { @org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_STRING", indexes = { @org.hibernate.annotations.Index(name = "IDX_SP_STRING", columnNames = { "RES_TYPE", "SP_NAME", "SP_VALUE_NORMALIZED" }) })
@org.hibernate.annotations.Index(name="IDX_SP_STRING", columnNames= {"RES_TYPE", "SP_NAME", "SP_VALUE_NORMALIZED"})})
public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchParam { public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchParam {
public static final int MAX_LENGTH = 100; public static final int MAX_LENGTH = 100;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Column(name = "SP_VALUE_EXACT", length = 100, nullable = true)
public String myValueExact;
@Column(name = "SP_VALUE_NORMALIZED", length = MAX_LENGTH, nullable = true) @Column(name = "SP_VALUE_NORMALIZED", length = MAX_LENGTH, nullable = true)
public String myValueNormalized; public String myValueNormalized;
@Column(name="SP_VALUE_EXACT",length=100,nullable=true)
public String myValueExact;
public ResourceIndexedSearchParamString() { public ResourceIndexedSearchParamString() {
} }
@ -53,21 +54,42 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
setValueExact(theValueExact); setValueExact(theValueExact);
} }
public String getValueNormalized() { @Override
return myValueNormalized; public boolean equals(Object theObj) {
} if (this == theObj) {
return true;
public void setValueNormalized(String theValueNormalized) {
if (StringUtils.defaultString(theValueNormalized).length() > MAX_LENGTH) {
throw new IllegalArgumentException("Value is too long: " + theValueNormalized.length());
} }
myValueNormalized = theValueNormalized; if (theObj == null) {
return false;
}
if (!(theObj instanceof ResourceIndexedSearchParamString)) {
return false;
}
ResourceIndexedSearchParamString obj = (ResourceIndexedSearchParamString) theObj;
EqualsBuilder b = new EqualsBuilder();
b.append(getParamName(), obj.getParamName());
b.append(getResource(), obj.getResource());
b.append(getValueExact(), obj.getValueExact());
return b.isEquals();
} }
public String getValueExact() { public String getValueExact() {
return myValueExact; return myValueExact;
} }
public String getValueNormalized() {
return myValueNormalized;
}
@Override
public int hashCode() {
HashCodeBuilder b = new HashCodeBuilder();
b.append(getParamName());
b.append(getResource());
b.append(getValueExact());
return b.toHashCode();
}
public void setValueExact(String theValueExact) { public void setValueExact(String theValueExact) {
if (StringUtils.defaultString(theValueExact).length() > MAX_LENGTH) { if (StringUtils.defaultString(theValueExact).length() > MAX_LENGTH) {
throw new IllegalArgumentException("Value is too long: " + theValueExact.length()); throw new IllegalArgumentException("Value is too long: " + theValueExact.length());
@ -75,6 +97,13 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
myValueExact = theValueExact; myValueExact = theValueExact;
} }
public void setValueNormalized(String theValueNormalized) {
if (StringUtils.defaultString(theValueNormalized).length() > MAX_LENGTH) {
throw new IllegalArgumentException("Value is too long: " + theValueNormalized.length());
}
myValueNormalized = theValueNormalized;
}
@Override @Override
public String toString() { public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
@ -83,5 +112,4 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
b.append("value", getValueNormalized()); b.append("value", getValueNormalized());
return b.build(); return b.build();
} }
} }

View File

@ -25,13 +25,15 @@ import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@Entity @Entity
@Table(name = "HFJ_SPIDX_TOKEN" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */) @Table(name = "HFJ_SPIDX_TOKEN" /* , indexes = { @Index(name = "IDX_SP_TOKEN", columnList = "SP_SYSTEM,SP_VALUE") } */)
@org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_TOKEN", indexes = { @org.hibernate.annotations.Table(appliesTo = "HFJ_SPIDX_TOKEN", indexes = { @org.hibernate.annotations.Index(name = "IDX_SP_TOKEN", columnNames = { "RES_TYPE", "SP_NAME", "SP_SYSTEM", "SP_VALUE" }),
@org.hibernate.annotations.Index(name = "IDX_SP_TOKEN", columnNames = { "RES_TYPE", "SP_NAME", "SP_SYSTEM", "SP_VALUE" }), @org.hibernate.annotations.Index(name = "IDX_SP_TOKEN_UNQUAL", columnNames = { "RES_TYPE", "SP_NAME", "SP_VALUE" }) })
@org.hibernate.annotations.Index(name = "IDX_SP_TOKEN_UNQUAL", columnNames = { "RES_TYPE", "SP_NAME", "SP_VALUE" })
})
public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam { public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam {
public static final int MAX_LENGTH = 100; public static final int MAX_LENGTH = 100;
@ -53,6 +55,26 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
setValue(theValue); setValue(theValue);
} }
@Override
public boolean equals(Object theObj) {
if (this == theObj) {
return true;
}
if (theObj == null) {
return false;
}
if (!(theObj instanceof ResourceIndexedSearchParamToken)) {
return false;
}
ResourceIndexedSearchParamToken obj = (ResourceIndexedSearchParamToken) theObj;
EqualsBuilder b = new EqualsBuilder();
b.append(getParamName(), obj.getParamName());
b.append(getResource(), obj.getResource());
b.append(getSystem(), obj.getSystem());
b.append(getValue(), obj.getValue());
return b.isEquals();
}
public String getSystem() { public String getSystem() {
return mySystem; return mySystem;
} }
@ -61,6 +83,16 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
return myValue; return myValue;
} }
@Override
public int hashCode() {
HashCodeBuilder b = new HashCodeBuilder();
b.append(getParamName());
b.append(getResource());
b.append(getSystem());
b.append(getValue());
return b.toHashCode();
}
public void setSystem(String theSystem) { public void setSystem(String theSystem) {
mySystem = StringUtils.defaultIfBlank(theSystem, null); mySystem = StringUtils.defaultIfBlank(theSystem, null);
} }
@ -69,4 +101,13 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
myValue = StringUtils.defaultIfBlank(theValue, null); myValue = StringUtils.defaultIfBlank(theValue, null);
} }
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
b.append("paramName", getParamName());
b.append("resourceId", getResource().getId()); // TODO: add a field so we don't need to resolve this
b.append("system", getSystem());
b.append("value", getValue());
return b.build();
}
} }

View File

@ -25,6 +25,8 @@ import javax.persistence.Entity;
import javax.persistence.Table; import javax.persistence.Table;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
//@formatter:off //@formatter:off
@ -51,10 +53,38 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
setUri(theUri); setUri(theUri);
} }
@Override
public boolean equals(Object theObj) {
if (this == theObj) {
return true;
}
if (theObj == null) {
return false;
}
if (!(theObj instanceof ResourceIndexedSearchParamUri)) {
return false;
}
ResourceIndexedSearchParamUri obj = (ResourceIndexedSearchParamUri) theObj;
EqualsBuilder b = new EqualsBuilder();
b.append(getParamName(), obj.getParamName());
b.append(getResource(), obj.getResource());
b.append(getUri(), obj.getUri());
return b.isEquals();
}
public String getUri() { public String getUri() {
return myUri; return myUri;
} }
@Override
public int hashCode() {
HashCodeBuilder b = new HashCodeBuilder();
b.append(getParamName());
b.append(getResource());
b.append(getUri());
return b.toHashCode();
}
public void setUri(String theUri) { public void setUri(String theUri) {
myUri = StringUtils.defaultIfBlank(theUri, null); myUri = StringUtils.defaultIfBlank(theUri, null);
} }

View File

@ -32,11 +32,12 @@ import javax.persistence.ManyToOne;
import javax.persistence.Table; import javax.persistence.Table;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
@Entity @Entity
@Table(name = "HFJ_RES_LINK"/*, indexes= {@Index(name="IDX_RL_TPATHRES", columnList= "SRC_PATH,TARGET_RESOURCE_ID")}*/) @Table(name = "HFJ_RES_LINK"/* , indexes= {@Index(name="IDX_RL_TPATHRES", columnList= "SRC_PATH,TARGET_RESOURCE_ID")} */)
@org.hibernate.annotations.Table(appliesTo="HFJ_RES_LINK",indexes= { @org.hibernate.annotations.Table(appliesTo = "HFJ_RES_LINK", indexes = { @org.hibernate.annotations.Index(name = "IDX_RL_TPATHRES", columnNames = { "SRC_PATH", "TARGET_RESOURCE_ID" }) })
@org.hibernate.annotations.Index(name="IDX_RL_TPATHRES", columnNames= {"SRC_PATH", "TARGET_RESOURCE_ID"})})
public class ResourceLink implements Serializable { public class ResourceLink implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -50,33 +51,21 @@ public class ResourceLink implements Serializable {
private String mySourcePath; private String mySourcePath;
@ManyToOne(optional = false) @ManyToOne(optional = false)
@JoinColumn(name = "SRC_RESOURCE_ID", referencedColumnName="RES_ID", nullable=false) @JoinColumn(name = "SRC_RESOURCE_ID", referencedColumnName = "RES_ID", nullable = false)
private ResourceTable mySourceResource; private ResourceTable mySourceResource;
@Column(name = "SRC_RESOURCE_ID", insertable = false, updatable = false, nullable=false) @Column(name = "SRC_RESOURCE_ID", insertable = false, updatable = false, nullable = false)
private Long mySourceResourcePid; private Long mySourceResourcePid;
@ManyToOne(optional = false) @ManyToOne(optional = false)
@JoinColumn(name = "TARGET_RESOURCE_ID", referencedColumnName="RES_ID", nullable=false) @JoinColumn(name = "TARGET_RESOURCE_ID", referencedColumnName = "RES_ID", nullable = false)
private ResourceTable myTargetResource; private ResourceTable myTargetResource;
@Column(name = "TARGET_RESOURCE_ID", insertable = false, updatable = false,nullable=false) @Column(name = "TARGET_RESOURCE_ID", insertable = false, updatable = false, nullable = false)
private Long myTargetResourcePid; private Long myTargetResourcePid;
public ResourceLink() { public ResourceLink() {
//nothing // nothing
}
@Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append("ResourceLink[");
b.append("path=").append(mySourcePath);
b.append(", src=").append(mySourceResource.getId());
b.append(", target=").append(myTargetResource.getId());
b.append("]");
return b.toString();
} }
public ResourceLink(String theSourcePath, ResourceTable theSourceResource, ResourceTable theTargetResource) { public ResourceLink(String theSourcePath, ResourceTable theSourceResource, ResourceTable theTargetResource) {
@ -86,6 +75,25 @@ public class ResourceLink implements Serializable {
myTargetResource = theTargetResource; myTargetResource = theTargetResource;
} }
@Override
public boolean equals(Object theObj) {
if (this == theObj) {
return true;
}
if (theObj == null) {
return false;
}
if (!(theObj instanceof ResourceLink)) {
return false;
}
ResourceLink obj = (ResourceLink) theObj;
EqualsBuilder b = new EqualsBuilder();
b.append(mySourcePath, obj.mySourcePath);
b.append(mySourceResource, obj.mySourceResource);
b.append(myTargetResource, obj.myTargetResource);
return b.isEquals();
}
public String getSourcePath() { public String getSourcePath() {
return mySourcePath; return mySourcePath;
} }
@ -106,6 +114,15 @@ public class ResourceLink implements Serializable {
return myTargetResourcePid; return myTargetResourcePid;
} }
@Override
public int hashCode() {
HashCodeBuilder b = new HashCodeBuilder();
b.append(mySourcePath);
b.append(mySourceResource);
b.append(myTargetResource);
return b.toHashCode();
}
public void setSourcePath(String theSourcePath) { public void setSourcePath(String theSourcePath) {
mySourcePath = theSourcePath; mySourcePath = theSourcePath;
} }
@ -114,17 +131,21 @@ public class ResourceLink implements Serializable {
mySourceResource = theSourceResource; mySourceResource = theSourceResource;
} }
public void setSourceResourcePid(Long theSourceResourcePid) {
mySourceResourcePid = theSourceResourcePid;
}
public void setTargetResource(ResourceTable theTargetResource) { public void setTargetResource(ResourceTable theTargetResource) {
Validate.notNull(theTargetResource); Validate.notNull(theTargetResource);
myTargetResource = theTargetResource; myTargetResource = theTargetResource;
} }
public void setTargetResourcePid(Long theTargetResourcePid) { @Override
myTargetResourcePid = theTargetResourcePid; public String toString() {
StringBuilder b = new StringBuilder();
b.append("ResourceLink[");
b.append("path=").append(mySourcePath);
b.append(", src=").append(mySourceResource.getId());
b.append(", target=").append(myTargetResource.getId());
b.append("]");
return b.toString();
} }
} }

View File

@ -19,14 +19,12 @@ package ca.uhn.fhir.jpa.entity;
* limitations under the License. * limitations under the License.
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.*;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import javax.persistence.CascadeType; import javax.persistence.CascadeType;
@ -329,7 +327,7 @@ public class ResourceTable extends BaseHasResource implements Serializable {
myParamsDatePopulated = theParamsDatePopulated; myParamsDatePopulated = theParamsDatePopulated;
} }
public void setParamsNumber(List<ResourceIndexedSearchParamNumber> theNumberParams) { public void setParamsNumber(Collection<ResourceIndexedSearchParamNumber> theNumberParams) {
if (!isParamsNumberPopulated() && theNumberParams.isEmpty()) { if (!isParamsNumberPopulated() && theNumberParams.isEmpty()) {
return; return;
} }
@ -396,7 +394,7 @@ public class ResourceTable extends BaseHasResource implements Serializable {
myProfile = theProfile; myProfile = theProfile;
} }
public void setResourceLinks(List<ResourceLink> theLinks) { public void setResourceLinks(Collection<ResourceLink> theLinks) {
if (!isHasLinks() && theLinks.isEmpty()) { if (!isHasLinks() && theLinks.isEmpty()) {
return; return;
} }

View File

@ -0,0 +1,47 @@
package ca.uhn.fhir.jpa.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
@Entity
@Table(name = "HFJ_SUBSCRIPTION_FLAG_RES")
public class SubscriptionFlaggedResource {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@SequenceGenerator(name = "SEQ_SUBSCRIPTION_FLAG_ID", sequenceName = "SEQ_SUBSCRIPTION_FLAG_ID")
@Column(name = "PID", insertable = false, updatable = false)
private Long myId;
@ManyToOne
@JoinColumn(name="RES_ID")
private ResourceTable myResource;
@ManyToOne()
@JoinColumn(name="SUBSCRIPTION_ID")
private SubscriptionTable mySubscription;
public ResourceTable getResource() {
return myResource;
}
public SubscriptionTable getSubscription() {
return mySubscription;
}
public void setResource(ResourceTable theResource) {
myResource = theResource;
}
public void setSubscription(SubscriptionTable theSubscription) {
mySubscription = theSubscription;
}
}

View File

@ -0,0 +1,117 @@
package ca.uhn.fhir.jpa.entity;
import java.util.Collection;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.ForeignKey;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
//@formatter:off
@Entity
@Table(name = "HFJ_SUBSCRIPTION", uniqueConstraints= {
@UniqueConstraint(name="IDX_SUBS_RESID", columnNames= { "RES_ID" }),
@UniqueConstraint(name="IDX_SUBS_NEXTCHECK", columnNames= { "SUBSCRIPTION_STATUS", "NEXT_CHECK" })
})
@NamedQueries({
@NamedQuery(name="Q_HFJ_SUBSCRIPTION_SET_STATUS", query="UPDATE SubscriptionTable t SET t.myStatus = :status WHERE t.myResId = :res_id"),
@NamedQuery(name="Q_HFJ_SUBSCRIPTION_NEXT_CHECK", query="SELECT t FROM SubscriptionTable t WHERE t.myStatus = :status AND t.myNextCheck <= :next_check"),
@NamedQuery(name="Q_HFJ_SUBSCRIPTION_GET_BY_RES", query="SELECT t FROM SubscriptionTable t WHERE t.myResId = :res_id"),
@NamedQuery(name="Q_HFJ_SUBSCRIPTION_DELETE", query="DELETE FROM SubscriptionTable t WHERE t.myResId = :res_id"),
})
//@formatter:on
public class SubscriptionTable {
@Column(name = "CHECK_INTERVAL", nullable = false)
private long myCheckInterval;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@SequenceGenerator(name = "SEQ_SUBSCRIPTION_ID", sequenceName = "SEQ_SUBSCRIPTION_ID")
@Column(name = "PID", insertable = false, updatable = false)
private Long myId;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "NEXT_CHECK", nullable = false)
private Date myNextCheck;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "MOST_RECENT_MATCH", nullable = false)
private Date myMostRecentMatch;
@Column(name = "RES_ID", insertable = false, updatable = false)
private Long myResId;
@Column(name = "SUBSCRIPTION_STATUS", nullable = false, length = 20)
@Enumerated(EnumType.STRING)
private SubscriptionStatusEnum myStatus;
@OneToOne()
@JoinColumn(name = "RES_ID", insertable = true, updatable = false, referencedColumnName = "RES_ID", foreignKey = @ForeignKey(name = "FK_SUBSCRIPTION_RESOURCE_ID") )
private ResourceTable mySubscriptionResource;
@OneToMany(orphanRemoval=true, mappedBy="mySubscription")
private Collection<SubscriptionFlaggedResource> myFlaggedResources;
public long getCheckInterval() {
return myCheckInterval;
}
public Long getId() {
return myId;
}
public Date getNextCheck() {
return myNextCheck;
}
public SubscriptionStatusEnum getStatus() {
return myStatus;
}
public ResourceTable getSubscriptionResource() {
return mySubscriptionResource;
}
public void setCheckInterval(long theCheckInterval) {
myCheckInterval = theCheckInterval;
}
public void setNextCheck(Date theNextCheck) {
myNextCheck = theNextCheck;
}
public void setStatus(SubscriptionStatusEnum theStatus) {
myStatus = theStatus;
}
public void setSubscriptionResource(ResourceTable theSubscriptionResource) {
mySubscriptionResource = theSubscriptionResource;
}
public Date getMostRecentMatch() {
return myMostRecentMatch;
}
public void setMostRecentMatch(Date theMostRecentMatch) {
myMostRecentMatch = theMostRecentMatch;
}
}

View File

@ -24,16 +24,9 @@ import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome.BaseIssue;
import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.composite.MetaDt;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome.Issue;
import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.Parameters;
import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum;
import ca.uhn.fhir.model.dstu2.valueset.IssueTypeEnum;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.IParserErrorHandler;
import ca.uhn.fhir.rest.annotation.ConditionalUrlParam; import ca.uhn.fhir.rest.annotation.ConditionalUrlParam;
import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.Delete; import ca.uhn.fhir.rest.annotation.Delete;
@ -46,16 +39,13 @@ import ca.uhn.fhir.rest.annotation.Validate;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.ValidationModeEnum; import ca.uhn.fhir.rest.api.ValidationModeEnum;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.validation.FhirValidator;
import ca.uhn.fhir.validation.ValidationResult;
public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResourceProvider<T> { public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResourceProvider<T> {
public static final String OPERATION_NAME_META = "$meta"; public static final String OPERATION_NAME_META = "$meta";
public static final String OPERATION_NAME_META_DELETE = "$meta-delete"; public static final String OPERATION_NAME_META_DELETE = "$meta-delete";
public static final String OPERATION_NAME_META_ADD = "$meta-add"; public static final String OPERATION_NAME_META_ADD = "$meta-add";
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JpaResourceProviderDstu2.class);
public JpaResourceProviderDstu2() { public JpaResourceProviderDstu2() {
// nothing // nothing
@ -123,6 +113,9 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
}) })
//@formatter:on //@formatter:on
public Parameters metaAdd(@IdParam IdDt theId, @OperationParam(name = "meta") MetaDt theMeta) { public Parameters metaAdd(@IdParam IdDt theId, @OperationParam(name = "meta") MetaDt theMeta) {
if (theMeta == null) {
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
}
Parameters parameters = new Parameters(); Parameters parameters = new Parameters();
MetaDt metaAddOperation = getDao().metaAddOperation(theId, theMeta); MetaDt metaAddOperation = getDao().metaAddOperation(theId, theMeta);
parameters.addParameter().setName("return").setValue(metaAddOperation); parameters.addParameter().setName("return").setValue(metaAddOperation);
@ -135,6 +128,9 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
}) })
//@formatter:on //@formatter:on
public Parameters metaDelete(@IdParam IdDt theId, @OperationParam(name = "meta") MetaDt theMeta) { public Parameters metaDelete(@IdParam IdDt theId, @OperationParam(name = "meta") MetaDt theMeta) {
if (theMeta == null) {
throw new InvalidRequestException("Input contains no parameter with name 'meta'");
}
Parameters parameters = new Parameters(); Parameters parameters = new Parameters();
parameters.addParameter().setName("return").setValue(getDao().metaDeleteOperation(theId, theMeta)); parameters.addParameter().setName("return").setValue(getDao().metaDeleteOperation(theId, theMeta));
return parameters; return parameters;
@ -150,10 +146,6 @@ public class JpaResourceProviderDstu2<T extends IResource> extends BaseJpaResour
theResource.setId(theId); theResource.setId(theId);
return getDao().update(theResource); return getDao().update(theResource);
} }
} catch (ResourceNotFoundException e) {
ourLog.info("Can't update resource with ID[" + theId.getValue() + "] because it doesn't exist, going to create it instead");
theResource.setId(theId);
return getDao().create(theResource);
} finally { } finally {
endRequest(theRequest); endRequest(theRequest);
} }

View File

@ -0,0 +1,16 @@
package ca.uhn.fhir.jpa.util;
import org.hibernate.dialect.DerbyTenSevenDialect;
/**
* As of Hibernate 5.0.1, DerbyTenSevenDialect doesn't seem to work when updating
* the schema, as it tries to create a duplicate schema
*/
public class HapiDerbyTenSevenDialect extends DerbyTenSevenDialect {
@Override
public String getQuerySequencesString() {
return "select SEQUENCENAME from sys.syssequences";
}
}

View File

@ -0,0 +1,103 @@
package ca.uhn.fhir.jpa.util;
import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import ca.uhn.fhir.jpa.dao.FhirResourceDaoSubscriptionDstu2;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.dstu2.resource.Subscription;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
import net.sourceforge.cobertura.CoverageIgnore;
/**
* Interceptor which requires newly created {@link Subscription subscriptions} to be in
* {@link SubscriptionStatusEnum#REQUESTED} state and prevents clients from changing the status.
*/
public class SubscriptionsRequireManualActivationInterceptor extends InterceptorAdapter {
public static final ResourceMetadataKeyEnum<Object> ALLOW_STATUS_CHANGE = new ResourceMetadataKeyEnum<Object>(FhirResourceDaoSubscriptionDstu2.class.getName() + "_ALLOW_STATUS_CHANGE") {
private static final long serialVersionUID = 1;
@CoverageIgnore
@Override
public Object get(IResource theResource) {
throw new UnsupportedOperationException();
}
@CoverageIgnore
@Override
public void put(IResource theResource, Object theObject) {
throw new UnsupportedOperationException();
}
};
@Autowired
@Qualifier("mySubscriptionDaoDstu2")
private IFhirResourceDao<Subscription> myDao;
@Override
public void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest) {
switch (theOperation) {
case CREATE:
case UPDATE:
if (theProcessedRequest.getResourceType().equals("Subscription")) {
verifyStatusOk(theProcessedRequest);
}
default:
break;
}
}
public void setDao(IFhirResourceDao<Subscription> theDao) {
myDao = theDao;
}
private void verifyStatusOk(ActionRequestDetails theRequestDetails) {
Subscription subscription = (Subscription) theRequestDetails.getResource();
;
SubscriptionStatusEnum newStatus = subscription.getStatusElement().getValueAsEnum();
if (newStatus == SubscriptionStatusEnum.REQUESTED || newStatus == SubscriptionStatusEnum.OFF) {
return;
}
IIdType requestId = theRequestDetails.getId();
if (requestId != null && requestId.hasIdPart()) {
Subscription existing;
try {
existing = myDao.read(requestId);
SubscriptionStatusEnum existingStatus = existing.getStatusElement().getValueAsEnum();
if (existingStatus != newStatus) {
throw new UnprocessableEntityException("Subscription.status can not be changed from " + describeStatus(existingStatus) + " to " + describeStatus(newStatus));
}
} catch (ResourceNotFoundException e) {
if (newStatus != SubscriptionStatusEnum.REQUESTED) {
throw new UnprocessableEntityException("Subscription.status must be '" + SubscriptionStatusEnum.REQUESTED.getCode() + "' on a newly created subscription");
}
}
} else {
if (newStatus != SubscriptionStatusEnum.REQUESTED) {
throw new UnprocessableEntityException("Subscription.status must be '" + SubscriptionStatusEnum.REQUESTED.getCode() + "' on a newly created subscription");
}
}
}
private String describeStatus(SubscriptionStatusEnum existingStatus) {
String existingStatusString;
if (existingStatus != null) {
existingStatusString = '\'' + existingStatus.getCode() + '\'';
} else {
existingStatusString = "null";
}
return existingStatusString;
}
}

View File

@ -35,7 +35,7 @@
<property name="showSql" value="false" /> <property name="showSql" value="false" />
<property name="generateDdl" value="true" /> <property name="generateDdl" value="true" />
<!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" /> --> <!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" /> -->
<property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" /> <property name="databasePlatform" value="ca.uhn.fhir.jpa.util.HapiDerbyTenSevenDialect" />
</bean> </bean>
</property> </property>
</bean> </bean>

View File

@ -12,13 +12,11 @@ import javax.persistence.PersistenceContext;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.Before; import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -39,12 +37,17 @@ import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.entity.ResourceLink; import ca.uhn.fhir.jpa.entity.ResourceLink;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.ResourceTag; import ca.uhn.fhir.jpa.entity.ResourceTag;
import ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource;
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.jpa.entity.TagDefinition; import ca.uhn.fhir.jpa.entity.TagDefinition;
import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2;
import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.ConceptMap;
import ca.uhn.fhir.model.dstu2.resource.Device; import ca.uhn.fhir.model.dstu2.resource.Device;
import ca.uhn.fhir.model.dstu2.resource.DiagnosticOrder;
import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport; import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu2.resource.Encounter; import ca.uhn.fhir.model.dstu2.resource.Encounter;
import ca.uhn.fhir.model.dstu2.resource.Immunization;
import ca.uhn.fhir.model.dstu2.resource.Location; import ca.uhn.fhir.model.dstu2.resource.Location;
import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Organization; import ca.uhn.fhir.model.dstu2.resource.Organization;
@ -53,6 +56,8 @@ import ca.uhn.fhir.model.dstu2.resource.Practitioner;
import ca.uhn.fhir.model.dstu2.resource.Questionnaire; import ca.uhn.fhir.model.dstu2.resource.Questionnaire;
import ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse; import ca.uhn.fhir.model.dstu2.resource.QuestionnaireResponse;
import ca.uhn.fhir.model.dstu2.resource.StructureDefinition; import ca.uhn.fhir.model.dstu2.resource.StructureDefinition;
import ca.uhn.fhir.model.dstu2.resource.Subscription;
import ca.uhn.fhir.model.dstu2.resource.Substance;
import ca.uhn.fhir.model.dstu2.resource.ValueSet; import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.method.MethodUtil; import ca.uhn.fhir.rest.method.MethodUtil;
@ -63,16 +68,21 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
@ContextConfiguration(locations={ @ContextConfiguration(locations={
"classpath:hapi-fhir-server-resourceproviders-dstu2.xml", "classpath:hapi-fhir-server-resourceproviders-dstu2.xml",
"classpath:fhir-jpabase-spring-test-config.xml"}) "classpath:fhir-jpabase-spring-test-config.xml"})
@TransactionConfiguration(defaultRollback=false)
//@formatter:on //@formatter:on
public abstract class BaseJpaDstu2Test extends BaseJpaTest { public abstract class BaseJpaDstu2Test extends BaseJpaTest {
@Autowired
@Qualifier("myConceptMapDaoDstu2")
protected IFhirResourceDao<ConceptMap> myConceptMapDao;
@Autowired @Autowired
protected DaoConfig myDaoConfig; protected DaoConfig myDaoConfig;
@Autowired @Autowired
@Qualifier("myDeviceDaoDstu2") @Qualifier("myDeviceDaoDstu2")
protected IFhirResourceDao<Device> myDeviceDao; protected IFhirResourceDao<Device> myDeviceDao;
@Autowired @Autowired
@Qualifier("myDiagnosticOrderDaoDstu2")
protected IFhirResourceDao<DiagnosticOrder> myDiagnosticOrderDao;
@Autowired
@Qualifier("myDiagnosticReportDaoDstu2") @Qualifier("myDiagnosticReportDaoDstu2")
protected IFhirResourceDao<DiagnosticReport> myDiagnosticReportDao; protected IFhirResourceDao<DiagnosticReport> myDiagnosticReportDao;
@Autowired @Autowired
@ -82,6 +92,9 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
protected EntityManager myEntityManager; protected EntityManager myEntityManager;
@Autowired @Autowired
protected FhirContext myFhirCtx; protected FhirContext myFhirCtx;
@Autowired
@Qualifier("myImmunizationDaoDstu2")
protected IFhirResourceDao<Immunization> myImmunizationDao;
protected IServerInterceptor myInterceptor; protected IServerInterceptor myInterceptor;
@Autowired @Autowired
@Qualifier("myLocationDaoDstu2") @Qualifier("myLocationDaoDstu2")
@ -111,6 +124,12 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
@Qualifier("myStructureDefinitionDaoDstu2") @Qualifier("myStructureDefinitionDaoDstu2")
protected IFhirResourceDao<StructureDefinition> myStructureDefinitionDao; protected IFhirResourceDao<StructureDefinition> myStructureDefinitionDao;
@Autowired @Autowired
@Qualifier("mySubscriptionDaoDstu2")
protected IFhirResourceDaoSubscription<Subscription> mySubscriptionDao;
@Autowired
@Qualifier("mySubstanceDaoDstu2")
protected IFhirResourceDao<Substance> mySubstanceDao;
@Autowired
@Qualifier("mySystemDaoDstu2") @Qualifier("mySystemDaoDstu2")
protected IFhirSystemDao<Bundle> mySystemDao; protected IFhirSystemDao<Bundle> mySystemDao;
@Autowired @Autowired
@ -145,6 +164,13 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
return newJsonParser.parseResource(type, string); return newJsonParser.parseResource(type, string);
} }
public TransactionTemplate newTxTemplate() {
TransactionTemplate retVal = new TransactionTemplate(myTxManager);
retVal.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
retVal.afterPropertiesSet();
return retVal;
}
public static void purgeDatabase(final EntityManager entityManager, PlatformTransactionManager theTxManager) { public static void purgeDatabase(final EntityManager entityManager, PlatformTransactionManager theTxManager) {
TransactionTemplate txTemplate = new TransactionTemplate(theTxManager); TransactionTemplate txTemplate = new TransactionTemplate(theTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED); txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
@ -159,6 +185,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
txTemplate.execute(new TransactionCallback<Void>() { txTemplate.execute(new TransactionCallback<Void>() {
@Override @Override
public Void doInTransaction(TransactionStatus theStatus) { public Void doInTransaction(TransactionStatus theStatus) {
entityManager.createQuery("DELETE from " + SubscriptionFlaggedResource.class.getSimpleName() + " d").executeUpdate();
entityManager.createQuery("DELETE from " + ForcedId.class.getSimpleName() + " d").executeUpdate(); entityManager.createQuery("DELETE from " + ForcedId.class.getSimpleName() + " d").executeUpdate();
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamDate.class.getSimpleName() + " d").executeUpdate(); entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamDate.class.getSimpleName() + " d").executeUpdate();
entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamNumber.class.getSimpleName() + " d").executeUpdate(); entityManager.createQuery("DELETE from " + ResourceIndexedSearchParamNumber.class.getSimpleName() + " d").executeUpdate();
@ -174,6 +201,7 @@ public abstract class BaseJpaDstu2Test extends BaseJpaTest {
txTemplate.execute(new TransactionCallback<Void>() { txTemplate.execute(new TransactionCallback<Void>() {
@Override @Override
public Void doInTransaction(TransactionStatus theStatus) { public Void doInTransaction(TransactionStatus theStatus) {
entityManager.createQuery("DELETE from " + SubscriptionTable.class.getSimpleName() + " d").executeUpdate();
entityManager.createQuery("DELETE from " + ResourceHistoryTag.class.getSimpleName() + " d").executeUpdate(); entityManager.createQuery("DELETE from " + ResourceHistoryTag.class.getSimpleName() + " d").executeUpdate();
entityManager.createQuery("DELETE from " + ResourceTag.class.getSimpleName() + " d").executeUpdate(); entityManager.createQuery("DELETE from " + ResourceTag.class.getSimpleName() + " d").executeUpdate();
entityManager.createQuery("DELETE from " + TagDefinition.class.getSimpleName() + " d").executeUpdate(); entityManager.createQuery("DELETE from " + TagDefinition.class.getSimpleName() + " d").executeUpdate();

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.dao;
import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsInRelativeOrder; import static org.hamcrest.Matchers.containsInRelativeOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
@ -24,7 +25,13 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.entity.ResourceLink;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.Include;
@ -38,19 +45,25 @@ import ca.uhn.fhir.model.dstu2.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu2.composite.PeriodDt; import ca.uhn.fhir.model.dstu2.composite.PeriodDt;
import ca.uhn.fhir.model.dstu2.composite.QuantityDt; import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.ConceptMap;
import ca.uhn.fhir.model.dstu2.resource.Device; import ca.uhn.fhir.model.dstu2.resource.Device;
import ca.uhn.fhir.model.dstu2.resource.DiagnosticOrder;
import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport; import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu2.resource.Encounter; import ca.uhn.fhir.model.dstu2.resource.Encounter;
import ca.uhn.fhir.model.dstu2.resource.Immunization;
import ca.uhn.fhir.model.dstu2.resource.Location; import ca.uhn.fhir.model.dstu2.resource.Location;
import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Organization; import ca.uhn.fhir.model.dstu2.resource.Organization;
import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.resource.Practitioner; import ca.uhn.fhir.model.dstu2.resource.Practitioner;
import ca.uhn.fhir.model.dstu2.resource.Substance;
import ca.uhn.fhir.model.dstu2.resource.ValueSet; import ca.uhn.fhir.model.dstu2.resource.ValueSet;
import ca.uhn.fhir.model.dstu2.valueset.ContactPointSystemEnum; import ca.uhn.fhir.model.dstu2.valueset.ContactPointSystemEnum;
import ca.uhn.fhir.model.primitive.CodeDt;
import ca.uhn.fhir.model.primitive.DateDt; import ca.uhn.fhir.model.primitive.DateDt;
import ca.uhn.fhir.model.primitive.DateTimeDt; import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.param.CompositeParam; import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam; import ca.uhn.fhir.rest.param.DateParam;
@ -58,6 +71,8 @@ import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.NumberParam; import ca.uhn.fhir.rest.param.NumberParam;
import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenAndListParam; import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenOrListParam;
@ -70,6 +85,163 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
public class FhirResourceDaoDstu2SearchTest extends BaseJpaDstu2Test { public class FhirResourceDaoDstu2SearchTest extends BaseJpaDstu2Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2SearchTest.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2SearchTest.class);
@Test
public void testIndexNoDuplicatesString() {
Patient p = new Patient();
p.addAddress().addLine("123 Fake Street");
p.addAddress().addLine("123 Fake Street");
p.addAddress().addLine("123 Fake Street");
p.addAddress().addLine("456 Fake Street");
p.addAddress().addLine("456 Fake Street");
p.addAddress().addLine("456 Fake Street");
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
Class<ResourceIndexedSearchParamString> type = ResourceIndexedSearchParamString.class;
List<ResourceIndexedSearchParamString> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(2, results.size());
List<IIdType> actual = toUnqualifiedVersionlessIds(myPatientDao.search(Patient.SP_ADDRESS, new StringParam("123 Fake Street")));
assertThat(actual, contains(id));
}
@Test
public void testIndexNoDuplicatesDate() {
DiagnosticOrder order = new DiagnosticOrder();
order.addItem().addEvent().setDateTime(new DateTimeDt("2011-12-11T11:12:12Z"));
order.addItem().addEvent().setDateTime(new DateTimeDt("2011-12-11T11:12:12Z"));
order.addItem().addEvent().setDateTime(new DateTimeDt("2011-12-11T11:12:12Z"));
order.addItem().addEvent().setDateTime(new DateTimeDt("2011-12-12T11:12:12Z"));
order.addItem().addEvent().setDateTime(new DateTimeDt("2011-12-12T11:12:12Z"));
order.addItem().addEvent().setDateTime(new DateTimeDt("2011-12-12T11:12:12Z"));
IIdType id = myDiagnosticOrderDao.create(order).getId().toUnqualifiedVersionless();
List<IIdType> actual = toUnqualifiedVersionlessIds(myDiagnosticOrderDao.search(DiagnosticOrder.SP_ITEM_DATE, new DateParam("2011-12-12T11:12:12Z")));
assertThat(actual, contains(id));
Class<ResourceIndexedSearchParamDate> type = ResourceIndexedSearchParamDate.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(2, results.size());
}
@Test
public void testIndexNoDuplicatesNumber() {
Immunization res = new Immunization();
res.addVaccinationProtocol().setDoseSequence(1);
res.addVaccinationProtocol().setDoseSequence(1);
res.addVaccinationProtocol().setDoseSequence(1);
res.addVaccinationProtocol().setDoseSequence(2);
res.addVaccinationProtocol().setDoseSequence(2);
res.addVaccinationProtocol().setDoseSequence(2);
IIdType id = myImmunizationDao.create(res).getId().toUnqualifiedVersionless();
List<IIdType> actual = toUnqualifiedVersionlessIds(myImmunizationDao.search(Immunization.SP_DOSE_SEQUENCE, new NumberParam("1")));
assertThat(actual, contains(id));
Class<ResourceIndexedSearchParamNumber> type = ResourceIndexedSearchParamNumber.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(2, results.size());
}
@Test
public void testIndexNoDuplicatesUri() {
ConceptMap res = new ConceptMap();
res.addElement().addTarget().addDependsOn().setElement("http://foo");
res.addElement().addTarget().addDependsOn().setElement("http://foo");
res.addElement().addTarget().addDependsOn().setElement("http://bar");
res.addElement().addTarget().addDependsOn().setElement("http://bar");
IIdType id = myConceptMapDao.create(res).getId().toUnqualifiedVersionless();
Class<ResourceIndexedSearchParamUri> type = ResourceIndexedSearchParamUri.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(2, results.size());
List<IIdType> actual = toUnqualifiedVersionlessIds(myConceptMapDao.search(ConceptMap.SP_DEPENDSON, new UriParam("http://foo")));
assertThat(actual, contains(id));
}
@Test
public void testIndexNoDuplicatesQuantity() {
Substance res = new Substance();
res.addInstance().getQuantity().setSystem("http://foo").setCode("UNIT").setValue(123);
res.addInstance().getQuantity().setSystem("http://foo").setCode("UNIT").setValue(123);
res.addInstance().getQuantity().setSystem("http://foo2").setCode("UNIT2").setValue(1232);
res.addInstance().getQuantity().setSystem("http://foo2").setCode("UNIT2").setValue(1232);
IIdType id = mySubstanceDao.create(res).getId().toUnqualifiedVersionless();
Class<ResourceIndexedSearchParamQuantity> type = ResourceIndexedSearchParamQuantity.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(2, results.size());
List<IIdType> actual = toUnqualifiedVersionlessIds(mySubstanceDao.search(Substance.SP_QUANTITY, new QuantityParam(null, 123, "http://foo", "UNIT")));
assertThat(actual, contains(id));
}
@Test
public void testIndexNoDuplicatesToken() {
Patient res = new Patient();
res.addIdentifier().setSystem("http://foo1").setValue("123");
res.addIdentifier().setSystem("http://foo1").setValue("123");
res.addIdentifier().setSystem("http://foo2").setValue("1234");
res.addIdentifier().setSystem("http://foo2").setValue("1234");
IIdType id = myPatientDao.create(res).getId().toUnqualifiedVersionless();
Class<ResourceIndexedSearchParamToken> type = ResourceIndexedSearchParamToken.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(2, results.size());
List<IIdType> actual = toUnqualifiedVersionlessIds(myPatientDao.search(Patient.SP_IDENTIFIER, new TokenParam("http://foo1", "123")));
assertThat(actual, contains(id));
}
@Test
public void testIndexNoDuplicatesReference() {
Practitioner pract =new Practitioner();
pract.setId("Practitioner/somepract");
pract.getName().addFamily("SOME PRACT");
myPractitionerDao.update(pract);
Practitioner pract2 =new Practitioner();
pract2.setId("Practitioner/somepract2");
pract2.getName().addFamily("SOME PRACT2");
myPractitionerDao.update(pract2);
DiagnosticOrder res = new DiagnosticOrder();
res.addEvent().setActor(new ResourceReferenceDt("Practitioner/somepract"));
res.addEvent().setActor(new ResourceReferenceDt("Practitioner/somepract"));
res.addEvent().setActor(new ResourceReferenceDt("Practitioner/somepract2"));
res.addEvent().setActor(new ResourceReferenceDt("Practitioner/somepract2"));
IIdType id = myDiagnosticOrderDao.create(res).getId().toUnqualifiedVersionless();
Class<ResourceLink> type = ResourceLink.class;
List<?> results = myEntityManager.createQuery("SELECT i FROM " + type.getSimpleName() + " i", type).getResultList();
ourLog.info(toStringMultiline(results));
assertEquals(2, results.size());
List<IIdType> actual = toUnqualifiedVersionlessIds(myDiagnosticOrderDao.search(DiagnosticOrder.SP_ACTOR, new ReferenceParam("Practitioner/somepract")));
assertThat(actual, contains(id));
}
private String toStringMultiline(List<?> theResults) {
StringBuilder b = new StringBuilder();
for (Object next : theResults) {
b.append('\n');
b.append(" * ").append(next.toString());
}
return b.toString();
}
@Test @Test
public void testSearchAll() { public void testSearchAll() {
{ {
@ -130,6 +302,151 @@ public class FhirResourceDaoDstu2SearchTest extends BaseJpaDstu2Test {
} }
/**
* #222
*/
@Test
public void testSearchForDeleted() {
{
Patient patient = new Patient();
patient.setId("TEST");
patient.setLanguage(new CodeDt("TEST"));
patient.addName().addFamily("TEST");
patient.addIdentifier().setSystem("TEST").setValue("TEST");
myPatientDao.update(patient);
}
Map<String, IQueryParameterType> params = new HashMap<String, IQueryParameterType>();
params.put("_id", new StringDt("TEST"));
assertEquals(1, toList(myPatientDao.search(params)).size());
params.put("_language", new StringParam("TEST"));
assertEquals(1, toList(myPatientDao.search(params)).size());
params.put(Patient.SP_IDENTIFIER, new TokenParam("TEST", "TEST"));
assertEquals(1, toList(myPatientDao.search(params)).size());
params.put(Patient.SP_NAME, new StringParam("TEST"));
assertEquals(1, toList(myPatientDao.search(params)).size());
myPatientDao.delete(new IdDt("Patient/TEST"));
params = new HashMap<String, IQueryParameterType>();
params.put("_id", new StringDt("TEST"));
assertEquals(0, toList(myPatientDao.search(params)).size());
params.put("_language", new StringParam("TEST"));
assertEquals(0, toList(myPatientDao.search(params)).size());
params.put(Patient.SP_IDENTIFIER, new TokenParam("TEST", "TEST"));
assertEquals(0, toList(myPatientDao.search(params)).size());
params.put(Patient.SP_NAME, new StringParam("TEST"));
assertEquals(0, toList(myPatientDao.search(params)).size());
}
@Test
public void testSearchByIdParamWrongType() {
IIdType id1;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id1 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
IIdType id2;
{
Organization patient = new Organization();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id2 = myOrganizationDao.create(patient).getId().toUnqualifiedVersionless();
}
SearchParameterMap params = new SearchParameterMap();
params.add("_id", new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam(id2.getIdPart())));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
}
@Test
public void testSearchByIdParamOr() {
IIdType id1;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id1 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
IIdType id2;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id2 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
SearchParameterMap params = new SearchParameterMap();
params.add("_id", new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam(id2.getIdPart())));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1, id2));
params = new SearchParameterMap();
params.add("_id", new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam(id1.getIdPart())));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
params = new SearchParameterMap();
params.add("_id", new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam("999999999999")));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
}
@Test
public void testSearchByIdParamAnd() {
IIdType id1;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id1 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
IIdType id2;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id2 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
SearchParameterMap params;
StringAndListParam param;
params = new SearchParameterMap();
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam(id1.getIdPart())).addOr(new StringParam(id2.getIdPart())));
param.addAnd(new StringOrListParam().addOr(new StringParam(id1.getIdPart())));
params.add("_id", param);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
params = new SearchParameterMap();
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam(id2.getIdPart())));
param.addAnd(new StringOrListParam().addOr(new StringParam(id1.getIdPart())));
params.add("_id", param);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), empty());
params = new SearchParameterMap();
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam(id2.getIdPart())));
param.addAnd(new StringOrListParam().addOr(new StringParam("9999999999999")));
params.add("_id", param);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), empty());
params = new SearchParameterMap();
param = new StringAndListParam();
param.addAnd(new StringOrListParam().addOr(new StringParam("9999999999999")));
param.addAnd(new StringOrListParam().addOr(new StringParam(id2.getIdPart())));
params.add("_id", param);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), empty());
}
@Test @Test
public void testSearchCompositeParam() { public void testSearchCompositeParam() {
Observation o1 = new Observation(); Observation o1 = new Observation();
@ -201,7 +518,6 @@ public class FhirResourceDaoDstu2SearchTest extends BaseJpaDstu2Test {
assertEquals(0, retrieved.size()); assertEquals(0, retrieved.size());
} }
} }
@Test @Test
public void testSearchLanguageParam() { public void testSearchLanguageParam() {
IIdType id1; IIdType id1;
@ -240,9 +556,155 @@ public class FhirResourceDaoDstu2SearchTest extends BaseJpaDstu2Test {
List<Patient> patients = toList(myPatientDao.search(params)); List<Patient> patients = toList(myPatientDao.search(params));
assertEquals(0, patients.size()); assertEquals(0, patients.size());
} }
}
@Test
public void testSearchLanguageParamAndOr() {
IIdType id1;
{
Patient patient = new Patient();
patient.getLanguage().setValue("en_CA");
patient.addIdentifier().setSystem("urn:system").setValue("001");
patient.addName().addFamily("testSearchLanguageParam").addGiven("Joe");
id1 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
IIdType id2;
{
Patient patient = new Patient();
patient.getLanguage().setValue("en_US");
patient.addIdentifier().setSystem("urn:system").setValue("002");
patient.addName().addFamily("testSearchLanguageParam").addGiven("John");
id2 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
{
SearchParameterMap params = new SearchParameterMap();
params.add(Patient.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("en_US")));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1, id2));
}
{
SearchParameterMap params = new SearchParameterMap();
params.add(Patient.SP_RES_LANGUAGE, new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
}
{
SearchParameterMap params = new SearchParameterMap();
StringAndListParam and = new StringAndListParam();
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")));
params.add(Patient.SP_RES_LANGUAGE, and);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
}
{
SearchParameterMap params = new SearchParameterMap();
StringAndListParam and = new StringAndListParam();
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
and.addAnd(new StringOrListParam().addOr(new StringParam("ZZZZZ")));
params.add(Patient.SP_RES_LANGUAGE, and);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), empty());
}
{
SearchParameterMap params = new SearchParameterMap();
StringAndListParam and = new StringAndListParam();
and.addAnd(new StringOrListParam().addOr(new StringParam("ZZZZZ")));
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
params.add(Patient.SP_RES_LANGUAGE, and);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), empty());
}
{
SearchParameterMap params = new SearchParameterMap();
StringAndListParam and = new StringAndListParam();
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
and.addAnd(new StringOrListParam().addOr(new StringParam("")).addOr(new StringParam(null)));
params.add(Patient.SP_RES_LANGUAGE, and);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
}
{
SearchParameterMap params = new SearchParameterMap();
params.add("_id", new StringParam(id1.getIdPart()));
StringAndListParam and = new StringAndListParam();
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
and.addAnd(new StringOrListParam().addOr(new StringParam("")).addOr(new StringParam(null)));
params.add(Patient.SP_RES_LANGUAGE, and);
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
}
{
SearchParameterMap params = new SearchParameterMap();
StringAndListParam and = new StringAndListParam();
and.addAnd(new StringOrListParam().addOr(new StringParam("en_CA")).addOr(new StringParam("ZZZZ")));
and.addAnd(new StringOrListParam().addOr(new StringParam("")).addOr(new StringParam(null)));
params.add(Patient.SP_RES_LANGUAGE, and);
params.add("_id", new StringParam(id1.getIdPart()));
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(params)), containsInAnyOrder(id1));
}
} }
@Test
public void testSearchLastUpdatedParamWithComparator() throws InterruptedException {
String methodName = "testSearchLastUpdatedParamWithComparator";
IIdType id0;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id0 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
int sleep = 100;
long start = System.currentTimeMillis();
Thread.sleep(sleep);
DateTimeDt beforeAny = new DateTimeDt(new Date(), TemporalPrecisionEnum.MILLI);
IIdType id1a;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id1a = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
IIdType id1b;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id1b = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
ourLog.info("Res 1: {}", ResourceMetadataKeyEnum.PUBLISHED.get(myPatientDao.read(id0)).getValueAsString());
ourLog.info("Res 2: {}", ResourceMetadataKeyEnum.PUBLISHED.get(myPatientDao.read(id1a)).getValueAsString());
InstantDt id1bpublished = ResourceMetadataKeyEnum.PUBLISHED.get(myPatientDao.read(id1b));
ourLog.info("Res 3: {}", id1bpublished.getValueAsString());
Thread.sleep(sleep);
long end = System.currentTimeMillis();
SearchParameterMap map;
Date startDate = new Date(start);
Date endDate = new Date(end);
DateTimeDt startDateTime = new DateTimeDt(startDate, TemporalPrecisionEnum.MILLI);
DateTimeDt endDateTime = new DateTimeDt(endDate, TemporalPrecisionEnum.MILLI);
map = new SearchParameterMap();
map.setLastUpdated(new DateRangeParam(startDateTime, endDateTime));
ourLog.info("Searching: {}", map.getLastUpdated());
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1a, id1b));
map = new SearchParameterMap();
map.setLastUpdated(new DateRangeParam(new DateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, startDateTime), new DateParam(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, endDateTime)));
ourLog.info("Searching: {}", map.getLastUpdated());
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1a, id1b));
map = new SearchParameterMap();
map.setLastUpdated(new DateRangeParam(new DateParam(QuantityCompararatorEnum.GREATERTHAN, startDateTime), new DateParam(QuantityCompararatorEnum.LESSTHAN, endDateTime)));
ourLog.info("Searching: {}", map.getLastUpdated());
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1a, id1b));
map = new SearchParameterMap();
map.setLastUpdated(new DateRangeParam(new DateParam(QuantityCompararatorEnum.GREATERTHAN, startDateTime.getValue()), new DateParam(QuantityCompararatorEnum.LESSTHAN, id1bpublished.getValue())));
ourLog.info("Searching: {}", map.getLastUpdated());
assertThat(toUnqualifiedVersionlessIds(myPatientDao.search(map)), containsInAnyOrder(id1a));
}
@Test @Test
public void testSearchLastUpdatedParam() throws InterruptedException { public void testSearchLastUpdatedParam() throws InterruptedException {
String methodName = "testSearchLastUpdatedParam"; String methodName = "testSearchLastUpdatedParam";

View File

@ -0,0 +1,174 @@
package ca.uhn.fhir.jpa.dao;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Set;
import javax.persistence.TypedQuery;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.Before;
import org.junit.Test;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import ca.uhn.fhir.jpa.entity.SubscriptionTable;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.dstu2.resource.Subscription;
import ca.uhn.fhir.model.dstu2.valueset.ObservationStatusEnum;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionChannelTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
public class FhirResourceDaoDstu2SubscriptionTest extends BaseJpaDstu2Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2SubscriptionTest.class);
@Test
public void testCreateSubscriptionInvalidCriteria() {
Subscription subs = new Subscription();
subs.setStatus(SubscriptionStatusEnum.REQUESTED);
subs.setCriteria("Observation");
try {
mySubscriptionDao.create(subs);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Subscription.criteria must be in the form \"{Resource Type}?[params]\""));
}
subs = new Subscription();
subs.setStatus(SubscriptionStatusEnum.REQUESTED);
subs.setCriteria("http://foo.com/Observation?AAA=BBB");
try {
mySubscriptionDao.create(subs);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Subscription.criteria must be in the form \"{Resource Type}?[params]\""));
}
subs = new Subscription();
subs.setStatus(SubscriptionStatusEnum.REQUESTED);
subs.setCriteria("ObservationZZZZ?a=b");
try {
mySubscriptionDao.create(subs);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Subscription.criteria contains invalid/unsupported resource type: ObservationZZZZ"));
}
subs = new Subscription();
subs.setStatus(SubscriptionStatusEnum.REQUESTED);
subs.setCriteria("Observation?identifier=123");
try {
mySubscriptionDao.create(subs);
fail();
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("Subscription.channel.type must be populated on this server"));
}
subs = new Subscription();
subs.setStatus(SubscriptionStatusEnum.REQUESTED);
subs.setCriteria("Observation?identifier=123");
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
assertTrue(mySubscriptionDao.create(subs).getId().hasIdPart());
}
@Before
public void beforeEnableSubscription() {
myDaoConfig.setSubscriptionEnabled(true);
}
@Test
public void testSubscriptionResourcesAppear() throws Exception {
myDaoConfig.setSubscriptionPollDelay(0);
String methodName = "testSubscriptionResourcesAppear";
Patient p = new Patient();
p.addName().addFamily(methodName);
IIdType pId = myPatientDao.create(p).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getSubject().setReference(pId);
obs.setStatus(ObservationStatusEnum.FINAL);
IIdType beforeId = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
Subscription subs = new Subscription();
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
subs.setCriteria("Observation?subject=Patient/" + pId.getIdPart());
subs.setStatus(SubscriptionStatusEnum.ACTIVE);
IIdType id = mySubscriptionDao.create(subs).getId().toUnqualifiedVersionless();
Thread.sleep(100);
ourLog.info("Before: {}", System.currentTimeMillis());
obs = new Observation();
obs.getSubject().setReference(pId);
obs.setStatus(ObservationStatusEnum.FINAL);
IIdType afterId1 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
obs = new Observation();
obs.getSubject().setReference(pId);
obs.setStatus(ObservationStatusEnum.FINAL);
IIdType afterId2 = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
Thread.sleep(100);
ourLog.info("After: {}", System.currentTimeMillis());
mySubscriptionDao.pollForNewUndeliveredResources();
}
@Test
public void testCreateSubscription() {
Subscription subs = new Subscription();
subs.setCriteria("Observation?subject=Patient/123");
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
subs.setStatus(SubscriptionStatusEnum.REQUESTED);
IIdType id = mySubscriptionDao.create(subs).getId().toUnqualifiedVersionless();
TypedQuery<SubscriptionTable> q = myEntityManager.createQuery("SELECT t from SubscriptionTable t WHERE t.mySubscriptionResource.myId = :id", SubscriptionTable.class);
q.setParameter("id", id.getIdPartAsLong());
final SubscriptionTable table = q.getSingleResult();
assertNotNull(table);
assertNotNull(table.getNextCheck());
assertEquals(table.getNextCheck(), table.getSubscriptionResource().getPublished().getValue());
assertEquals(SubscriptionStatusEnum.REQUESTED, myEntityManager.find(SubscriptionTable.class, table.getId()).getStatus());
assertEquals(SubscriptionStatusEnum.REQUESTED, mySubscriptionDao.read(id).getStatusElement().getValueAsEnum());
subs.setStatus(SubscriptionStatusEnum.ACTIVE);
mySubscriptionDao.update(subs);
assertEquals(SubscriptionStatusEnum.ACTIVE, myEntityManager.find(SubscriptionTable.class, table.getId()).getStatus());
assertEquals(SubscriptionStatusEnum.ACTIVE, mySubscriptionDao.read(id).getStatusElement().getValueAsEnum());
mySubscriptionDao.delete(id);
assertNull(myEntityManager.find(SubscriptionTable.class, table.getId()));
/*
* Re-create again
*/
subs = new Subscription();
subs.setCriteria("Observation?subject=Patient/123");
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
subs.setId(id);
subs.setStatus(SubscriptionStatusEnum.REQUESTED);
mySubscriptionDao.update(subs);
assertEquals(SubscriptionStatusEnum.REQUESTED, myEntityManager.createQuery("SELECT t FROM SubscriptionTable t WHERE t.myResId = " + id.getIdPart(), SubscriptionTable.class).getSingleResult().getStatus());
assertEquals(SubscriptionStatusEnum.REQUESTED, mySubscriptionDao.read(id).getStatusElement().getValueAsEnum());
}
}

View File

@ -77,6 +77,7 @@ import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.SortOrderEnum; import ca.uhn.fhir.rest.api.SortOrderEnum;
@ -966,8 +967,13 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
assertEquals(id.withVersion("1"), entries.get(2).getIdElement()); assertEquals(id.withVersion("1"), entries.get(2).getIdElement());
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) entries.get(0))); assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) entries.get(0)));
assertEquals(BundleEntryTransactionMethodEnum.PUT, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get((IResource) entries.get(0)));
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) entries.get(1))); assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) entries.get(1)));
assertEquals(BundleEntryTransactionMethodEnum.DELETE, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get((IResource) entries.get(1)));
assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) entries.get(2))); assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) entries.get(2)));
assertEquals(BundleEntryTransactionMethodEnum.POST, ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get((IResource) entries.get(2)));
} }
@Test @Test

View File

@ -0,0 +1,126 @@
package ca.uhn.fhir.jpa.provider;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
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.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import ca.uhn.fhir.jpa.dao.BaseJpaDstu2Test;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.dstu2.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
public abstract class BaseResourceProviderDstu2Test extends BaseJpaDstu2Test {
protected static IGenericClient ourClient;
protected static CloseableHttpClient ourHttpClient;
protected static int ourPort;
private static Server ourServer;
protected static String ourServerBase;
public BaseResourceProviderDstu2Test() {
super();
}
protected List<IdDt> toIdListUnqualifiedVersionless(Bundle found) {
List<IdDt> list = new ArrayList<IdDt>();
for (BundleEntry next : found.getEntries()) {
list.add(next.getResource().getId().toUnqualifiedVersionless());
}
return list;
}
protected List<String> toNameList(Bundle resp) {
List<String> names = new ArrayList<String>();
for (BundleEntry next : resp.getEntries()) {
Patient nextPt = (Patient) next.getResource();
String nextStr = nextPt.getNameFirstRep().getGivenAsSingleString() + " " + nextPt.getNameFirstRep().getFamilyAsSingleString();
if (isNotBlank(nextStr)) {
names.add(nextStr);
}
}
return names;
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
ourHttpClient.close();
ourServer = null;
ourHttpClient = null;
}
@After
public void after() {
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Before
public void before() throws Exception {
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myFhirCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
if (ourServer == null) {
ourPort = RandomServerPortProvider.findFreePort();
RestfulServer restServer = new RestfulServer(myFhirCtx);
ourServerBase = "http://localhost:" + ourPort + "/fhir/context";
restServer.setResourceProviders((List)myResourceProviders);
restServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
restServer.setPlainProviders(mySystemProvider);
JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(restServer, mySystemDao);
confProvider.setImplementationDescription("THIS IS THE DESC");
restServer.setServerConformanceProvider(confProvider);
restServer.setPagingProvider(new FifoMemoryPagingProvider(10));
Server server = new Server(ourPort);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(restServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
server.setHandler(proxyHandler);
server.start();
ourClient = myFhirCtx.newRestfulGenericClient(ourServerBase);
ourClient.registerInterceptor(new LoggingInterceptor(true));
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourHttpClient = builder.build();
ourServer = server;
}
}
}

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.jpa.provider; package ca.uhn.fhir.jpa.provider;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsInRelativeOrder; import static org.hamcrest.Matchers.containsInRelativeOrder;
@ -29,7 +28,6 @@ import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -41,23 +39,11 @@ import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut; import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType; import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity; import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.jpa.dao.BaseJpaDstu2Test;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.api.Bundle; 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.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
@ -95,27 +81,18 @@ import ca.uhn.fhir.model.primitive.UnsignedIntDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum;
import ca.uhn.fhir.model.valueset.BundleTypeEnum; import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
public class ResourceProviderDstu2Test extends BaseJpaDstu2Test { public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
private static IGenericClient ourClient;
private static CloseableHttpClient ourHttpClient;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderDstu2Test.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderDstu2Test.class);
private static int ourPort;
private static Server ourServer;
private static String ourServerBase;
// private static JpaConformanceProvider ourConfProvider; // private static JpaConformanceProvider ourConfProvider;
@ -151,6 +128,33 @@ public class ResourceProviderDstu2Test extends BaseJpaDstu2Test {
// } // }
// } // }
@Test
public void testSearchByIdOr() {
IIdType id1;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id1 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
IIdType id2;
{
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:system").setValue("001");
id2 = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
}
//@formatter:off
Bundle found = ourClient
.search()
.forResource(Patient.class)
.where(Patient.RES_ID.matches().values(id1.getIdPart(), id2.getIdPart()))
.and(Patient.RES_ID.matches().value(id1.getIdPart()))
.execute();
//@formatter:on
assertThat(toIdListUnqualifiedVersionless(found), containsInAnyOrder(id1));
}
@Test @Test
public void testBundleCreate() throws Exception { public void testBundleCreate() throws Exception {
IGenericClient client = ourClient; IGenericClient client = ourClient;
@ -202,6 +206,19 @@ public class ResourceProviderDstu2Test extends BaseJpaDstu2Test {
} }
@Test
public void testCreateWithForcedId() throws IOException {
String methodName = "testCreateWithForcedId";
Patient p = new Patient();
p.addName().addFamily(methodName);
p.setId(methodName);
IIdType optId = ourClient.update().resource(p).execute().getId();
assertEquals(methodName, optId.getIdPart());
assertEquals("1", optId.getVersionIdPart());
}
@Test @Test
public void testCreateQuestionnaireResponseWithValidation() throws IOException { public void testCreateQuestionnaireResponseWithValidation() throws IOException {
ValueSet options = new ValueSet(); ValueSet options = new ValueSet();
@ -730,6 +747,50 @@ public class ResourceProviderDstu2Test extends BaseJpaDstu2Test {
} }
} }
@Test
public void testMetaOperationWithNoMetaParameter() throws Exception {
Patient p = new Patient();
p.addName().addFamily("testMetaAddInvalid");
IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
//@formatter:off
String input = "<Parameters>\n" +
" <meta>\n" +
" <tag>\n" +
" <system value=\"http://example.org/codes/tags\"/>\n" +
" <code value=\"record-lost\"/>\n" +
" <display value=\"Patient File Lost\"/>\n" +
" </tag>\n" +
" </meta>\n" +
"</Parameters>";
//@formatter:on
HttpPost post = new HttpPost(ourServerBase + "/Patient/" + id.getIdPart() + "/$meta-add");
post.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
String output = IOUtils.toString(response.getEntity().getContent());
ourLog.info(output);
assertEquals(400, response.getStatusLine().getStatusCode());
assertThat(output, containsString("Input contains no parameter with name 'meta'"));
} finally {
response.close();
}
post = new HttpPost(ourServerBase + "/Patient/" + id.getIdPart() + "/$meta-delete");
post.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
response = ourHttpClient.execute(post);
try {
String output = IOUtils.toString(response.getEntity().getContent());
ourLog.info(output);
assertEquals(400, response.getStatusLine().getStatusCode());
assertThat(output, containsString("Input contains no parameter with name 'meta'"));
} finally {
response.close();
}
}
@Test @Test
public void testMetaOperations() throws Exception { public void testMetaOperations() throws Exception {
String methodName = "testMetaOperations"; String methodName = "testMetaOperations";
@ -1635,84 +1696,4 @@ public class ResourceProviderDstu2Test extends BaseJpaDstu2Test {
} }
private List<IdDt> toIdListUnqualifiedVersionless(Bundle found) {
List<IdDt> list = new ArrayList<IdDt>();
for (BundleEntry next : found.getEntries()) {
list.add(next.getResource().getId().toUnqualifiedVersionless());
}
return list;
}
private List<String> toNameList(Bundle resp) {
List<String> names = new ArrayList<String>();
for (BundleEntry next : resp.getEntries()) {
Patient nextPt = (Patient) next.getResource();
String nextStr = nextPt.getNameFirstRep().getGivenAsSingleString() + " " + nextPt.getNameFirstRep().getFamilyAsSingleString();
if (isNotBlank(nextStr)) {
names.add(nextStr);
}
}
return names;
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
ourHttpClient.close();
}
@After
public void after() {
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Before
public void before() throws Exception {
myFhirCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
myFhirCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
if (ourServer == null) {
ourPort = RandomServerPortProvider.findFreePort();
RestfulServer restServer = new RestfulServer(myFhirCtx);
ourServerBase = "http://localhost:" + ourPort + "/fhir/context";
restServer.setResourceProviders((List)myResourceProviders);
restServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
restServer.setPlainProviders(mySystemProvider);
JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(restServer, mySystemDao);
confProvider.setImplementationDescription("THIS IS THE DESC");
restServer.setServerConformanceProvider(confProvider);
restServer.setPagingProvider(new FifoMemoryPagingProvider(10));
Server server = new Server(ourPort);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(restServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
server.setHandler(proxyHandler);
server.start();
ourClient = myFhirCtx.newRestfulGenericClient(ourServerBase);
ourClient.registerInterceptor(new LoggingInterceptor(true));
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourHttpClient = builder.build();
ourServer = server;
}
}
} }

View File

@ -0,0 +1,101 @@
package ca.uhn.fhir.jpa.provider;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.Test;
import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptor;
import ca.uhn.fhir.model.dstu2.resource.Subscription;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionChannelTypeEnum;
import ca.uhn.fhir.model.dstu2.valueset.SubscriptionStatusEnum;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
public class SubscriptionsRequireManualActivationInterceptorTest extends BaseResourceProviderDstu2Test {
@Test
public void testCreateInvalidNoStatus() {
Subscription subs = new Subscription();
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
subs.setCriteria("Observation?identifier=123");
try {
ourClient.create().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Subscription.status must be 'requested' on a newly created subscription", e.getMessage());
}
subs.setId("ABC");
try {
ourClient.update().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Subscription.status must be 'requested' on a newly created subscription", e.getMessage());
}
subs.setStatus(SubscriptionStatusEnum.REQUESTED);
ourClient.update().resource(subs).execute();
}
@Test
public void testCreateInvalidWrongStatus() {
Subscription subs = new Subscription();
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
subs.setStatus(SubscriptionStatusEnum.ACTIVE);
subs.setCriteria("Observation?identifier=123");
try {
ourClient.create().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Subscription.status must be 'requested' on a newly created subscription", e.getMessage());
}
subs.setId("ABC");
try {
ourClient.update().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Subscription.status must be 'requested' on a newly created subscription", e.getMessage());
}
}
@Test
public void testUpdateFails() {
Subscription subs = new Subscription();
subs.getChannel().setType(SubscriptionChannelTypeEnum.WEBSOCKET);
subs.setStatus(SubscriptionStatusEnum.REQUESTED);
subs.setCriteria("Observation?identifier=123");
IIdType id = ourClient.create().resource(subs).execute().getId().toUnqualifiedVersionless();
subs.setId(id);
try {
subs.setStatus(SubscriptionStatusEnum.ACTIVE);
ourClient.update().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Subscription.status can not be changed from 'requested' to 'active'", e.getMessage());
}
try {
subs.setStatus((SubscriptionStatusEnum) null);
ourClient.update().resource(subs).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Subscription.status can not be changed from 'requested' to null", e.getMessage());
}
subs.setStatus(SubscriptionStatusEnum.OFF);
}
@Override
public void beforeCreateInterceptor() {
super.beforeCreateInterceptor();
SubscriptionsRequireManualActivationInterceptor interceptor = new SubscriptionsRequireManualActivationInterceptor();
interceptor.setDao(mySubscriptionDao);
myDaoConfig.getInterceptors().add(interceptor);
}
}

View File

@ -21,16 +21,13 @@
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords</class> <class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords</class>
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class> <class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class> <class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
<class>ca.uhn.fhir.jpa.entity.SubscriptionTable</class>
<class>ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource</class>
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class> <class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes> <exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties> <properties>
<property name="hibernate.connection.url" value="jdbc:hsqldb:mem:unit-testing-jpa" />
<property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" />
<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyTenSevenDialect" />
<property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.connection.username" value="sa" />
<property name="hibernate.connection.password" value="" />
<property name="hibernate.jdbc.batch_size" value="0" /> <property name="hibernate.jdbc.batch_size" value="0" />
<property name="hibernate.cache.use_minimal_puts" value="false" /> <property name="hibernate.cache.use_minimal_puts" value="false" />
<property name="hibernate.cache.use_query_cache" value="false" /> <property name="hibernate.cache.use_query_cache" value="false" />

View File

@ -33,7 +33,7 @@
<property name="showSql" value="false" /> <property name="showSql" value="false" />
<property name="generateDdl" value="true" /> <property name="generateDdl" value="true" />
<!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" /> --> <!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" /> -->
<property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" /> <property name="databasePlatform" value="ca.uhn.fhir.jpa.util.HapiDerbyTenSevenDialect" />
</bean> </bean>
</property> </property>
</bean> </bean>

View File

@ -6,7 +6,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -31,33 +31,33 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<!-- At least one "structures" JAR must also be included --> <!-- At least one "structures" JAR must also be included -->
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId> <artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<!-- This dependency includes the JPA server itself, which is packaged separately from the rest of HAPI FHIR --> <!-- This dependency includes the JPA server itself, which is packaged separately from the rest of HAPI FHIR -->
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId> <artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<!-- This dependency is used for the "FHIR Tester" web app overlay --> <!-- This dependency is used for the "FHIR Tester" web app overlay -->
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId> <artifactId>hapi-fhir-testpage-overlay</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<type>war</type> <type>war</type>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>

View File

@ -20,6 +20,8 @@
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class> <class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
<class>ca.uhn.fhir.jpa.entity.ResourceTable</class> <class>ca.uhn.fhir.jpa.entity.ResourceTable</class>
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class> <class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
<class>ca.uhn.fhir.jpa.entity.SubscriptionTable</class>
<class>ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource</class>
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class> <class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes> <exclude-unlisted-classes>true</exclude-unlisted-classes>

View File

@ -21,7 +21,7 @@
and other properties supported by BasicDataSource. and other properties supported by BasicDataSource.
--> -->
<bean id="myPersistenceDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close"> <bean id="myPersistenceDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="url" value="jdbc:derby:directory:jpaserver_derby_files;create=true" /> <property name="url" value="jdbc:derby:directory:target/jpaserver_derby_files;create=true" />
<property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver"></property> <property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver"></property>
<property name="username" value=""/> <property name="username" value=""/>
<property name="password" value=""/> <property name="password" value=""/>
@ -40,7 +40,7 @@
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" /> <property name="showSql" value="false" />
<property name="generateDdl" value="true" /> <property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" /> <property name="databasePlatform" value="ca.uhn.fhir.jpa.util.HapiDerbyTenSevenDialect" />
</bean> </bean>
</property> </property>
</bean> </bean>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -18,27 +18,27 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId> <artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId> <artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId> <artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId> <artifactId>hapi-fhir-testpage-overlay</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<type>war</type> <type>war</type>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
@ -52,13 +52,6 @@
<artifactId>phloc-commons</artifactId> <artifactId>phloc-commons</artifactId>
</dependency> </dependency>
<!--dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-test</artifactId>
<version>0.9-SNAPSHOT</version>
<scope>test</scope>
</dependency-->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId> <artifactId>spring-web</artifactId>

View File

@ -20,11 +20,13 @@
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class> <class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
<class>ca.uhn.fhir.jpa.entity.ResourceTable</class> <class>ca.uhn.fhir.jpa.entity.ResourceTable</class>
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class> <class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
<class>ca.uhn.fhir.jpa.entity.SubscriptionTable</class>
<class>ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource</class>
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class> <class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes> <exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties> <properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyTenSevenDialect" /> <property name="hibernate.dialect" value="ca.uhn.fhir.jpa.util.HapiDerbyTenSevenDialect" />
<property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.jdbc.batch_size" value="20" /> <property name="hibernate.jdbc.batch_size" value="20" />
<property name="hibernate.cache.use_minimal_puts" value="true" /> <property name="hibernate.cache.use_minimal_puts" value="true" />

View File

@ -44,7 +44,7 @@
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" /> <property name="showSql" value="false" />
<property name="generateDdl" value="true" /> <property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" /> <property name="databasePlatform" value="ca.uhn.fhir.jpa.util.HapiDerbyTenSevenDialect" />
<!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" />--> <!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" />-->
<!-- <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" /> --> <!-- <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" /> -->
</bean> </bean>

View File

@ -31,7 +31,7 @@
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" /> <property name="showSql" value="false" />
<property name="generateDdl" value="true" /> <property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" /> <property name="databasePlatform" value="ca.uhn.fhir.jpa.util.HapiDerbyTenSevenDialect" />
<!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" /> --> <!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" /> -->
<!-- <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" /> --> <!-- <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" /> -->
</bean> </bean>

View File

@ -18,6 +18,7 @@
<util:list id="myServerInterceptors"> <util:list id="myServerInterceptors">
<ref bean="myLoggingInterceptor"/> <ref bean="myLoggingInterceptor"/>
<ref bean="mySubscriptionSecurityInterceptor"/>
</util:list> </util:list>
<!-- <!--
@ -42,6 +43,8 @@
<bean id="dbServer" class="ca.uhn.fhirtest.DerbyNetworkServer"> <bean id="dbServer" class="ca.uhn.fhirtest.DerbyNetworkServer">
</bean> </bean>
<bean id="mySubscriptionSecurityInterceptor" class="ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptor"/>
<!-- <!--
Do some fancy logging to create a nice access log that has details Do some fancy logging to create a nice access log that has details

View File

@ -42,7 +42,7 @@
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="false" /> <property name="showSql" value="false" />
<property name="generateDdl" value="true" /> <property name="generateDdl" value="true" />
<property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" /> <property name="databasePlatform" value="ca.uhn.fhir.jpa.util.HapiDerbyTenSevenDialect" />
</bean> </bean>
</property> </property>
</bean> </bean>

View File

@ -16,12 +16,14 @@
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class> <class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
<class>ca.uhn.fhir.jpa.entity.ResourceTable</class> <class>ca.uhn.fhir.jpa.entity.ResourceTable</class>
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class> <class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
<class>ca.uhn.fhir.jpa.entity.SubscriptionTable</class>
<class>ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes> <exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties> <properties>
<property name="hibernate.connection.url" value="jdbc:hsqldb:mem:unit-testing-jpa" /> <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:unit-testing-jpa" />
<property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" /> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" />
<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyTenSevenDialect" /> <property name="hibernate.dialect" value="ca.uhn.fhir.jpa.util.HapiDerbyTenSevenDialect" />
<property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.connection.username" value="sa" /> <property name="hibernate.connection.username" value="sa" />
<property name="hibernate.connection.password" value="" /> <property name="hibernate.connection.password" value="" />

View File

@ -1,31 +1,76 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>1.2-SNAPSHOT</version> <version>1.3-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>
<artifactId>hapi-fhir-osgi-core</artifactId> <artifactId>hapi-fhir-osgi-core</artifactId>
<packaging>jar</packaging> <packaging>bundle</packaging>
<url>http://jamesagnew.github.io/hapi-fhir/</url> <url>http://jamesagnew.github.io/hapi-fhir/</url>
<name>HAPI FHIR - OSGi Bundle</name> <name>HAPI FHIR - OSGi Bundle</name>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2-SNAPSHOT</version> <version>1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>1.3-SNAPSHOT</version>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>
<plugins>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Export-Package>ca.uhn.fhir</Export-Package>
<Export-Package>org.hl7.fhir</Export-Package>
<Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
<Embed-Dependency>*;scope=!provided|test</Embed-Dependency>
<Embed-Directory>lib</Embed-Directory>
<Embed-Transitive>true</Embed-Transitive>
<_removeheaders>Built-By</_removeheaders>
<!-- <Private-Package>org.foo.myproject.*</Private-Package> <Bundle-Activator>org.foo.myproject.impl1.Activator</Bundle-Activator> -->
</instructions>
</configuration>
<executions>
<execution>
<goals>
<goal>bundle</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
<resources> <resources>
<resource> <resource>
<directory>src/main/resources</directory> <directory>src/main/resources</directory>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>
@ -17,7 +17,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
@ -136,7 +136,7 @@
<plugin> <plugin>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId> <artifactId>hapi-tinder-plugin</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<executions> <executions>
<execution> <execution>
<goals> <goals>

View File

@ -10,12 +10,16 @@ import java.util.List;
import org.junit.Test; import org.junit.Test;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.model.primitive.DateTimeDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.method.QualifiedParamList; import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class DateRangeParamTest { public class DateRangeParamTest {
private static SimpleDateFormat ourFmt; private static SimpleDateFormat ourFmt;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DateRangeParamTest.class);
static { static {
ourFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS"); ourFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSS");
@ -25,6 +29,31 @@ public class DateRangeParamTest {
return new DateRangeParam(new DateParam(theString)); return new DateRangeParam(new DateParam(theString));
} }
@Test
public void testRange() {
InstantDt start = new InstantDt("2015-09-23T07:43:34.811-04:00");
InstantDt end = new InstantDt("2015-09-23T07:43:34.899-04:00");
DateParam lowerBound = new DateParam(QuantityCompararatorEnum.GREATERTHAN, start.getValue());
DateParam upperBound = new DateParam(QuantityCompararatorEnum.LESSTHAN, end.getValue());
assertEquals(QuantityCompararatorEnum.GREATERTHAN, lowerBound.getComparator());
assertEquals(QuantityCompararatorEnum.LESSTHAN, upperBound.getComparator());
/*
* When DateParam (which extends DateTimeDt) gets passed in, make sure we preserve the
* comparators..
*/
DateRangeParam param = new DateRangeParam(lowerBound, upperBound);
ourLog.info(param.toString());
assertEquals(QuantityCompararatorEnum.GREATERTHAN, param.getLowerBound().getComparator());
assertEquals(QuantityCompararatorEnum.LESSTHAN, param.getUpperBound().getComparator());
param = new DateRangeParam(new DateTimeDt(lowerBound.getValue()), new DateTimeDt(upperBound.getValue()));
ourLog.info(param.toString());
assertEquals(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, param.getLowerBound().getComparator());
assertEquals(QuantityCompararatorEnum.LESSTHAN_OR_EQUALS, param.getUpperBound().getComparator());
}
@Test @Test
public void testAddAnd() { public void testAddAnd() {
assertEquals(1, new DateAndListParam().addAnd(new DateOrListParam()).getValuesAsQueryTokens().size()); assertEquals(1, new DateAndListParam().addAnd(new DateOrListParam()).getValuesAsQueryTokens().size());

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>
@ -17,13 +17,13 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId> <artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
@ -144,7 +144,7 @@
<plugin> <plugin>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId> <artifactId>hapi-tinder-plugin</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<executions> <executions>
<execution> <execution>
<id>generate</id> <id>generate</id>

View File

@ -28,6 +28,7 @@ import ca.uhn.fhir.model.dstu2.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu2.resource.Binary; import ca.uhn.fhir.model.dstu2.resource.Binary;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.resource.Conformance;
import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport; import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport;
import ca.uhn.fhir.model.dstu2.resource.Medication; import ca.uhn.fhir.model.dstu2.resource.Medication;
import ca.uhn.fhir.model.dstu2.resource.MedicationOrder; import ca.uhn.fhir.model.dstu2.resource.MedicationOrder;
@ -474,17 +475,6 @@ public class JsonParserDstu2Test {
assertThat(ourCtx.newJsonParser().setOmitResourceId(true).encodeResourceToString(p), not(containsString("123"))); assertThat(ourCtx.newJsonParser().setOmitResourceId(true).encodeResourceToString(p), not(containsString("123")));
} }
@Test
public void testParseAndEncodeBundleResourceWithComments() throws Exception {
String content = IOUtils.toString(JsonParserDstu2Test.class.getResourceAsStream("/bundle-transaction2.json"));
ourCtx.newJsonParser().parseBundle(content);
ca.uhn.fhir.model.dstu2.resource.Bundle parsed = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, content);
// TODO: preserve comments
}
@Test @Test
public void testParseAndEncodeBundle() throws Exception { public void testParseAndEncodeBundle() throws Exception {
String content = IOUtils.toString(JsonParserDstu2Test.class.getResourceAsStream("/bundle-example.json")); String content = IOUtils.toString(JsonParserDstu2Test.class.getResourceAsStream("/bundle-example.json"));
@ -527,7 +517,7 @@ public class JsonParserDstu2Test {
assertEquals(exp, act); assertEquals(exp, act);
} }
/** /**
* Test for #146 * Test for #146
*/ */
@ -641,6 +631,17 @@ public class JsonParserDstu2Test {
} }
@Test
public void testParseAndEncodeBundleResourceWithComments() throws Exception {
String content = IOUtils.toString(JsonParserDstu2Test.class.getResourceAsStream("/bundle-transaction2.json"));
ourCtx.newJsonParser().parseBundle(content);
ca.uhn.fhir.model.dstu2.resource.Bundle parsed = ourCtx.newJsonParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, content);
// TODO: preserve comments
}
@Test @Test
public void testParseAndEncodeBundleWithDeletedEntry() { public void testParseAndEncodeBundleWithDeletedEntry() {
@ -856,7 +857,7 @@ public class JsonParserDstu2Test {
assertEquals(exp, act); assertEquals(exp, act);
} }
@Test @Test
public void testParsePatientInBundle() { public void testParsePatientInBundle() {
@ -917,6 +918,17 @@ public class JsonParserDstu2Test {
} }
} }
@Test
public void testParseWithWrongTypeObjectShouldBeArray() throws Exception {
String input = IOUtils.toString(getClass().getResourceAsStream("/invalid_metadata.json"));
try {
ourCtx.newJsonParser().parseResource(Conformance.class, input);
fail();
} catch (DataFormatException e) {
assertEquals("Syntax error parsing JSON FHIR structure: Expected ARRAY at element 'modifierExtension', found 'OBJECT'", e.getMessage());
}
}
/** /**
* See #144 and #146 * See #144 and #146
*/ */

View File

@ -51,11 +51,13 @@ public class SearchDstu2Test {
private static Server ourServer; private static Server ourServer;
private static String ourLastMethod; private static String ourLastMethod;
private static DateAndListParam ourLastDateAndList; private static DateAndListParam ourLastDateAndList;
private static ReferenceParam ourLastRef;
@Before @Before
public void before() { public void before() {
ourLastMethod = null; ourLastMethod = null;
ourLastDateAndList = null; ourLastDateAndList = null;
ourLastRef = null;
} }
@Test @Test
@ -68,6 +70,59 @@ public class SearchDstu2Test {
assertEquals(400, status.getStatusLine().getStatusCode()); assertEquals(400, status.getStatusLine().getStatusCode());
} }
@Test
public void testSearchReferenceParams01() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchNoList&ref=123");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("123", ourLastRef.getIdPart());
assertEquals(null, ourLastRef.getResourceType());
}
@Test
public void testSearchReferenceParams02() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchNoList&ref=Patient/123");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("123", ourLastRef.getIdPart());
assertEquals("Patient", ourLastRef.getResourceType());
}
@Test
public void testSearchReferenceParams03() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchNoList&ref:Patient=Patient/123");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("123", ourLastRef.getIdPart());
assertEquals("Patient", ourLastRef.getResourceType());
}
@Test
public void testSearchReferenceParams04() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchNoList&ref:Patient=123");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
IOUtils.closeQuietly(status.getEntity().getContent());
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertEquals("123", ourLastRef.getIdPart());
assertEquals("Patient", ourLastRef.getResourceType());
}
@Test @Test
public void testSearchDateAndList() throws Exception { public void testSearchDateAndList() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?searchDateAndList=2001,2002&searchDateAndList=2003,2004"); HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?searchDateAndList=2001,2002&searchDateAndList=2003,2004");
@ -220,7 +275,17 @@ public class SearchDstu2Test {
return Collections.emptyList(); return Collections.emptyList();
} }
//@formatter:on //@formatter:on
//@formatter:off
@Search(queryName="searchNoList")
public List<Patient> searchNoList(
@RequiredParam(name = "ref") ReferenceParam theParam) {
ourLastMethod = "searchNoList";
ourLastRef = theParam;
return Collections.emptyList();
}
//@formatter:on
//@formatter:off //@formatter:off
@Search() @Search()
public List<Patient> searchDateAndList( public List<Patient> searchDateAndList(

View File

@ -0,0 +1,65 @@
{
"resourceType": "Conformance",
"meta": {
"versionId": "0.0.1"
},
"status": "draft",
"experimental": true,
"date": "2015-09-23T12:00:00Z",
"fhirVersion": "DSTU 2 0.5.0",
"acceptUnknown": false,
"format": [
"json"
],
"rest": [
{
"mode": "server",
"documentation": "Information about the system's restful capabilities that apply across all applications, such as security",
"security": {
"cors": false,
"service": [
{
"coding": [
{
"system": "http://hl7.org/fhir/restful-security-service",
"code": "OAuth"
}
]
}
],
"description": "General description of how security works",
"certificate": [
{
"type": "json"
}
],
"modifierExtension": {
"url": "http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris"
},
"extension": {
"url": "http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris"
}
},
"resource": [
{
"type": "Patient",
"profile": {
"reference": "http://hl7.org/fhir/StructureDefinition/patient-daf-dafpatient"
},
"interaction": [
{
"code": "read"
},
{
"code": "update"
},
{
"code": "search-type"
}
],
"versioning": "no-version"
}
]
}
]
}

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>
@ -18,12 +18,12 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId> <artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -27,22 +27,22 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId> <artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId> <artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<!--<dependency> <!--<dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
@ -66,13 +66,6 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- <dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-test</artifactId>
<version>0.9-SNAPSHOT</version>
<scope>test</scope>
</dependency>-->
<dependency> <dependency>
<groupId>ch.qos.logback</groupId> <groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId> <artifactId>logback-classic</artifactId>
@ -111,12 +104,10 @@
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId> <artifactId>spring-webmvc</artifactId>
<version>${spring_version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId> <artifactId>spring-context</artifactId>
<version>${spring_version}</version>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>xml-apis</artifactId> <artifactId>xml-apis</artifactId>
@ -127,53 +118,44 @@
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId> <artifactId>spring-beans</artifactId>
<version>${spring_version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId> <artifactId>spring-tx</artifactId>
<version>${spring_version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId> <artifactId>spring-context-support</artifactId>
<version>${spring_version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId> <artifactId>spring-web</artifactId>
<version>${spring_version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlets</artifactId> <artifactId>jetty-servlets</artifactId>
<version>${jetty_version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId> <artifactId>jetty-webapp</artifactId>
<version>${jetty_version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId> <artifactId>jetty-server</artifactId>
<version>${jetty_version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId> <artifactId>jetty-servlet</artifactId>
<version>${jetty_version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.eclipse.jetty</groupId> <groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId> <artifactId>jetty-util</artifactId>
<version>${jetty_version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -21,13 +21,15 @@
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords</class> <class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords</class>
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class> <class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class> <class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
<class>ca.uhn.fhir.jpa.entity.SubscriptionTable</class>
<class>ca.uhn.fhir.jpa.entity.SubscriptionFlaggedResource</class>
<class>ca.uhn.fhir.jpa.entity.TagDefinition</class> <class>ca.uhn.fhir.jpa.entity.TagDefinition</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes> <exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties> <properties>
<property name="hibernate.connection.url" value="jdbc:hsqldb:mem:unit-testing-jpa" /> <property name="hibernate.connection.url" value="jdbc:hsqldb:mem:unit-testing-jpa" />
<property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" /> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" />
<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyTenSevenDialect" /> <property name="hibernate.dialect" value="ca.uhn.fhir.jpa.util.HapiDerbyTenSevenDialect" />
<property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.connection.username" value="sa" /> <property name="hibernate.connection.username" value="sa" />
<property name="hibernate.connection.password" value="" /> <property name="hibernate.connection.password" value="" />

View File

@ -38,7 +38,7 @@
<property name="showSql" value="false" /> <property name="showSql" value="false" />
<property name="generateDdl" value="true" /> <property name="generateDdl" value="true" />
<!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" /> --> <!-- <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" /> -->
<property name="databasePlatform" value="org.hibernate.dialect.DerbyTenSevenDialect" /> <property name="databasePlatform" value="ca.uhn.fhir.jpa.util.HapiDerbyTenSevenDialect" />
</bean> </bean>
</property> </property>
</bean> </bean>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -19,7 +19,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<!-- <!--
Because Tinder is a part of the HAPI FHIR build process (it generates Because Tinder is a part of the HAPI FHIR build process (it generates

View File

@ -44,11 +44,11 @@ public class ${className}ResourceProvider extends
@Description(shortDefinition="The resource identity") @Description(shortDefinition="The resource identity")
@OptionalParam(name="_id") @OptionalParam(name="_id")
StringParam theId, StringAndListParam theId,
@Description(shortDefinition="The resource language") @Description(shortDefinition="The resource language")
@OptionalParam(name="_language") @OptionalParam(name="_language")
StringParam theResourceLanguage, StringAndListParam theResourceLanguage,
@Description(shortDefinition="Search for resources which have the given tag") @Description(shortDefinition="Search for resources which have the given tag")
@OptionalParam(name=ca.uhn.fhir.rest.server.Constants.PARAM_TAG) @OptionalParam(name=ca.uhn.fhir.rest.server.Constants.PARAM_TAG)

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xmlns:util="http://www.springframework.org/schema/util" xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:task="http://www.springframework.org/schema/task" xmlns:util="http://www.springframework.org/schema/util"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
default-autowire="no" default-lazy-init="false" default-autowire="no" default-lazy-init="false"
xsi:schemaLocation=" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
@ -8,6 +9,7 @@
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
" "
> >
@ -21,6 +23,12 @@
</bean> </bean>
#if ( ${versionCapitalized} == 'Dstu2' ) #if ( ${versionCapitalized} == 'Dstu2' )
<jpa:repositories base-package="ca.uhn.fhir.jpa.dao.data" />
<task:annotation-driven executor="myExecutor" scheduler="myScheduler"/>
<task:executor id="myExecutor" pool-size="5"/>
<task:scheduler id="myScheduler" pool-size="10"/>
<bean id="myJpaValidationSupport${versionCapitalized}" class="ca.uhn.fhir.jpa.dao.JpaValidationSupport${versionCapitalized}"/> <bean id="myJpaValidationSupport${versionCapitalized}" class="ca.uhn.fhir.jpa.dao.JpaValidationSupport${versionCapitalized}"/>
#end #end
@ -33,7 +41,7 @@
#foreach ( $res in $resources ) #foreach ( $res in $resources )
<bean id="my${res.name}Dao${versionCapitalized}" <bean id="my${res.name}Dao${versionCapitalized}"
## Some resource types have customized DAOs for resource specific functionality ## Some resource types have customized DAOs for resource specific functionality
#if ( ${versionCapitalized} == 'Dstu2' && ( ${res.name} == 'Bundle' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'ValueSet')) #if ( ${versionCapitalized} == 'Dstu2' && ( ${res.name} == 'Bundle' || ${res.name} == 'Subscription' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'ValueSet'))
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${res.name}${versionCapitalized}"> class="ca.uhn.fhir.jpa.dao.FhirResourceDao${res.name}${versionCapitalized}">
#else #else
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${versionCapitalized}"> class="ca.uhn.fhir.jpa.dao.FhirResourceDao${versionCapitalized}">

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -17,12 +17,12 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId> <artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
@ -45,7 +45,7 @@
<plugin> <plugin>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId> <artifactId>hapi-tinder-plugin</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<executions> <executions>
<execution> <execution>
<id>custom-structs</id> <id>custom-structs</id>
@ -104,7 +104,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu</artifactId> <artifactId>hapi-fhir-structures-dstu</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
</dependencies> </dependencies>
</plugin> </plugin>

45
pom.xml
View File

@ -11,7 +11,7 @@
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<name>HAPI-FHIR</name> <name>HAPI-FHIR</name>
<url>http://jamesagnew.github.io/hapi-fhir/</url> <url>http://jamesagnew.github.io/hapi-fhir/</url>
@ -214,15 +214,13 @@
<derby_version>10.11.1.1</derby_version> <derby_version>10.11.1.1</derby_version>
<!-- Note on Hibernate versions: Hibernate 4.3+ uses JPA 2.1, which is too new for a number of platforms including JBoss EAP 6.x and Glassfish 3.0. Upgrade this <!-- Note on Hibernate versions: Hibernate 4.3+ uses JPA 2.1, which is too new for a number of platforms including JBoss EAP 6.x and Glassfish 3.0. Upgrade this
version with caution! Also note that if you change this, you may get a failure in hibernate4-maven-plugin. See the note in hapi-fhir-jpaserver-base/pom.xml's configuration version with caution! Also note that if you change this, you may get a failure in hibernate4-maven-plugin. See the note in hapi-fhir-jpaserver-base/pom.xml's configuration
for that plugin... <hibernate_version>4.3.7.Final</hibernate_version> --> for that plugin... -->
<hibernate_version>4.2.17.Final</hibernate_version> <hibernate_version>5.0.1.Final</hibernate_version>
<hibernate_validator_version>5.1.0.Final</hibernate_validator_version> <hibernate_validator_version>5.2.1.Final</hibernate_validator_version>
<jetty_version>9.2.6.v20141205</jetty_version> <jetty_version>9.2.6.v20141205</jetty_version>
<maven_build_helper_plugin_version>1.9.1</maven_build_helper_plugin_version> <maven_build_helper_plugin_version>1.9.1</maven_build_helper_plugin_version>
<maven_assembly_plugin_version>2.5.3</maven_assembly_plugin_version> <maven_assembly_plugin_version>2.5.3</maven_assembly_plugin_version>
<maven_failsafe_plugin_version>2.18.1</maven_failsafe_plugin_version> <maven_failsafe_plugin_version>2.18.1</maven_failsafe_plugin_version>
<maven_gpg_plugin_version>1.6</maven_gpg_plugin_version>
<maven_jxr_plugin_version>2.5</maven_jxr_plugin_version>
<maven_license_plugin_version>1.8</maven_license_plugin_version> <maven_license_plugin_version>1.8</maven_license_plugin_version>
<maven_project_info_plugin_version>2.8</maven_project_info_plugin_version> <maven_project_info_plugin_version>2.8</maven_project_info_plugin_version>
<maven_surefire_plugin_version>2.18.1</maven_surefire_plugin_version> <maven_surefire_plugin_version>2.18.1</maven_surefire_plugin_version>
@ -231,7 +229,7 @@
<mitreid-connect-version>1.1.8</mitreid-connect-version> <mitreid-connect-version>1.1.8</mitreid-connect-version>
<phloc_schematron_version>2.7.1</phloc_schematron_version> <phloc_schematron_version>2.7.1</phloc_schematron_version>
<phloc_commons_version>4.3.6</phloc_commons_version> <phloc_commons_version>4.3.6</phloc_commons_version>
<spring_version>4.1.5.RELEASE</spring_version> <spring_version>4.2.1.RELEASE</spring_version>
<thymeleaf-version>2.1.4.RELEASE</thymeleaf-version> <thymeleaf-version>2.1.4.RELEASE</thymeleaf-version>
<ebay_cors_filter_version>1.0.1</ebay_cors_filter_version> <ebay_cors_filter_version>1.0.1</ebay_cors_filter_version>
<xmlunit_version>1.6</xmlunit_version> <xmlunit_version>1.6</xmlunit_version>
@ -505,6 +503,11 @@
<artifactId>spring-core</artifactId> <artifactId>spring-core</artifactId>
<version>${spring_version}</version> <version>${spring_version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>1.9.0.RELEASE</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId> <artifactId>spring-orm</artifactId>
@ -525,6 +528,11 @@
<artifactId>spring-web</artifactId> <artifactId>spring-web</artifactId>
<version>${spring_version}</version> <version>${spring_version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring_version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.thymeleaf</groupId> <groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId> <artifactId>thymeleaf</artifactId>
@ -541,6 +549,16 @@
<build> <build>
<pluginManagement> <pluginManagement>
<plugins> <plugins>
<plugin>
<groupId>de.juplo</groupId>
<artifactId>hibernate4-maven-plugin</artifactId>
<version>1.1.0</version>
</plugin>
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.5.4</version>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId> <artifactId>maven-antrun-plugin</artifactId>
@ -585,11 +603,21 @@
<artifactId>maven-deploy-plugin</artifactId> <artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version> <version>2.8.2</version>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.1</version> <version>2.10.1</version>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
<version>2.5</version>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId> <artifactId>maven-failsafe-plugin</artifactId>
@ -1218,7 +1246,6 @@
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId> <artifactId>maven-gpg-plugin</artifactId>
<version>${maven_gpg_plugin_version}</version>
<executions> <executions>
<execution> <execution>
<id>sign-artifacts</id> <id>sign-artifacts</id>
@ -1290,7 +1317,7 @@
<module>hapi-fhir-cli</module> <module>hapi-fhir-cli</module>
<module>hapi-fhir-dist</module> <module>hapi-fhir-dist</module>
<module>examples</module> <module>examples</module>
<!--<module>hapi-fhir-osgi-core</module>--> <module>hapi-fhir-osgi-core</module>
</modules> </modules>
</profile> </profile>
<profile> <profile>

View File

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -17,12 +17,12 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -8,13 +8,13 @@
<parent> <parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>restful-server-example</artifactId> <artifactId>restful-server-example</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<packaging>war</packaging> <packaging>war</packaging>
<name>HAPI FHIR Sample RESTful Server</name> <name>HAPI FHIR Sample RESTful Server</name>
@ -35,20 +35,20 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId> <artifactId>hapi-fhir-base</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<!-- At least one "structures" JAR must also be included --> <!-- At least one "structures" JAR must also be included -->
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId> <artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
</dependency> </dependency>
<!-- This dependency is used for the "FHIR Tester" web app overlay --> <!-- This dependency is used for the "FHIR Tester" web app overlay -->
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-testpage-overlay</artifactId> <artifactId>hapi-fhir-testpage-overlay</artifactId>
<version>1.2</version> <version>1.3-SNAPSHOT</version>
<type>war</type> <type>war</type>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>

View File

@ -6,6 +6,50 @@
<title>HAPI FHIR Changelog</title> <title>HAPI FHIR Changelog</title>
</properties> </properties>
<body> <body>
<release version="1.3" date="TBD">
<action type="add">
Bump the version of a few dependencies to the
latest versions (dependent HAPI modules listed in brackets):
<![CDATA[
<ul>
<li>Springframework (JPA, Web Tester): 4.1.5 -> 4.2.1</li>
<li>Hibernate (JPA, Web Tester): 4.2.17 -> 5.0.1</li>
</ul>
]]>
</action>
<action type="add">
JPA server removes duplicate resource index entries before storing them
(e.g. if a patient has the same name twice, only one index entry is created
for that name)
</action>
<action type="fix">
JPA server did not correctly index search parameters of type "reference" where the
path had multiple entries (i.e. "Resource.path1 | Resource.path2")
</action>
<action type="fix">
JPA server _history operations (server, type, instance) not correctly set the
Bundle.entry.request.method to POST or PUT for create and updates of the resource.
</action>
<action type="add" issue="225">
Support AND/OR on _id search parameter in JPA
</action>
<action type="fix">
Constructor for DateRanfeParam which dates in two DateParam instances was ignoring
comparators on the DateParam.
</action>
<action type="fix">
In JSON parsing, finding an object where an array was expected led to an unhelpful
error message. Thanks to Avinash Shanbhag for reporting!
</action>
<action type="add">
JPA server gave an unhelpful error message if $meta-add or $meta-delete were called
with no meta elements in the input Parameters
</action>
<action type="fix">
Narrative generator did not include OperationOutcome.issue.diagnostics in the
generated narrative.
</action>
</release>
<release version="1.2" date="2015-09-18"> <release version="1.2" date="2015-09-18">
<action type="add"> <action type="add">
JPA server now validates QuestionnaireAnswers for conformance to their respective Questionnaire JPA server now validates QuestionnaireAnswers for conformance to their respective Questionnaire