Lots more JPA work, and getting sorting in`

This commit is contained in:
jamesagnew 2014-05-20 08:44:55 -04:00
parent 27dab8a442
commit 5675237f40
68 changed files with 2123 additions and 693 deletions

View File

@ -166,4 +166,26 @@ public class Bundle extends BaseBundle /*implements IElement*/ {
return retVal;
}
/**
* Returns the number of entries in this bundle
*/
public int size() {
return getEntries().size();
}
/**
* Returns a list containing all resources of the given type from this bundle
*/
public <T extends IResource> List<T> getResources(Class<T> theClass) {
ArrayList<T> retVal = new ArrayList<T>();
for (BundleEntry next : getEntries()) {
if (next.getResource()!=null && theClass.isAssignableFrom(next.getResource().getClass())) {
@SuppressWarnings("unchecked")
T resource = (T) next.getResource();
retVal.add(resource);
}
}
return retVal;
}
}

View File

@ -45,7 +45,7 @@ public interface IQueryParameterAnd {
* for information on the <b>token</b> format
* </p>
*/
public List<QualifiedParamList> getValuesAsQueryTokens();
public List<IQueryParameterOr> getValuesAsQueryTokens();
}

View File

@ -20,32 +20,33 @@ package ca.uhn.fhir.model.api;
* #L%
*/
import java.util.List;
import ca.uhn.fhir.rest.method.QualifiedParamList;
public interface IQueryParameterOr {
/**
* Sets the value of this type using the <b>token</b> format. This
* format is used in HTTP queries as a parameter format.
* Sets the value of this type using the <b>token</b> format. This format is used in HTTP queries as a parameter
* format.
* <p>
* See FHIR specification
* <a href="http://www.hl7.org/implement/standards/fhir/search.html#ptypes">2.2.2 Search SearchParameter Types</a>
* for information on the <b>token</b> format
* See FHIR specification <a href="http://www.hl7.org/implement/standards/fhir/search.html#ptypes">2.2.2 Search
* SearchParameter Types</a> for information on the <b>token</b> format
* </p>
*/
public void setValuesAsQueryTokens(QualifiedParamList theParameters);
// public void setValuesAsQueryTokens(List<IQueryParameterType> theParameters);
/**
* Returns the value of this type using the <b>token</b> format. This
* format is used in HTTP queries as a parameter format.
* Returns the value of this type using the <b>token</b> format. This format is used in HTTP queries as a parameter
* format.
*
* <p>
* See FHIR specification
* <a href="http://www.hl7.org/implement/standards/fhir/search.html#ptypes">2.2.2 Search SearchParameter Types</a>
* for information on the <b>token</b> format
* See FHIR specification <a href="http://www.hl7.org/implement/standards/fhir/search.html#ptypes">2.2.2 Search
* SearchParameter Types</a> for information on the <b>token</b> format
* </p>
*/
public QualifiedParamList getValuesAsQueryTokens();
public List<IQueryParameterType> getValuesAsQueryTokens();
}

View File

@ -175,8 +175,7 @@ public class ResourceReferenceDt
* </p>
*/
public ResourceReferenceDt setReference( String theString) {
myReference = new StringDt(theString);
return this;
return setReference(new StringDt(theString));
}

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.parser;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.io.ObjectInputStream.GetField;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -39,6 +40,7 @@ import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
import ca.uhn.fhir.context.RuntimeElemContainedResources;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeNarrativeDefinition;
@ -663,7 +665,7 @@ class ParserState<T> {
case RESOURCE_REF: {
ResourceReferenceDt newChildInstance = new ResourceReferenceDt();
myDefinition.getMutator().addValue(myParentInstance, newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(getPreResourceState(), newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(getPreResourceState(), (RuntimeResourceReferenceDefinition)target, newChildInstance);
push(newState);
return;
}
@ -767,7 +769,7 @@ class ParserState<T> {
ResourceReferenceDt newChildInstance = new ResourceReferenceDt();
getPreResourceState().getResourceReferences().add(newChildInstance);
child.getMutator().addValue(myInstance, newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(getPreResourceState(), newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(getPreResourceState(), resourceRefTarget, newChildInstance);
push(newState);
return;
}
@ -873,7 +875,7 @@ class ParserState<T> {
case RESOURCE_REF: {
ResourceReferenceDt newChildInstance = new ResourceReferenceDt();
myExtension.setValue(newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(getPreResourceState(), newChildInstance);
ResourceReferenceState newState = new ResourceReferenceState(getPreResourceState(), null, newChildInstance);
push(newState);
return;
}
@ -1125,7 +1127,7 @@ class ParserState<T> {
private ResourceReferenceDt myInstance;
private ResourceReferenceSubState mySubState;
public ResourceReferenceState(PreResourceState thePreResourceState, ResourceReferenceDt theInstance) {
public ResourceReferenceState(PreResourceState thePreResourceState, RuntimeResourceReferenceDefinition theTarget, ResourceReferenceDt theInstance) {
super(thePreResourceState);
myInstance = theInstance;
mySubState = ResourceReferenceSubState.INITIAL;
@ -1144,6 +1146,25 @@ class ParserState<T> {
case INITIAL:
throw new DataFormatException("Unexpected attribute: " + theValue);
case REFERENCE:
int lastSlash = theValue.lastIndexOf('/');
if (lastSlash==-1) {
myInstance.setResourceId(theValue);
} else if (lastSlash==0) {
myInstance.setResourceId(theValue.substring(1));
}else {
int secondLastSlash=theValue.lastIndexOf('/', lastSlash-1);
String resourceTypeName;
if (secondLastSlash==-1) {
resourceTypeName=theValue.substring(0,lastSlash);
}else {
resourceTypeName=theValue.substring(secondLastSlash+1,lastSlash);
}
myInstance.setResourceId(theValue.substring(lastSlash+1));
RuntimeResourceDefinition def = myContext.getResourceDefinition(resourceTypeName);
if(def!=null) {
myInstance.setResourceType(def.getImplementingClass());
}
}
myInstance.setReference(theValue);
break;
}

View File

@ -21,35 +21,23 @@ package ca.uhn.fhir.rest.api;
*/
/**
* Represents values for <a
* href="http://hl7.org/implement/standards/fhir/search.html#sort">sorting</a>
* resources returned by a server.
* Represents values for <a href="http://hl7.org/implement/standards/fhir/search.html#sort">sorting</a> resources
* returned by a server.
*/
public class SortSpec {
private String myFieldName;
private SortSpec myChain;
private String myFieldName;
private SortOrderEnum myOrder;
/**
* Gets the chained sort specification, or <code>null</code> if none. If
* multiple sort parameters are chained (indicating a sub-sort), the second
* level sort is chained via this property.
* Gets the chained sort specification, or <code>null</code> if none. If multiple sort parameters are chained
* (indicating a sub-sort), the second level sort is chained via this property.
*/
public SortSpec getChain() {
return myChain;
}
/**
* Sets the chained sort specification, or <code>null</code> if none. If
* multiple sort parameters are chained (indicating a sub-sort), the second
* level sort is chained via this property.
*/
public void setChain(SortSpec theChain) {
myChain = theChain;
}
private SortOrderEnum myOrder;
/**
* Returns the actual name of the field to sort by
*/
@ -58,16 +46,22 @@ public class SortSpec {
}
/**
* Returns the sort order specified by this parameter, or <code>null</code>
* if none is explicitly defined (which means {@link SortOrderEnum#ASC}
* according to the <a
* href="http://hl7.org/implement/standards/fhir/search.html#sort">FHIR
* specification</a>)
* Returns the sort order specified by this parameter, or <code>null</code> if none is explicitly defined (which
* means {@link SortOrderEnum#ASC} according to the <a
* href="http://hl7.org/implement/standards/fhir/search.html#sort">FHIR specification</a>)
*/
public SortOrderEnum getOrder() {
return myOrder;
}
/**
* Sets the chained sort specification, or <code>null</code> if none. If multiple sort parameters are chained
* (indicating a sub-sort), the second level sort is chained via this property.
*/
public void setChain(SortSpec theChain) {
myChain = theChain;
}
/**
* Sets the actual name of the field to sort by
*/
@ -76,11 +70,9 @@ public class SortSpec {
}
/**
* Sets the sort order specified by this parameter, or <code>null</code> if
* none is explicitly defined (which means {@link SortOrderEnum#ASC}
* according to the <a
* href="http://hl7.org/implement/standards/fhir/search.html#sort">FHIR
* specification</a>)
* Sets the sort order specified by this parameter, or <code>null</code> if none is explicitly defined (which means
* {@link SortOrderEnum#ASC} according to the <a
* href="http://hl7.org/implement/standards/fhir/search.html#sort">FHIR specification</a>)
*/
public void setOrder(SortOrderEnum theOrder) {
myOrder = theOrder;

View File

@ -154,16 +154,25 @@ public abstract class BaseClient {
}
if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() > 299) {
BaseServerResponseException exception = BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase());
String body=null;
try {
String body = IOUtils.toString(reader);
exception.setResponseBody(body);
body = IOUtils.toString(reader);
} catch (Exception e) {
ourLog.debug("Failed to read input stream", e);
} finally {
IOUtils.closeQuietly(reader);
}
String message = "HTTP " + response.getStatusLine().getStatusCode()+" " +response.getStatusLine().getReasonPhrase();
if (Constants.CT_TEXT.equals(mimeType)) {
message = message+": " + body;
}
BaseServerResponseException exception = BaseServerResponseException.newInstance(response.getStatusLine().getStatusCode(), message);
if(body!=null) {
exception.setResponseBody(body);
}
throw exception;
}

View File

@ -431,6 +431,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
params.get(parameterName).add(parameterValue);
}
@Override
public IQuery prettyPrint() {
setPrettyPrint(true);
return this;
}
}
private class QueryInternal implements IUntypedQuery {

View File

@ -45,5 +45,7 @@ public interface IQuery {
* can be useful for debugging, but is generally not desirable in a production situation.
*/
IQuery andLogRequestAndResponse(boolean theLogRequestAndResponse);
IQuery prettyPrint();
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.gclient;
import ca.uhn.fhir.model.primitive.IdDt;
/*
* #%L
* HAPI FHIR Library
@ -38,6 +40,14 @@ public class ReferenceParam implements IParam {
return new ReferenceChainCriterion(getParamName(), theICriterion);
}
/**
* Match the referenced resource if the resource has the given ID (this can be
* the logical ID or the absolute URL of the resource)
*/
public ICriterion hasId(IdDt theId) {
return new StringCriterion(getParamName(), theId.getValueAsString());
}
/**
* Match the referenced resource if the resource has the given ID (this can be
* the logical ID or the absolute URL of the resource)
@ -48,8 +58,8 @@ public class ReferenceParam implements IParam {
private static class ReferenceChainCriterion implements ICriterion, ICriterionInternal {
private ICriterionInternal myWrappedCriterion;
private String myParamName;
private ICriterionInternal myWrappedCriterion;
public ReferenceChainCriterion(String theParamName, ICriterion theWrappedCriterion) {
myParamName = theParamName;
@ -57,13 +67,13 @@ public class ReferenceParam implements IParam {
}
@Override
public String getParameterValue() {
return myWrappedCriterion.getParameterValue();
public String getParameterName() {
return myParamName + "." + myWrappedCriterion.getParameterName();
}
@Override
public String getParameterName() {
return myParamName + "." + myWrappedCriterion.getParameterName();
public String getParameterValue() {
return myWrappedCriterion.getParameterValue();
}
}

View File

@ -23,6 +23,10 @@ package ca.uhn.fhir.rest.method;
import java.util.ArrayList;
import java.util.StringTokenizer;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
public class QualifiedParamList extends ArrayList<String> {
private static final long serialVersionUID = 1L;
@ -37,6 +41,15 @@ public class QualifiedParamList extends ArrayList<String> {
super(theCapacity);
}
public QualifiedParamList(FhirContext theContext, IQueryParameterOr theNextOr) {
for (IQueryParameterType next : theNextOr.getValuesAsQueryTokens()) {
if (myQualifier==null) {
myQualifier=next.getQueryParameterQualifier(theContext);
}
add(next.getValueAsQueryToken());
}
}
public String getQualifier() {
return myQualifier;
}

View File

@ -159,6 +159,8 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
return false;
}
} else {
methodParamsTemp.add(name);
}
}
if (myQueryName != null) {
@ -181,11 +183,17 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
methodParamsTemp.add(next);
}
}
boolean retVal = methodParamsTemp.containsAll(theRequest.getParameters().keySet());
Set<String> keySet = theRequest.getParameters().keySet();
for (String next : keySet) {
if (next.startsWith("_")) {
continue;
}
if (!methodParamsTemp.contains(next)) {
return false;
}
}
ourLog.trace("Method {} matches: {}", getMethod().getName(), retVal);
return retVal;
return true;
}
public void setResourceType(Class<?> resourceType) {

View File

@ -26,6 +26,7 @@ import java.util.Iterator;
import java.util.List;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.rest.method.QualifiedParamList;
@ -97,10 +98,10 @@ public class CodingListParam implements IQueryParameterOr, Iterable<CodingDt> {
}
@Override
public QualifiedParamList getValuesAsQueryTokens() {
QualifiedParamList retVal = new QualifiedParamList();
public List<IQueryParameterType> getValuesAsQueryTokens() {
ArrayList<IQueryParameterType> retVal = new ArrayList<IQueryParameterType>();
for (CodingDt next : myCodings) {
retVal.add(next.getValueAsQueryToken());
retVal.add(next);
}
return retVal;
}

View File

@ -25,6 +25,7 @@ import java.util.Date;
import java.util.List;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.method.QualifiedParamList;
@ -156,13 +157,13 @@ public class DateRangeParam implements IQueryParameterAnd {
}
@Override
public List<QualifiedParamList> getValuesAsQueryTokens() {
ArrayList<QualifiedParamList> retVal = new ArrayList<QualifiedParamList>();
public List<IQueryParameterOr> getValuesAsQueryTokens() {
ArrayList<IQueryParameterOr> retVal = new ArrayList<IQueryParameterOr>();
if (myLowerBound != null) {
retVal.add(QualifiedParamList.singleton(myLowerBound.getValueAsQueryToken()));
retVal.add(ParameterUtil.singleton(myLowerBound));
}
if (myUpperBound != null) {
retVal.add(QualifiedParamList.singleton(myUpperBound.getValueAsQueryToken()));
retVal.add(ParameterUtil.singleton(myUpperBound));
}
return retVal;
}

View File

@ -23,13 +23,14 @@ package ca.uhn.fhir.rest.param;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
interface IParamBinder {
List<QualifiedParamList> encode(FhirContext theContext, Object theString) throws InternalErrorException;
List<IQueryParameterOr> encode(FhirContext theContext, Object theString) throws InternalErrorException;
Object parse(List<QualifiedParamList> theList) throws InternalErrorException, InvalidRequestException;

View File

@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.List;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.rest.method.QualifiedParamList;
@ -39,10 +40,10 @@ public class IdentifierListParam implements IQueryParameterOr {
}
@Override
public QualifiedParamList getValuesAsQueryTokens() {
QualifiedParamList retVal = new QualifiedParamList();
public List<IQueryParameterType> getValuesAsQueryTokens() {
ArrayList<IQueryParameterType> retVal = new ArrayList<IQueryParameterType>();
for (IdentifierDt next : myIdentifiers) {
retVal.add(next.getValueAsQueryToken());
retVal.add(next);
}
return retVal;
}

View File

@ -41,6 +41,8 @@ import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.time.DateUtils;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.PathSpecification;
import ca.uhn.fhir.model.api.TagList;
@ -56,7 +58,9 @@ import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.ServerBase;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.annotation.VersionIdParam;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.util.ReflectionUtil;
public class ParameterUtil {
@ -69,7 +73,7 @@ public class ParameterUtil {
intTypes.add(IntegerDt.class);
intTypes.add(Integer.class);
BINDABLE_INTEGER_TYPES = Collections.unmodifiableSet(intTypes);
HashSet<Class<?>> timeTypes = new HashSet<Class<?>>();
timeTypes.add(InstantDt.class);
timeTypes.add(Date.class);
@ -135,6 +139,7 @@ public class ParameterUtil {
public static Set<Class<?>> getBindableInstantTypes() {
return BINDABLE_TIME_TYPES;
}
public static Set<Class<?>> getBindableIntegerTypes() {
return BINDABLE_INTEGER_TYPES;
}
@ -153,7 +158,7 @@ public class ParameterUtil {
Class<? extends java.util.Collection<?>> innerCollectionType = null;
if (TagList.class.isAssignableFrom(parameterType)) {
// TagList is handled directly within the method bindings
param=new NullParameter();
param = new NullParameter();
} else {
if (Collection.class.isAssignableFrom(parameterType)) {
innerCollectionType = (Class<? extends java.util.Collection<?>>) parameterType;
@ -218,6 +223,8 @@ public class ParameterUtil {
param = new SinceParameter();
} else if (nextAnnotation instanceof Count) {
param = new CountParameter();
} else if (nextAnnotation instanceof Sort) {
param = new SortParameter();
} else {
continue;
}
@ -227,7 +234,7 @@ public class ParameterUtil {
}
if (param == null) {
throw new ConfigurationException("Parameter #" + paramIndex + " of method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName()
throw new ConfigurationException("Parameter #" + ((paramIndex+1))+"/" + (parameterTypes.length) + " of method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName()
+ "' has no recognized FHIR interface parameter annotations. Don't know how to handle this parameter");
}
@ -290,4 +297,25 @@ public class ParameterUtil {
return null;
}
public static IQueryParameterOr singleton(final IQueryParameterType theParam) {
return new IQueryParameterOr() {
@Override
public void setValuesAsQueryTokens(QualifiedParamList theParameters) {
if (theParameters.isEmpty()) {
return;
}
if (theParameters.size() > 1) {
throw new IllegalArgumentException("Type " + theParam.getClass().getCanonicalName() + " does not support multiple values");
}
theParam.setValueAsQueryToken(theParameters.getQualifier(), theParameters.get(0));
}
@Override
public List<IQueryParameterType> getValuesAsQueryTokens() {
return Collections.singletonList(theParam);
}
};
}
}

View File

@ -24,6 +24,7 @@ import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -36,8 +37,8 @@ final class QueryParameterAndBinder implements IParamBinder {
}
@Override
public List<QualifiedParamList> encode(FhirContext theContext, Object theString) throws InternalErrorException {
List<QualifiedParamList> retVal = ((IQueryParameterAnd) theString).getValuesAsQueryTokens();
public List<IQueryParameterOr> encode(FhirContext theContext, Object theString) throws InternalErrorException {
List<IQueryParameterOr> retVal = ((IQueryParameterAnd) theString).getValuesAsQueryTokens();
return retVal;
}

View File

@ -37,8 +37,8 @@ final class QueryParameterOrBinder implements IParamBinder {
}
@Override
public List<QualifiedParamList> encode(FhirContext theContext, Object theString) throws InternalErrorException {
QualifiedParamList retVal = ((IQueryParameterOr) theString).getValuesAsQueryTokens();
public List<IQueryParameterOr> encode(FhirContext theContext, Object theString) throws InternalErrorException {
IQueryParameterOr retVal = ((IQueryParameterOr) theString);
return Collections.singletonList(retVal);
}

View File

@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -37,11 +38,9 @@ final class QueryParameterTypeBinder implements IParamBinder {
}
@Override
public List<QualifiedParamList> encode(FhirContext theContext, Object theString) throws InternalErrorException {
public List<IQueryParameterOr> encode(FhirContext theContext, Object theString) throws InternalErrorException {
IQueryParameterType param = (IQueryParameterType) theString;
String retVal = param.getValueAsQueryToken();
String qualifier=param.getQueryParameterQualifier(theContext);
return Collections.singletonList(QualifiedParamList.singleton(qualifier, retVal));
return Collections.singletonList(ParameterUtil.singleton(param));
}
@Override

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.rest.param;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
@ -27,7 +28,7 @@ import ca.uhn.fhir.model.api.IResource;
public class ReferenceParam implements IQueryParameterType {
private String myChain;
private Class<? extends IResource> myType;
private String myResourceType;
private String myValue;
public ReferenceParam() {
@ -42,8 +43,8 @@ public class ReferenceParam implements IQueryParameterType {
setChain(theChain);
}
public ReferenceParam(Class<? extends IResource> theType, String theChain, String theValue) {
setType(theType);
public ReferenceParam(String theResourceType, String theChain, String theValue) {
setResourceType(theResourceType);
setValueAsQueryToken(null, theValue);
setChain(theChain);
}
@ -52,8 +53,25 @@ public class ReferenceParam implements IQueryParameterType {
return myChain;
}
public Class<? extends IResource> getType() {
return myType;
@Override
public String getQueryParameterQualifier(FhirContext theContext) {
StringBuilder b = new StringBuilder();
if (isNotBlank(myResourceType)) {
b.append(':');
b.append(myResourceType);
}
if (isNotBlank(myChain)) {
b.append('.');
b.append(myChain);
}
if (b.length() != 0) {
return b.toString();
}
return null;
}
public String getResourceType() {
return myResourceType;
}
@Override
@ -65,21 +83,39 @@ public class ReferenceParam implements IQueryParameterType {
myChain = theChain;
}
public void setType(Class<? extends IResource> theType) {
myType = theType;
public Class<? extends IResource> getResourceType(FhirContext theCtx) {
if (isBlank(myResourceType)) {
return null;
}
return theCtx.getResourceDefinition(myResourceType).getImplementingClass();
}
public void setResourceType(String theResourceType) {
myResourceType = theResourceType;
}
@Override
public void setValueAsQueryToken(String theQualifier, String theValue) {
String q = theQualifier;
if (isNotBlank(q)) {
if (q.startsWith(":")) {
int nextIdx = q.indexOf('.');
if (nextIdx != -1) {
myResourceType = q.substring(1, nextIdx);
myChain = q.substring(nextIdx + 1);
} else {
myResourceType = q.substring(1);
}
} else if (q.startsWith(".")) {
myChain = q.substring(1);
}
}
myValue = theValue;
}
@Override
public String getQueryParameterQualifier(FhirContext theContext) {
if (myType != null) {
return ":" + theContext.getResourceDefinition(myType).getName();
}
return null;
public String getValue() {
return myValue;
}
}

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.rest.param;
* #L%
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@ -63,7 +64,14 @@ public class SearchParameter extends BaseQueryParameter {
*/
@Override
public List<QualifiedParamList> encode(FhirContext theContext, Object theObject) throws InternalErrorException {
return myParamBinder.encode(theContext, theObject);
ArrayList<QualifiedParamList> retVal = new ArrayList<QualifiedParamList>();
List<IQueryParameterOr> val = myParamBinder.encode(theContext, theObject);
for (IQueryParameterOr nextOr : val) {
retVal.add(new QualifiedParamList(theContext, nextOr));
}
return retVal;
}
public String getDescription() {
@ -131,7 +139,7 @@ public class SearchParameter extends BaseQueryParameter {
} else if (IQueryParameterAnd.class.isAssignableFrom(type)) {
myParamBinder = new QueryParameterAndBinder((Class<? extends IQueryParameterAnd>) type);
} else if (String.class.equals(type)) {
myParamBinder=new StringBinder();
myParamBinder = new StringBinder();
} else {
throw new ConfigurationException("Unsupported data type for parameter: " + type.getCanonicalName());
}
@ -150,6 +158,8 @@ public class SearchParameter extends BaseQueryParameter {
myParamType = SearchParamTypeEnum.TOKEN;
} else if (QuantityDt.class.isAssignableFrom(type)) {
myParamType = SearchParamTypeEnum.QUANTITY;
} else if (ReferenceParam.class.isAssignableFrom(type)) {
myParamType = SearchParamTypeEnum.REFERENCE;
} else {
throw new ConfigurationException("Unknown search parameter type: " + type);
}

View File

@ -20,7 +20,10 @@ package ca.uhn.fhir.rest.param;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@ -41,41 +44,82 @@ public class SortParameter implements IParameter {
@Override
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseClientInvocation theClientInvocation) throws InternalErrorException {
SortSpec ss = (SortSpec) theSourceClientArgument;
if (ss ==null) {
if (ss == null) {
return;
}
String name;
if (ss.getOrder()==null) {
if (ss.getOrder() == null) {
name = Constants.PARAM_SORT;
}else if (ss.getOrder() == SortOrderEnum.ASC) {
} else if (ss.getOrder() == SortOrderEnum.ASC) {
name = Constants.PARAM_SORT_ASC;
}else {
} else {
name = Constants.PARAM_SORT_DESC;
}
if (ss.getFieldName() != null) {
if (!theTargetQueryArguments.containsKey(name)) {
// TODO: implement
theTargetQueryArguments.put(name, new ArrayList<String>());
}
theTargetQueryArguments.get(name).add(ss.getFieldName());
}
}
@Override
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
// TODO Auto-generated method stub
return null;
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT)) {
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_ASC)) {
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_DESC)) {
return null;
}
}
}
SortSpec outerSpec = null;
SortSpec innerSpec = null;
for (String nextParamName : theRequest.getParameters().keySet()) {
SortOrderEnum order;
if (Constants.PARAM_SORT.equals(nextParamName)) {
order = null;
} else if (Constants.PARAM_SORT_ASC.equals(nextParamName)) {
order = SortOrderEnum.ASC;
} else if (Constants.PARAM_SORT_DESC.equals(nextParamName)) {
order = SortOrderEnum.DESC;
} else {
continue;
}
String[] values = theRequest.getParameters().get(nextParamName);
if (values != null) {
for (String nextValue : values) {
if (isNotBlank(nextValue)) {
SortSpec spec = new SortSpec();
spec.setOrder(order);
spec.setFieldName(nextValue);
if (innerSpec == null) {
outerSpec = spec;
innerSpec = spec;
} else {
innerSpec.setChain(spec);
innerSpec = spec;
}
}
}
}
}
return outerSpec;
}
@Override
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
if (theOuterCollectionType != null) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + Sort.class.getName() + " but can not be of collection type");
if (theOuterCollectionType != null || theInnerCollectionType!=null) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" +theMethod.getDeclaringClass().getCanonicalName()+ "' is annotated with @" + Sort.class.getName() + " but can not be of collection type");
}
if (!ParameterUtil.getBindableInstantTypes().contains(theParameterType)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '" + "' is annotated with @" + Sort.class.getName() + " but is an invalid type, must be: " + SortSpec.class.getCanonicalName());
if (!theParameterType.equals(SortSpec.class)) {
throw new ConfigurationException("Method '" + theMethod.getName() + "' in type '"+theMethod.getDeclaringClass().getCanonicalName() + "' is annotated with @" + Sort.class.getName() + " but is an invalid type, must be: " + SortSpec.class.getCanonicalName());
}
}
}

View File

@ -24,6 +24,7 @@ import java.util.Collections;
import java.util.List;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -33,9 +34,9 @@ final class StringBinder implements IParamBinder {
}
@Override
public List<QualifiedParamList> encode(FhirContext theContext, Object theString) throws InternalErrorException {
public List<IQueryParameterOr> encode(FhirContext theContext, Object theString) throws InternalErrorException {
String retVal = ((String) theString);
return Collections.singletonList(QualifiedParamList.singleton(retVal));
return Collections.singletonList(ParameterUtil.singleton(new StringParam(retVal)));
}
@Override

View File

@ -360,31 +360,35 @@ public class RestfulServer extends HttpServlet {
private void findResourceMethods(Object theProvider, Class<?> clazz) {
for (Method m : clazz.getDeclaredMethods()) {
if (Modifier.isPublic(m.getModifiers()) && !Modifier.isStatic(m.getModifiers())) {
ourLog.debug("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
if (!Modifier.isPublic(m.getModifiers())) {
ourLog.debug("Ignoring non-public method: {}",m);
} else {
if (!Modifier.isStatic(m.getModifiers())) {
ourLog.debug("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, myFhirContext, theProvider);
if (foundMethodBinding != null) {
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, myFhirContext, theProvider);
if (foundMethodBinding != null) {
String resourceName = foundMethodBinding.getResourceName();
ResourceBinding resourceBinding;
if (resourceName == null) {
resourceBinding = myNullResourceBinding;
} else {
RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(resourceName);
if (myResourceNameToProvider.containsKey(definition.getName())) {
resourceBinding = myResourceNameToProvider.get(definition.getName());
String resourceName = foundMethodBinding.getResourceName();
ResourceBinding resourceBinding;
if (resourceName == null) {
resourceBinding = myNullResourceBinding;
} else {
resourceBinding = new ResourceBinding();
resourceBinding.setResourceName(resourceName);
myResourceNameToProvider.put(resourceName, resourceBinding);
RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(resourceName);
if (myResourceNameToProvider.containsKey(definition.getName())) {
resourceBinding = myResourceNameToProvider.get(definition.getName());
} else {
resourceBinding = new ResourceBinding();
resourceBinding.setResourceName(resourceName);
myResourceNameToProvider.put(resourceName, resourceBinding);
}
}
}
resourceBinding.addMethod(foundMethodBinding);
ourLog.debug(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
} else {
ourLog.debug(" * Method: {}#{} is not a handler", theProvider.getClass(), m.getName());
resourceBinding.addMethod(foundMethodBinding);
ourLog.debug(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
} else {
ourLog.debug(" * Method: {}#{} is not a handler", theProvider.getClass(), m.getName());
}
}
}
}
@ -535,7 +539,7 @@ public class RestfulServer extends HttpServlet {
} else {
resourceBinding = myResourceNameToProvider.get(resourceName);
if (resourceBinding == null) {
throw new ResourceNotFoundException("Unknown resource type '" + resourceName + "' - Server knows how to handle: " + myResourceNameToProvider.keySet());
throw new InvalidRequestException("Unknown resource type '" + resourceName + "' - Server knows how to handle: " + myResourceNameToProvider.keySet());
}
}
@ -614,7 +618,7 @@ public class RestfulServer extends HttpServlet {
resourceMethod = resourceBinding.getMethod(r);
}
if (null == resourceMethod) {
throw new ResourceNotFoundException("No resource method available for the supplied parameters " + params);
throw new InvalidRequestException("No resource method available for the supplied parameters " + params);
}
resourceMethod.invokeServer(this, r, theResponse);

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.rest.server.provider;
* #L%
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@ -89,7 +90,7 @@ public class ServerConformanceProvider {
resource.getProfile().setId(new IdDt(def.getResourceProfile()));
Map<String, Conformance.RestResourceSearchParam> nameToSearchParam = new HashMap<String, Conformance.RestResourceSearchParam>();
for (BaseMethodBinding nextMethodBinding : next.getMethodBindings()) {
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) {
RestfulOperationTypeEnum resOp = nextMethodBinding.getResourceOperationType();
if (resOp != null) {
if (resourceOps.contains(resOp) == false) {
@ -108,19 +109,34 @@ public class ServerConformanceProvider {
if (nextMethodBinding instanceof SearchMethodBinding) {
List<IParameter> params = ((SearchMethodBinding) nextMethodBinding).getParameters();
// TODO: this would probably work best if we sorted these by
// required first, then optional
List<SearchParameter> searchParameters = new ArrayList<SearchParameter>();
for (IParameter nextParameter : params) {
if ((nextParameter instanceof SearchParameter)) {
searchParameters.add((SearchParameter) nextParameter);
}
}
Collections.sort(searchParameters, new Comparator<SearchParameter>() {
@Override
public int compare(SearchParameter theO1, SearchParameter theO2) {
if (theO1.isRequired() == theO2.isRequired()) {
return 0;
}
if (theO1.isRequired()) {
return 1;
}
return -1;
}
});
if (searchParameters.isEmpty()) {
continue;
}
boolean allOptional = searchParameters.get(0).isRequired()==false;
RestResourceSearchParam searchParam = null;
ExtensionDt searchParamChain = null;
for (IParameter nextParameterObj : params) {
if (!(nextParameterObj instanceof SearchParameter)) {
continue;
}
for (SearchParameter nextParameter : searchParameters) {
SearchParameter nextParameter = (SearchParameter) nextParameterObj;
if (searchParam == null) {
if (searchParam == null || allOptional) {
if (!nameToSearchParam.containsKey(nextParameter.getName())) {
RestResourceSearchParam param = resource.addSearchParam();
param.setName(nextParameter.getName());

View File

@ -59,6 +59,7 @@ import ca.uhn.fhir.rest.client.api.IRestfulClient;
import ca.uhn.fhir.rest.param.CodingListParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.QualifiedDateParam;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
@ -90,6 +91,34 @@ public List<Patient> findPatients(
}
//END SNIPPET: underlyingReq
//START SNIPPET: reference
@Search
public List<Patient> findPatients(
@RequiredParam(name=Patient.SP_PROVIDER) ReferenceParam theProvider
) {
// May be populated with the resource type (e.g. "Patient") if the client requested one
String type = theProvider.getResourceType();
// May be populated with the chain (e.g. "name") if the client requested one
String chain = theProvider.getChain();
/* The actual parameter value. This will be the resource ID if no chain was provided,
* but refers to the value of a specific property noted by the chain if one was given.
* For example, the following request:
* http://example.com/fhir/Patient?provider:Organization.name=FooOrg
* has a type of "Organization" and a chain of "name", meaning that
* the returned patients should have a provider which is an Organization
* with a name matching "FooOrg".
*/
String value = theProvider.getValue();
List<Patient> retVal=new ArrayList<Patient>(); // populate this
return retVal;
}
//END SNIPPET: reference
//START SNIPPET: read
@Read()
@ -557,7 +586,7 @@ private interface IPatientClient extends IBasicClient
// Only one method is shown here, but many methods may be
// added to the same client interface!
}
//START SNIPPET: clientReadInterface
//END SNIPPET: clientReadInterface
public void clientRead() {
//START SNIPPET: clientReadTags

View File

@ -693,6 +693,49 @@
</subsection>
<subsection name="Search Parameters: Resource Reference">
<p>
Many search parameters refer to resource references. For instance, the Patient
parameter "provider" refers to the resource marked as the managing organization
for patients.
</p>
<p>
Reference parameters use the
<a href="./apidocs/ca/uhn/fhir/rest/param/ReferenceParam.html">ReferenceParam</a>
type. Reference param objects being passed into server methods will have three properties
populated:
</p>
<ul>
<li>
<b>The resource type (optional):</b> This is the type of resource
being targeted, if the client specifies one in the URL.
</li>
<li>
<b>The chain property (optional):</b> This is the name of the
property on the target resource to search for
</li>
<li>
<b>The value (required):</b> This is the actual value to search for. If
a chain property has not been specified, this is the ID of the resource.
</li>
</ul>
<macro name="snippet">
<param name="id" value="reference" />
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
</macro>
<p>
Example URLs to invoke this method:
<br />
Resource by ID: <code>http://fhir.example.com/Patient?provider=1234</code>
<br />
Resource by chained parameter value: <code>http://fhir.example.com/Patient?provider:Organization.name=FooOrg</code>
</p>
</subsection>
<subsection name="Combining Multiple Parameters">
<p>

View File

@ -0,0 +1,100 @@
package ca.uhn.fhir.model.dstu.composite;
import static org.junit.Assert.*;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
public class ResourceReferenceDtTest {
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceReferenceDtTest.class);
@Test
public void testParseValueAbsolute() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("http://foo/fhir/Organization/123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(Organization.class, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
@Test
public void testParseValueMissingType1() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("/123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(null, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
@Test
public void testParseValueMissingType2() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(null, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
@Test
public void testParseValueRelative1() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("Organization/123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(Organization.class, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
@Test
public void testParseValueRelative2() {
Patient patient = new Patient();
ResourceReferenceDt rr = new ResourceReferenceDt();
rr.setReference("/Organization/123");
patient.setManagingOrganization(rr);
Patient actual = parseAndEncode(patient);
rr = actual.getManagingOrganization();
assertEquals(Organization.class, rr.getResourceType());
assertEquals("123", rr.getResourceId());
}
private Patient parseAndEncode(Patient patient) {
String encoded = ourCtx.newXmlParser().encodeResourceToString(patient);
ourLog.info("\n" + encoded);
return ourCtx.newXmlParser().parseResource(Patient.class, encoded);
}
@BeforeClass
public static void beforeClass() {
ourCtx = new FhirContext();
}
}

View File

@ -0,0 +1,129 @@
package ca.uhn.fhir.rest.client;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.StringReader;
import java.nio.charset.Charset;
import java.util.List;
import org.apache.commons.io.input.ReaderInputStream;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicStatusLine;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.client.api.IBasicClient;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.Constants;
public class ReferenceClientTest {
private FhirContext ctx;
private HttpClient httpClient;
private HttpResponse httpResponse;
// atom-document-large.xml
@Before
public void before() {
ctx = new FhirContext(Patient.class, Conformance.class);
httpClient = mock(HttpClient.class, new ReturnsDeepStubs());
ctx.getRestfulClientFactory().setHttpClient(httpClient);
httpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
}
@Test
public void testWithParam() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_ATOM_XML + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(createBundle()), Charset.forName("UTF-8")));
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
client.search(new ReferenceParam("123"));
assertEquals(HttpGet.class, capt.getValue().getClass());
HttpGet get = (HttpGet) capt.getValue();
assertEquals("http://foo/Patient?provider=123", get.getURI().toString());
}
@Test
public void testWithParamAndChain() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_ATOM_XML + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(createBundle()), Charset.forName("UTF-8")));
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
client.search(new ReferenceParam("chain", "123"));
assertEquals(HttpGet.class, capt.getValue().getClass());
HttpGet get = (HttpGet) capt.getValue();
assertEquals("http://foo/Patient?provider.chain=123", get.getURI().toString());
}
@Test
public void testWithParamAndTypeAndChain() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_ATOM_XML + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(createBundle()), Charset.forName("UTF-8")));
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
client.search(new ReferenceParam("Organization", "chain", "123"));
assertEquals(HttpGet.class, capt.getValue().getClass());
HttpGet get = (HttpGet) capt.getValue();
assertEquals("http://foo/Patient?provider%3AOrganization.chain=123", get.getURI().toString());
}
@Test
public void testWithParamAndType() throws Exception {
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_ATOM_XML + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(createBundle()), Charset.forName("UTF-8")));
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
client.search(new ReferenceParam("Organization", null, "123"));
assertEquals(HttpGet.class, capt.getValue().getClass());
HttpGet get = (HttpGet) capt.getValue();
assertEquals("http://foo/Patient?provider%3AOrganization=123", get.getURI().toString());
}
private String createBundle() {
return ctx.newXmlParser().encodeBundleToString(new Bundle());
}
private interface IClient extends IBasicClient {
@Search(type=Patient.class)
public List<Patient> search(@RequiredParam(name=Patient.SP_PROVIDER) ReferenceParam theString);
}
}

View File

@ -0,0 +1,165 @@
package ca.uhn.fhir.rest.server;
import static org.apache.commons.lang3.StringUtils.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.BundleEntry;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class ReferenceParameterTest {
private static CloseableHttpClient ourClient;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReferenceParameterTest.class);
private static int ourPort;
private static Server ourServer;
private static FhirContext ourCtx;
@Test
public void testSearchWithValue() throws Exception {
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?" + Patient.SP_PROVIDER+"=123");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
List<BundleEntry> entries = new FhirContext().newXmlParser().parseBundle(responseContent).getEntries();
assertEquals(1, entries.size());
Patient p = (Patient) entries.get(0).getResource();
assertEquals("0123",p.getName().get(0).getFamilyFirstRep().getValue());
assertEquals("1",p.getName().get(1).getFamilyFirstRep().getValue());
assertEquals("2",p.getName().get(2).getFamilyFirstRep().getValue());
}
}
@Test
public void testSearchWithValueAndType() throws Exception {
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?" + Patient.SP_PROVIDER+":Organization=123");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
List<BundleEntry> entries = new FhirContext().newXmlParser().parseBundle(responseContent).getEntries();
assertEquals(1, entries.size());
Patient p = (Patient) entries.get(0).getResource();
assertEquals("0123",p.getName().get(0).getFamilyFirstRep().getValue());
assertEquals("1Organization",p.getName().get(1).getFamilyFirstRep().getValue());
assertEquals("2",p.getName().get(2).getFamilyFirstRep().getValue());
}
}
@Test
public void testSearchWithValueAndTypeAndChain() throws Exception {
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?" + Patient.SP_PROVIDER+":Organization.name=123");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
List<BundleEntry> entries = new FhirContext().newXmlParser().parseBundle(responseContent).getEntries();
assertEquals(1, entries.size());
Patient p = (Patient) entries.get(0).getResource();
assertEquals("0123",p.getName().get(0).getFamilyFirstRep().getValue());
assertEquals("1Organization",p.getName().get(1).getFamilyFirstRep().getValue());
assertEquals("2name",p.getName().get(2).getFamilyFirstRep().getValue());
}
}
@Test
public void testSearchWithValueAndChain() throws Exception {
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?" + Patient.SP_PROVIDER+".name=123");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
List<BundleEntry> entries = new FhirContext().newXmlParser().parseBundle(responseContent).getEntries();
assertEquals(1, entries.size());
Patient p = (Patient) entries.get(0).getResource();
assertEquals("0123",p.getName().get(0).getFamilyFirstRep().getValue());
assertEquals("1",p.getName().get(1).getFamilyFirstRep().getValue());
assertEquals("2name",p.getName().get(2).getFamilyFirstRep().getValue());
}
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
ourCtx = servlet.getFhirContext();
}
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class DummyPatientResourceProvider implements IResourceProvider {
@Search
public List<Patient> findPatient(@RequiredParam(name = Patient.SP_PROVIDER) ReferenceParam theParam) {
ArrayList<Patient> retVal = new ArrayList<Patient>();
Patient p = new Patient();
p.addName().addFamily("0"+theParam.getValueAsQueryToken());
p.addName().addFamily("1"+defaultString(theParam.getResourceType()));
p.addName().addFamily("2"+defaultString(theParam.getChain()));
retVal.add(p);
return retVal;
}
@Override
public Class<? extends IResource> getResourceType() {
return Patient.class;
}
}
}

View File

@ -96,7 +96,9 @@ public class ResourceMethodTest {
inputParams.add("lastName");
inputParams.add("mrn");
assertEquals(true, rm.incomingServerRequestMatchesMethod(Request.withResourceAndParams("Patient", RequestType.GET, inputParams))); // True
Request params = Request.withResourceAndParams("Patient", RequestType.GET, inputParams);
boolean actual = rm.incomingServerRequestMatchesMethod(params);
assertTrue( actual); // True
}
@Test

View File

@ -13,6 +13,8 @@ import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.resource.Conformance;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.method.BaseMethodBinding;
@ -20,9 +22,9 @@ import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.param.SearchParameter;
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
public class DocumentationTest {
public class ServerConformanceProviderTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DocumentationTest.class);
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerConformanceProviderTest.class);
@Test
public void testSearchParameterDocumentation() throws Exception {
@ -55,6 +57,39 @@ public class DocumentationTest {
assertThat(conf, containsString("<type value=\"token\"/>"));
}
@Test
public void testMultiOptionalDocumentation() throws Exception {
RestfulServer rs = new RestfulServer();
rs.setProviders(new MultiOptionalProvider());
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(null);
boolean found=false;
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().iterator().next();
assertEquals("The patient's identifier", param.getDescription());
found=true;
}
}
assertTrue(found);
Conformance conformance = sc.getServerConformance();
String conf = new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
ourLog.info(conf);
assertThat(conf, containsString("<documentation value=\"The patient's identifier (MRN or other card number)\"/>"));
assertThat(conf, containsString("<type value=\"token\"/>"));
}
/**
* Created by dsotnikov on 2/25/2014.
*/
@ -69,5 +104,23 @@ public class DocumentationTest {
}
}
/**
* Created by dsotnikov on 2/25/2014.
*/
@SuppressWarnings("unused")
public static class MultiOptionalProvider {
@Search(type = Patient.class)
public Patient findPatient(
@Description(shortDefinition = "The patient's identifier")
@OptionalParam(name = Patient.SP_IDENTIFIER) IdentifierDt theIdentifier,
@Description(shortDefinition = "The patient's name")
@OptionalParam(name=Patient.SP_NAME) StringDt theName) {
return null;
}
}
}

View File

@ -0,0 +1,177 @@
package ca.uhn.fhir.rest.server;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Sort;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.testutil.RandomServerPortProvider;
/**
* Created by dsotnikov on 2/25/2014.
*/
public class SortTest {
private static CloseableHttpClient ourClient;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SortTest.class);
private static int ourPort;
private static Server ourServer;
@Test
public void testNoSort() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = new FhirContext().newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.size());
Patient p = bundle.getResources(Patient.class).get(0);
assertEquals(1, p.getName().size());
assertEquals("Hello", p.getNameFirstRep().getFamilyFirstRep().getValue());
}
@Test
public void testSingleSort() throws Exception {
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_sort=given");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = new FhirContext().newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.size());
Patient p = bundle.getResources(Patient.class).get(0);
assertEquals(2, p.getName().size());
assertEquals("Hello", p.getNameFirstRep().getFamilyFirstRep().getValue());
assertEquals("given|null", p.getName().get(1).getFamilyFirstRep().getValue());
}
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_sort:asc=given");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = new FhirContext().newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.size());
Patient p = bundle.getResources(Patient.class).get(0);
assertEquals(2, p.getName().size());
assertEquals("Hello", p.getNameFirstRep().getFamilyFirstRep().getValue());
assertEquals("given|ASC", p.getName().get(1).getFamilyFirstRep().getValue());
}
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_sort:desc=given");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = new FhirContext().newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.size());
Patient p = bundle.getResources(Patient.class).get(0);
assertEquals(2, p.getName().size());
assertEquals("Hello", p.getNameFirstRep().getFamilyFirstRep().getValue());
assertEquals("given|DESC", p.getName().get(1).getFamilyFirstRep().getValue());
}
}
@Test
public void testSortChain() throws Exception {
{
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?name=Hello&_sort=given&_sort=family&_sort=name");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
assertEquals(200, status.getStatusLine().getStatusCode());
Bundle bundle = new FhirContext().newXmlParser().parseBundle(responseContent);
assertEquals(1, bundle.size());
Patient p = bundle.getResources(Patient.class).get(0);
assertEquals(4, p.getName().size());
assertEquals("Hello", p.getNameFirstRep().getFamilyFirstRep().getValue());
assertEquals("given|null", p.getName().get(1).getFamilyFirstRep().getValue());
assertEquals("family|null", p.getName().get(2).getFamilyFirstRep().getValue());
assertEquals("name|null", p.getName().get(3).getFamilyFirstRep().getValue());
}
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
}
@BeforeClass
public static void beforeClass() throws Exception {
ourPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(ourPort);
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
ServletHandler proxyHandler = new ServletHandler();
RestfulServer servlet = new RestfulServer();
servlet.setResourceProviders(patientProvider);
ServletHolder servletHolder = new ServletHolder(servlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setConnectionManager(connectionManager);
ourClient = builder.build();
}
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class DummyPatientResourceProvider implements IResourceProvider {
@Search
public List<Patient> findPatient(@RequiredParam(name = Patient.SP_NAME) StringDt theName, @Sort SortSpec theSort) {
ArrayList<Patient> retVal = new ArrayList<Patient>();
Patient p = new Patient();
p.addName().addFamily().setValue(theName.getValue());
SortSpec sort = theSort;
while (sort != null) {
p.addName().addFamily().setValue(sort.getFieldName() + "|" + sort.getOrder());
sort = sort.getChain();
}
retVal.add(p);
return retVal;
}
@Override
public Class<? extends IResource> getResourceType() {
return Patient.class;
}
}
}

View File

@ -1,151 +1,152 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/test/java" output="target/test-classes" including="**/*.java"/>
<classpathentry kind="src" path="src/test/resources" output="target/test-classes" excluding="**/*.java"/>
<classpathentry kind="src" path="src/main/java" including="**/*.java"/>
<classpathentry kind="src" path="src/main/resources" excluding="**/*.java"/>
<classpathentry kind="output" path="target/classes"/>
<classpathentry kind="var" path="M2_REPO/javax/activation/activation/1.1/activation-1.1.jar" sourcepath="M2_REPO/javax/activation/activation/1.1/activation-1.1-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/javax/json/javax.json-api/1.0/javax.json-api-1.0.jar" sourcepath="M2_REPO/javax/json/javax.json-api/1.0/javax.json-api-1.0-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/javax/mail/javax.mail-api/1.5.0/javax.mail-api-1.5.0.jar" sourcepath="M2_REPO/javax/mail/javax.mail-api/1.5.0/javax.mail-api-1.5.0-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/javax/mail/javax.mail-api/1.5.0/javax.mail-api-1.5.0-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/javax/transaction/jta/1.1/jta-1.1.jar" sourcepath="M2_REPO/javax/transaction/jta/1.1/jta-1.1-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/javax/xml/stream/stax-api/1.0-2/stax-api-1.0-2.jar" sourcepath="M2_REPO/javax/xml/stream/stax-api/1.0-2/stax-api-1.0-2-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/javax/validation/validation-api/1.1.0.Final/validation-api-1.1.0.Final.jar" sourcepath="M2_REPO/javax/validation/validation-api/1.1.0.Final/validation-api-1.1.0.Final-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/antlr/antlr/2.7.7/antlr-2.7.7.jar"/>
<classpathentry kind="var" path="M2_REPO/aopalliance/aopalliance/1.0/aopalliance-1.0.jar" sourcepath="M2_REPO/aopalliance/aopalliance/1.0/aopalliance-1.0-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/com/fasterxml/classmate/1.0.0/classmate-1.0.0.jar" sourcepath="M2_REPO/com/fasterxml/classmate/1.0.0/classmate-1.0.0-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/commons-codec/commons-codec/1.9/commons-codec-1.9.jar" sourcepath="M2_REPO/commons-codec/commons-codec/1.9/commons-codec-1.9-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/commons-codec/commons-codec/1.9/commons-codec-1.9-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/commons-dbcp/commons-dbcp/1.4/commons-dbcp-1.4.jar" sourcepath="M2_REPO/commons-dbcp/commons-dbcp/1.4/commons-dbcp-1.4-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/commons-io/commons-io/2.4/commons-io-2.4.jar" sourcepath="M2_REPO/commons-io/commons-io/2.4/commons-io-2.4-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1.jar" sourcepath="M2_REPO/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar" sourcepath="M2_REPO/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/commons-pool/commons-pool/1.5.4/commons-pool-1.5.4.jar" sourcepath="M2_REPO/commons-pool/commons-pool/1.5.4/commons-pool-1.5.4-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/apache/derby/derby/10.10.2.0/derby-10.10.2.0.jar"/>
<classpathentry kind="var" path="M2_REPO/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar" sourcepath="M2_REPO/dom4j/dom4j/1.6.1/dom4j-1.6.1-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/hamcrest/hamcrest-all/1.3/hamcrest-all-1.3.jar" sourcepath="M2_REPO/org/hamcrest/hamcrest-all/1.3/hamcrest-all-1.3-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/hamcrest/hamcrest-all/1.3/hamcrest-all-1.3-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar" sourcepath="M2_REPO/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="/hapi-fhir-base"/>
<classpathentry kind="var" path="M2_REPO/org/hibernate/common/hibernate-commons-annotations/4.0.4.Final/hibernate-commons-annotations-4.0.4.Final.jar" sourcepath="M2_REPO/org/hibernate/common/hibernate-commons-annotations/4.0.4.Final/hibernate-commons-annotations-4.0.4.Final-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/hibernate/hibernate-core/4.3.5.Final/hibernate-core-4.3.5.Final.jar" sourcepath="M2_REPO/org/hibernate/hibernate-core/4.3.5.Final/hibernate-core-4.3.5.Final-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/hibernate/hibernate-ehcache/4.3.5.Final/hibernate-ehcache-4.3.5.Final.jar" sourcepath="M2_REPO/org/hibernate/hibernate-ehcache/4.3.5.Final/hibernate-ehcache-4.3.5.Final-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/hibernate/hibernate-entitymanager/4.3.5.Final/hibernate-entitymanager-4.3.5.Final.jar" sourcepath="M2_REPO/org/hibernate/hibernate-entitymanager/4.3.5.Final/hibernate-entitymanager-4.3.5.Final-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar" sourcepath="M2_REPO/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/hibernate/hibernate-validator/5.1.0.Final/hibernate-validator-5.1.0.Final.jar" sourcepath="M2_REPO/org/hibernate/hibernate-validator/5.1.0.Final/hibernate-validator-5.1.0.Final-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3.jar" sourcepath="M2_REPO/org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2.jar" sourcepath="M2_REPO/org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/jboss/jandex/1.1.0.Final/jandex-1.1.0.Final.jar" sourcepath="M2_REPO/org/jboss/jandex/1.1.0.Final/jandex-1.1.0.Final-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA.jar" sourcepath="M2_REPO/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/glassfish/javax.json/1.0.4/javax.json-1.0.4.jar" sourcepath="M2_REPO/org/glassfish/javax.json/1.0.4/javax.json-1.0.4-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/jboss/logging/jboss-logging/3.1.3.GA/jboss-logging-3.1.3.GA.jar" sourcepath="M2_REPO/org/jboss/logging/jboss-logging/3.1.3.GA/jboss-logging-3.1.3.GA-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/jboss/logging/jboss-logging/3.1.3.GA/jboss-logging-3.1.3.GA-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/jboss/logging/jboss-logging-annotations/1.2.0.Beta1/jboss-logging-annotations-1.2.0.Beta1.jar" sourcepath="M2_REPO/org/jboss/logging/jboss-logging-annotations/1.2.0.Beta1/jboss-logging-annotations-1.2.0.Beta1-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/jboss/logging/jboss-logging-annotations/1.2.0.Beta1/jboss-logging-annotations-1.2.0.Beta1-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/jboss/spec/javax/transaction/jboss-transaction-api_1.2_spec/1.0.0.Final/jboss-transaction-api_1.2_spec-1.0.0.Final.jar" sourcepath="M2_REPO/org/jboss/spec/javax/transaction/jboss-transaction-api_1.2_spec/1.0.0.Final/jboss-transaction-api_1.2_spec-1.0.0.Final-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/jboss/spec/javax/transaction/jboss-transaction-api_1.2_spec/1.0.0.Final/jboss-transaction-api_1.2_spec-1.0.0.Final-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/junit/junit/4.11/junit-4.11.jar" sourcepath="M2_REPO/junit/junit/4.11/junit-4.11-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/junit/junit/4.11/junit-4.11-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6.jar" sourcepath="M2_REPO/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/springframework/spring-aop/4.0.1.RELEASE/spring-aop-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-aop/4.0.1.RELEASE/spring-aop-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/springframework/spring-aop/4.0.1.RELEASE/spring-aop-4.0.1.RELEASE-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/springframework/spring-beans/4.0.1.RELEASE/spring-beans-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-beans/4.0.1.RELEASE/spring-beans-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/springframework/spring-beans/4.0.1.RELEASE/spring-beans-4.0.1.RELEASE-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/springframework/spring-context/4.0.1.RELEASE/spring-context-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-context/4.0.1.RELEASE/spring-context-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/springframework/spring-context/4.0.1.RELEASE/spring-context-4.0.1.RELEASE-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/springframework/spring-context-support/4.0.1.RELEASE/spring-context-support-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-context-support/4.0.1.RELEASE/spring-context-support-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/springframework/spring-context-support/4.0.1.RELEASE/spring-context-support-4.0.1.RELEASE-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/springframework/spring-core/4.0.1.RELEASE/spring-core-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-core/4.0.1.RELEASE/spring-core-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/springframework/spring-core/4.0.1.RELEASE/spring-core-4.0.1.RELEASE-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/springframework/spring-expression/4.0.1.RELEASE/spring-expression-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-expression/4.0.1.RELEASE/spring-expression-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/springframework/spring-expression/4.0.1.RELEASE/spring-expression-4.0.1.RELEASE-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/springframework/spring-jdbc/4.0.1.RELEASE/spring-jdbc-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-jdbc/4.0.1.RELEASE/spring-jdbc-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/springframework/spring-jdbc/4.0.1.RELEASE/spring-jdbc-4.0.1.RELEASE-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/springframework/spring-orm/4.0.1.RELEASE/spring-orm-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-orm/4.0.1.RELEASE/spring-orm-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/springframework/spring-orm/4.0.1.RELEASE/spring-orm-4.0.1.RELEASE-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/springframework/spring-tx/4.0.1.RELEASE/spring-tx-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-tx/4.0.1.RELEASE/spring-tx-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/springframework/spring-tx/4.0.1.RELEASE/spring-tx-4.0.1.RELEASE-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="var" path="M2_REPO/org/codehaus/woodstox/stax2-api/3.1.1/stax2-api-3.1.1.jar" sourcepath="M2_REPO/org/codehaus/woodstox/stax2-api/3.1.1/stax2-api-3.1.1-sources.jar"/>
<classpathentry kind="var" path="M2_REPO/org/codehaus/woodstox/woodstox-core-asl/4.2.0/woodstox-core-asl-4.2.0.jar" sourcepath="M2_REPO/org/codehaus/woodstox/woodstox-core-asl/4.2.0/woodstox-core-asl-4.2.0-sources.jar">
<attributes>
<attribute value="jar:file:/Users/james/.m2/repository/org/codehaus/woodstox/woodstox-core-asl/4.2.0/woodstox-core-asl-4.2.0-javadoc.jar!/" name="javadoc_location"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
</classpath>
<classpathentry including="**/*.java" kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry excluding="**/*.java" kind="src" output="target/test-classes" path="src/test/resources"/>
<classpathentry including="**/*.java" kind="src" path="src/main/java"/>
<classpathentry excluding="**/*.java" kind="src" path="src/main/resources"/>
<classpathentry exported="true" kind="var" path="M2_REPO/javax/activation/activation/1.1/activation-1.1.jar" sourcepath="M2_REPO/javax/activation/activation/1.1/activation-1.1-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/javax/json/javax.json-api/1.0/javax.json-api-1.0.jar" sourcepath="M2_REPO/javax/json/javax.json-api/1.0/javax.json-api-1.0-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/javax/mail/javax.mail-api/1.5.0/javax.mail-api-1.5.0.jar" sourcepath="M2_REPO/javax/mail/javax.mail-api/1.5.0/javax.mail-api-1.5.0-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/javax/mail/javax.mail-api/1.5.0/javax.mail-api-1.5.0-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/javax/transaction/jta/1.1/jta-1.1.jar" sourcepath="M2_REPO/javax/transaction/jta/1.1/jta-1.1-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/javax/xml/stream/stax-api/1.0-2/stax-api-1.0-2.jar" sourcepath="M2_REPO/javax/xml/stream/stax-api/1.0-2/stax-api-1.0-2-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/javax/validation/validation-api/1.1.0.Final/validation-api-1.1.0.Final.jar" sourcepath="M2_REPO/javax/validation/validation-api/1.1.0.Final/validation-api-1.1.0.Final-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/antlr/antlr/2.7.7/antlr-2.7.7.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/aopalliance/aopalliance/1.0/aopalliance-1.0.jar" sourcepath="M2_REPO/aopalliance/aopalliance/1.0/aopalliance-1.0-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/com/fasterxml/classmate/1.0.0/classmate-1.0.0.jar" sourcepath="M2_REPO/com/fasterxml/classmate/1.0.0/classmate-1.0.0-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/commons-codec/commons-codec/1.9/commons-codec-1.9.jar" sourcepath="M2_REPO/commons-codec/commons-codec/1.9/commons-codec-1.9-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/commons-codec/commons-codec/1.9/commons-codec-1.9-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/commons-dbcp/commons-dbcp/1.4/commons-dbcp-1.4.jar" sourcepath="M2_REPO/commons-dbcp/commons-dbcp/1.4/commons-dbcp-1.4-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/commons-io/commons-io/2.4/commons-io-2.4.jar" sourcepath="M2_REPO/commons-io/commons-io/2.4/commons-io-2.4-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/commons-io/commons-io/2.4/commons-io-2.4-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1.jar" sourcepath="M2_REPO/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/apache/commons/commons-lang3/3.2.1/commons-lang3-3.2.1-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1.jar" sourcepath="M2_REPO/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/commons-logging/commons-logging/1.1.1/commons-logging-1.1.1-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/commons-pool/commons-pool/1.5.4/commons-pool-1.5.4.jar" sourcepath="M2_REPO/commons-pool/commons-pool/1.5.4/commons-pool-1.5.4-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/apache/derby/derby/10.10.2.0/derby-10.10.2.0.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar" sourcepath="M2_REPO/dom4j/dom4j/1.6.1/dom4j-1.6.1-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/hamcrest/hamcrest-all/1.3/hamcrest-all-1.3.jar" sourcepath="M2_REPO/org/hamcrest/hamcrest-all/1.3/hamcrest-all-1.3-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/hamcrest/hamcrest-all/1.3/hamcrest-all-1.3-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar" sourcepath="M2_REPO/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="src" path="/hapi-fhir-base"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/hibernate/common/hibernate-commons-annotations/4.0.4.Final/hibernate-commons-annotations-4.0.4.Final.jar" sourcepath="M2_REPO/org/hibernate/common/hibernate-commons-annotations/4.0.4.Final/hibernate-commons-annotations-4.0.4.Final-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/hibernate/hibernate-core/4.3.5.Final/hibernate-core-4.3.5.Final.jar" sourcepath="M2_REPO/org/hibernate/hibernate-core/4.3.5.Final/hibernate-core-4.3.5.Final-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/hibernate/hibernate-ehcache/4.3.5.Final/hibernate-ehcache-4.3.5.Final.jar" sourcepath="M2_REPO/org/hibernate/hibernate-ehcache/4.3.5.Final/hibernate-ehcache-4.3.5.Final-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/hibernate/hibernate-entitymanager/4.3.5.Final/hibernate-entitymanager-4.3.5.Final.jar" sourcepath="M2_REPO/org/hibernate/hibernate-entitymanager/4.3.5.Final/hibernate-entitymanager-4.3.5.Final-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final.jar" sourcepath="M2_REPO/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/hibernate/javax/persistence/hibernate-jpa-2.1-api/1.0.0.Final/hibernate-jpa-2.1-api-1.0.0.Final-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/hibernate/hibernate-validator/5.1.0.Final/hibernate-validator-5.1.0.Final.jar" sourcepath="M2_REPO/org/hibernate/hibernate-validator/5.1.0.Final/hibernate-validator-5.1.0.Final-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3.jar" sourcepath="M2_REPO/org/apache/httpcomponents/httpclient/4.3.3/httpclient-4.3.3-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2.jar" sourcepath="M2_REPO/org/apache/httpcomponents/httpcore/4.3.2/httpcore-4.3.2-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/jboss/jandex/1.1.0.Final/jandex-1.1.0.Final.jar" sourcepath="M2_REPO/org/jboss/jandex/1.1.0.Final/jandex-1.1.0.Final-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA.jar" sourcepath="M2_REPO/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/javassist/javassist/3.18.1-GA/javassist-3.18.1-GA-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/glassfish/javax.json/1.0.4/javax.json-1.0.4.jar" sourcepath="M2_REPO/org/glassfish/javax.json/1.0.4/javax.json-1.0.4-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/jboss/logging/jboss-logging/3.1.3.GA/jboss-logging-3.1.3.GA.jar" sourcepath="M2_REPO/org/jboss/logging/jboss-logging/3.1.3.GA/jboss-logging-3.1.3.GA-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/jboss/logging/jboss-logging/3.1.3.GA/jboss-logging-3.1.3.GA-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/jboss/logging/jboss-logging-annotations/1.2.0.Beta1/jboss-logging-annotations-1.2.0.Beta1.jar" sourcepath="M2_REPO/org/jboss/logging/jboss-logging-annotations/1.2.0.Beta1/jboss-logging-annotations-1.2.0.Beta1-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/jboss/logging/jboss-logging-annotations/1.2.0.Beta1/jboss-logging-annotations-1.2.0.Beta1-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/jboss/spec/javax/transaction/jboss-transaction-api_1.2_spec/1.0.0.Final/jboss-transaction-api_1.2_spec-1.0.0.Final.jar" sourcepath="M2_REPO/org/jboss/spec/javax/transaction/jboss-transaction-api_1.2_spec/1.0.0.Final/jboss-transaction-api_1.2_spec-1.0.0.Final-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/jboss/spec/javax/transaction/jboss-transaction-api_1.2_spec/1.0.0.Final/jboss-transaction-api_1.2_spec-1.0.0.Final-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/junit/junit/4.11/junit-4.11.jar" sourcepath="M2_REPO/junit/junit/4.11/junit-4.11-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/junit/junit/4.11/junit-4.11-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6.jar" sourcepath="M2_REPO/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/springframework/spring-aop/4.0.1.RELEASE/spring-aop-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-aop/4.0.1.RELEASE/spring-aop-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/springframework/spring-aop/4.0.1.RELEASE/spring-aop-4.0.1.RELEASE-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/springframework/spring-beans/4.0.1.RELEASE/spring-beans-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-beans/4.0.1.RELEASE/spring-beans-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/springframework/spring-beans/4.0.1.RELEASE/spring-beans-4.0.1.RELEASE-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/springframework/spring-context/4.0.1.RELEASE/spring-context-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-context/4.0.1.RELEASE/spring-context-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/springframework/spring-context/4.0.1.RELEASE/spring-context-4.0.1.RELEASE-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/springframework/spring-context-support/4.0.1.RELEASE/spring-context-support-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-context-support/4.0.1.RELEASE/spring-context-support-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/springframework/spring-context-support/4.0.1.RELEASE/spring-context-support-4.0.1.RELEASE-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/springframework/spring-core/4.0.1.RELEASE/spring-core-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-core/4.0.1.RELEASE/spring-core-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/springframework/spring-core/4.0.1.RELEASE/spring-core-4.0.1.RELEASE-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/springframework/spring-expression/4.0.1.RELEASE/spring-expression-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-expression/4.0.1.RELEASE/spring-expression-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/springframework/spring-expression/4.0.1.RELEASE/spring-expression-4.0.1.RELEASE-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/springframework/spring-jdbc/4.0.1.RELEASE/spring-jdbc-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-jdbc/4.0.1.RELEASE/spring-jdbc-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/springframework/spring-jdbc/4.0.1.RELEASE/spring-jdbc-4.0.1.RELEASE-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/springframework/spring-orm/4.0.1.RELEASE/spring-orm-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-orm/4.0.1.RELEASE/spring-orm-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/springframework/spring-orm/4.0.1.RELEASE/spring-orm-4.0.1.RELEASE-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/springframework/spring-tx/4.0.1.RELEASE/spring-tx-4.0.1.RELEASE.jar" sourcepath="M2_REPO/org/springframework/spring-tx/4.0.1.RELEASE/spring-tx-4.0.1.RELEASE-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/springframework/spring-tx/4.0.1.RELEASE/spring-tx-4.0.1.RELEASE-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="var" path="M2_REPO/org/codehaus/woodstox/stax2-api/3.1.1/stax2-api-3.1.1.jar" sourcepath="M2_REPO/org/codehaus/woodstox/stax2-api/3.1.1/stax2-api-3.1.1-sources.jar"/>
<classpathentry exported="true" kind="var" path="M2_REPO/org/codehaus/woodstox/woodstox-core-asl/4.2.0/woodstox-core-asl-4.2.0.jar" sourcepath="M2_REPO/org/codehaus/woodstox/woodstox-core-asl/4.2.0/woodstox-core-asl-4.2.0-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:file:/Users/james/.m2/repository/org/codehaus/woodstox/woodstox-core-asl/4.2.0/woodstox-core-asl-4.2.0-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@ -1 +1,2 @@
target/
/bin

View File

@ -175,6 +175,59 @@
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>de.juplo</groupId>
<artifactId>hibernate4-maven-plugin</artifactId>
<version>1.0.1</version>
<configuration>
<force>true</force>
<target>SCRIPT</target>
<skip>false</skip>
</configuration>
<executions>
<execution>
<id>o10g</id>
<goals>
<goal>export</goal>
</goals>
<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>
<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>
<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>
<configuration>
<hibernateDialect>org.hibernate.dialect.MySQL5Dialect</hibernateDialect>
<outputFile>${project.build.directory}/schema_mysql_5.sql</outputFile>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

View File

@ -56,8 +56,10 @@ import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.AddressDt;
import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.ContactDt;
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
@ -96,6 +98,312 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
private Class<X> myTableType;
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
@Override
public MethodOutcome create(T theResource) {
final X entity = toEntity(theResource);
entity.setPublished(new Date());
entity.setUpdated(entity.getPublished());
final List<ResourceIndexedSearchParamString> stringParams = extractSearchParamStrings(entity, theResource);
final List<ResourceIndexedSearchParamToken> tokenParams = extractSearchParamTokens(entity, theResource);
final List<ResourceIndexedSearchParamNumber> numberParams = extractSearchParamNumber(entity, theResource);
final List<ResourceIndexedSearchParamDate> dateParams = extractSearchParamDates(entity, theResource);
final List<ResourceLink> links = extractResourceLinks(entity, theResource);
ourLog.info("Saving links: {}", links);
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
template.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
template.setReadOnly(false);
template.execute(new TransactionCallback<X>() {
@Override
public X doInTransaction(TransactionStatus theStatus) {
myEntityManager.persist(entity);
for (ResourceIndexedSearchParamString next : stringParams) {
myEntityManager.persist(next);
}
for (ResourceIndexedSearchParamToken next : tokenParams) {
myEntityManager.persist(next);
}
for (ResourceIndexedSearchParamNumber next : numberParams) {
myEntityManager.persist(next);
}
for (ResourceIndexedSearchParamDate next : dateParams) {
myEntityManager.persist(next);
}
for (ResourceLink next : links) {
myEntityManager.persist(next);
}
return entity;
}
});
MethodOutcome outcome = toMethodOutcome(entity);
return outcome;
}
public Class<T> getResourceType() {
return myResourceType;
}
@Override
public Class<X> getTableType() {
return myTableType;
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public List<T> history(IdDt theId) {
ArrayList<T> retVal = new ArrayList<T>();
String resourceType = myCtx.getResourceDefinition(myResourceType).getName();
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery(ResourceHistoryTable.Q_GETALL, ResourceHistoryTable.class);
q.setParameter("PID", theId.asLong());
q.setParameter("RESTYPE", resourceType);
// TypedQuery<ResourceHistoryTable> query =
// myEntityManager.createQuery(criteriaQuery);
List<ResourceHistoryTable> results = q.getResultList();
for (ResourceHistoryTable next : results) {
retVal.add(toResource(next));
}
try {
retVal.add(read(theId));
} catch (ResourceNotFoundException e) {
// ignore
}
if (retVal.isEmpty()) {
throw new ResourceNotFoundException(theId);
}
return retVal;
}
@PostConstruct
public void postConstruct() throws Exception {
myResourceType = myTableType.newInstance().getResourceType();
myCtx = new FhirContext(myResourceType);
myResourceName = myCtx.getResourceDefinition(myResourceType).getName();
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public T read(IdDt theId) {
X entity = readEntity(theId);
T retVal = toResource(entity);
return retVal;
}
@Override
public List<T> search(Map<String, IQueryParameterType> theParams) {
SearchParameterMap map = new SearchParameterMap();
for (Entry<String, IQueryParameterType> nextEntry : theParams.entrySet()) {
map.put(nextEntry.getKey(), new ArrayList<List<IQueryParameterType>>());
map.get(nextEntry.getKey()).add(Collections.singletonList(nextEntry.getValue()));
}
return search(map);
}
@Override
public List<T> search(SearchParameterMap theParams) {
Set<Long> pids;
if (theParams.isEmpty()) {
pids = null;
} else {
pids = searchForIdsWithAndOr(theParams);
if (pids.isEmpty()) {
return new ArrayList<>();
}
}
// Execute the query and make sure we return distinct results
{
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<X> cq = builder.createQuery(myTableType);
Root<X> from = cq.from(myTableType);
if (!theParams.isEmpty()) {
cq.where(from.get("myId").in(pids));
}
TypedQuery<X> q = myEntityManager.createQuery(cq);
List<T> retVal = new ArrayList<>();
for (X next : q.getResultList()) {
T resource = toResource(next);
retVal.add(resource);
}
return retVal;
}
}
@Override
public List<T> search(String theParameterName, IQueryParameterType theValue) {
return search(Collections.singletonMap(theParameterName, theValue));
}
@Override
public Set<Long> searchForIds(Map<String, IQueryParameterType> theParams) {
Map<String, List<List<IQueryParameterType>>> map = new HashMap<String, List<List<IQueryParameterType>>>();
for (Entry<String, IQueryParameterType> nextEntry : theParams.entrySet()) {
map.put(nextEntry.getKey(), new ArrayList<List<IQueryParameterType>>());
map.get(nextEntry.getKey()).add(Collections.singletonList(nextEntry.getValue()));
}
return searchForIdsWithAndOr(map);
}
@Override
public Set<Long> searchForIds(String theParameterName, IQueryParameterType theValue) {
return searchForIds(Collections.singletonMap(theParameterName, theValue));
}
@Override
public Set<Long> searchForIdsWithAndOr(Map<String, List<List<IQueryParameterType>>> theParams) {
Map<String, List<List<IQueryParameterType>>> params = theParams;
if (params == null) {
params = Collections.emptyMap();
}
RuntimeResourceDefinition resourceDef = myCtx.getResourceDefinition(myResourceType);
Set<Long> pids = new HashSet<Long>();
for (Entry<String, List<List<IQueryParameterType>>> nextParamEntry : params.entrySet()) {
String nextParamName = nextParamEntry.getKey();
RuntimeSearchParam nextParamDef = resourceDef.getSearchParam(nextParamName);
if (nextParamDef != null) {
if (nextParamDef.getParamType() == SearchParamTypeEnum.TOKEN) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateToken(pids, nextAnd);
if (pids.isEmpty()) {
return new HashSet<Long>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.STRING) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateString(pids, nextAnd);
if (pids.isEmpty()) {
return new HashSet<Long>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.QUANTITY) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateQuantity(pids, nextAnd);
if (pids.isEmpty()) {
return new HashSet<Long>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.DATE) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateDate(pids, nextAnd);
if (pids.isEmpty()) {
return new HashSet<Long>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.REFERENCE) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateReference(nextParamName, pids, nextAnd);
if (pids.isEmpty()) {
return new HashSet<Long>();
}
}
} else {
throw new IllegalArgumentException("Don't know how to handle parameter of type: " + nextParamDef.getParamType());
}
}
}
return pids;
}
@Required
public void setTableType(Class<X> theTableType) {
myTableType = theTableType;
}
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public MethodOutcome update(final T theResource, final IdDt theId) {
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
X savedEntity = template.execute(new TransactionCallback<X>() {
@Override
public X doInTransaction(TransactionStatus theStatus) {
final X entity = readEntity(theId);
entity.setUpdated(entity.getPublished());
final ResourceHistoryTable historyEntry = entity.toHistory(myCtx);
final List<ResourceIndexedSearchParamString> stringParams = extractSearchParamStrings(entity, theResource);
final List<ResourceIndexedSearchParamToken> tokenParams = extractSearchParamTokens(entity, theResource);
final List<ResourceIndexedSearchParamNumber> numberParams = extractSearchParamNumber(entity, theResource);
final List<ResourceIndexedSearchParamDate> dateParams = extractSearchParamDates(entity, theResource);
final List<ResourceLink> links = extractResourceLinks(entity, theResource);
populateResourceIntoEntity(theResource, entity);
myEntityManager.persist(historyEntry);
entity.setUpdated(new Date());
myEntityManager.persist(entity);
if (entity.isParamsStringPopulated()) {
for (ResourceIndexedSearchParamString next : entity.getParamsString()) {
myEntityManager.remove(next);
}
}
for (ResourceIndexedSearchParamString next : stringParams) {
myEntityManager.persist(next);
}
if (entity.isParamsTokenPopulated()) {
for (ResourceIndexedSearchParamToken next : entity.getParamsToken()) {
myEntityManager.remove(next);
}
}
for (ResourceIndexedSearchParamToken next : tokenParams) {
myEntityManager.persist(next);
}
if (entity.isParamsNumberPopulated()) {
for (ResourceIndexedSearchParamNumber next : entity.getParamsNumber()) {
myEntityManager.remove(next);
}
}
for (ResourceIndexedSearchParamNumber next : numberParams) {
myEntityManager.persist(next);
}
if (entity.isParamsDatePopulated()) {
for (ResourceIndexedSearchParamDate next : entity.getParamsDate()) {
myEntityManager.remove(next);
}
}
for (ResourceIndexedSearchParamDate next : dateParams) {
myEntityManager.persist(next);
}
if (entity.isHasLinks()) {
for (ResourceLink next : entity.getResourceLinks()) {
myEntityManager.remove(next);
}
}
for (ResourceLink next : links) {
myEntityManager.persist(next);
}
return entity;
}
});
return toMethodOutcome(savedEntity);
}
private Set<Long> addPredicateDate(Set<Long> thePids, List<IQueryParameterType> theOrParams) {
if (theOrParams == null || theOrParams.isEmpty()) {
return thePids;
@ -266,19 +574,19 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
Predicate eq = builder.equal(from.get("myTargetResourcePid"), targetPid);
codePredicates.add(eq);
} else {
// TODO: handle chain with resource type
String chain = myCtx.getResourceDefinition(myResourceType).getSearchParam(theParamName).getPath();
BaseRuntimeChildDefinition def = myCtx.newTerser().getDefinition(myResourceType, chain);
if (!(def instanceof RuntimeChildResourceDefinition)) {
throw new ConfigurationException("Property " + chain + " of type " + myResourceName + " is not a resource: " + def.getClass());
}
List<Class<? extends IResource>> resourceTypes;
if (ref.getType() == null) {
if (isBlank(ref.getResourceType())) {
RuntimeChildResourceDefinition resDef = (RuntimeChildResourceDefinition) def;
resourceTypes = resDef.getResourceTypes();
} else {
resourceTypes = new ArrayList<>();
resourceTypes.add(ref.getType());
RuntimeResourceDefinition resDef = myCtx.getResourceDefinition(ref.getResourceType());
resourceTypes.add(resDef.getImplementingClass());
}
for (Class<? extends IResource> nextType : resourceTypes) {
RuntimeResourceDefinition typeDef = myCtx.getResourceDefinition(nextType);
@ -313,7 +621,10 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
Predicate masterCodePredicate = builder.or(codePredicates.toArray(new Predicate[0]));
Predicate type = builder.equal(from.get("mySourcePath"), myResourceName + "." + theParamName);
RuntimeSearchParam param = myCtx.getResourceDefinition(getResourceType()).getSearchParam(theParamName);
String path = param.getPath();
Predicate type = builder.equal(from.get("mySourcePath"), path);
if (pidsToRetain.size() > 0) {
Predicate inPids = (from.get("mySourceResourcePid").in(pidsToRetain));
cq.where(builder.and(type, inPids, masterCodePredicate));
@ -422,53 +733,6 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
return new HashSet<Long>(q.getResultList());
}
@Transactional(propagation = Propagation.REQUIRED, readOnly = true)
@Override
public MethodOutcome create(T theResource) {
final X entity = toEntity(theResource);
entity.setPublished(new Date());
entity.setUpdated(entity.getPublished());
final List<ResourceIndexedSearchParamString> stringParams = extractSearchParamStrings(entity, theResource);
final List<ResourceIndexedSearchParamToken> tokenParams = extractSearchParamTokens(entity, theResource);
final List<ResourceIndexedSearchParamNumber> numberParams = extractSearchParamNumber(entity, theResource);
final List<ResourceIndexedSearchParamDate> dateParams = extractSearchParamDates(entity, theResource);
final List<ResourceLink> links = extractResourceLinks(entity, theResource);
ourLog.info("Saving links: {}", links);
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
template.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
template.setReadOnly(false);
template.execute(new TransactionCallback<X>() {
@Override
public X doInTransaction(TransactionStatus theStatus) {
myEntityManager.persist(entity);
for (ResourceIndexedSearchParamString next : stringParams) {
myEntityManager.persist(next);
}
for (ResourceIndexedSearchParamToken next : tokenParams) {
myEntityManager.persist(next);
}
for (ResourceIndexedSearchParamNumber next : numberParams) {
myEntityManager.persist(next);
}
for (ResourceIndexedSearchParamDate next : dateParams) {
myEntityManager.persist(next);
}
for (ResourceLink next : links) {
myEntityManager.persist(next);
}
return entity;
}
});
MethodOutcome outcome = toMethodOutcome(entity);
return outcome;
}
private List<ResourceLink> extractResourceLinks(X theEntity, T theResource) {
ArrayList<ResourceLink> retVal = new ArrayList<ResourceLink>();
@ -533,6 +797,8 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
}
}
theEntity.setHasLinks(retVal.size() > 0);
return retVal;
}
@ -580,6 +846,8 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
}
}
theEntity.setParamsDatePopulated(retVal.size() > 0);
return retVal;
}
@ -608,8 +876,7 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
if (nextObject instanceof QuantityDt) {
QuantityDt nextValue = (QuantityDt) nextObject;
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue().getValue(), nextValue.getSystem().getValueAsString(),
nextValue.getUnits().getValue());
ResourceIndexedSearchParamNumber nextEntity = new ResourceIndexedSearchParamNumber(resourceName, nextValue.getValue().getValue(), nextValue.getSystem().getValueAsString(), nextValue.getUnits().getValue());
nextEntity.setResource(theEntity, def.getName());
retVal.add(nextEntity);
} else {
@ -622,6 +889,8 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
}
}
theEntity.setParamsNumberPopulated(retVal.size() > 0);
return retVal;
}
@ -657,42 +926,57 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextValue.getValueAsString()), nextValue.getValueAsString());
nextEntity.setResource(theEntity, def.getName());
retVal.add(nextEntity);
} else if (nextObject instanceof HumanNameDt) {
ArrayList<StringDt> allNames = new ArrayList<>();
allNames.addAll(((HumanNameDt) nextObject).getFamily());
allNames.addAll(((HumanNameDt) nextObject).getGiven());
for (StringDt nextName : allNames) {
if (nextName.isEmpty()) {
continue;
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextName.getValueAsString()), nextName.getValueAsString());
nextEntity.setResource(theEntity, def.getName());
retVal.add(nextEntity);
}
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
if (nextObject instanceof HumanNameDt) {
ArrayList<StringDt> allNames = new ArrayList<>();
HumanNameDt nextHumanName = (HumanNameDt) nextObject;
allNames.addAll(nextHumanName.getFamily());
allNames.addAll(nextHumanName.getGiven());
for (StringDt nextName : allNames) {
if (nextName.isEmpty()) {
continue;
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextName.getValueAsString()), nextName.getValueAsString());
nextEntity.setResource(theEntity, def.getName());
retVal.add(nextEntity);
}
} else if (nextObject instanceof AddressDt) {
ArrayList<StringDt> allNames = new ArrayList<>();
AddressDt nextAddress = (AddressDt) nextObject;
allNames.addAll(nextAddress.getLine());
allNames.add(nextAddress.getCity());
allNames.add(nextAddress.getState());
allNames.add(nextAddress.getCountry());
allNames.add(nextAddress.getZip());
for (StringDt nextName : allNames) {
if (nextName.isEmpty()) {
continue;
}
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextName.getValueAsString()), nextName.getValueAsString());
nextEntity.setResource(theEntity, def.getName());
retVal.add(nextEntity);
}
} else if (nextObject instanceof ContactDt) {
ContactDt nextContact = (ContactDt) nextObject;
if (nextContact.getValue().isEmpty() == false) {
ResourceIndexedSearchParamString nextEntity = new ResourceIndexedSearchParamString(resourceName, normalizeString(nextContact.getValue().getValueAsString()), nextContact.getValue().getValueAsString());
nextEntity.setResource(theEntity, def.getName());
retVal.add(nextEntity);
}
} else {
if (!multiType) {
throw new ConfigurationException("Search param " + resourceName + " is of unexpected datatype: " + nextObject.getClass());
}
}
}
}
}
theEntity.setParamsStringPopulated(retVal.size() > 0);
return retVal;
}
private String normalizeString(String theString) {
char[] out = new char[theString.length()];
theString = Normalizer.normalize(theString, Normalizer.Form.NFD);
int j = 0;
for (int i = 0, n = theString.length(); i < n; ++i) {
char c = theString.charAt(i);
if (c <= '\u007F') {
out[j++] = c;
}
}
return new String(out).toUpperCase();
}
private List<ResourceIndexedSearchParamToken> extractSearchParamTokens(X theEntity, T theResource) {
ArrayList<ResourceIndexedSearchParamToken> retVal = new ArrayList<ResourceIndexedSearchParamToken>();
@ -753,11 +1037,9 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
}
}
return retVal;
}
theEntity.setParamsTokenPopulated(retVal.size() > 0);
public Class<T> getResourceType() {
return myResourceType;
return retVal;
}
private Map<Class<? extends IResource>, IFhirResourceDao<?>> getResourceTypeToDao() {
@ -772,39 +1054,17 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
return resourceTypeToDao;
}
@Override
public Class<X> getTableType() {
return myTableType;
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public List<T> history(IdDt theId) {
ArrayList<T> retVal = new ArrayList<T>();
String resourceType = myCtx.getResourceDefinition(myResourceType).getName();
TypedQuery<ResourceHistoryTable> q = myEntityManager.createQuery(ResourceHistoryTable.Q_GETALL, ResourceHistoryTable.class);
q.setParameter("PID", theId.asLong());
q.setParameter("RESTYPE", resourceType);
// TypedQuery<ResourceHistoryTable> query =
// myEntityManager.createQuery(criteriaQuery);
List<ResourceHistoryTable> results = q.getResultList();
for (ResourceHistoryTable next : results) {
retVal.add(toResource(next));
private String normalizeString(String theString) {
char[] out = new char[theString.length()];
theString = Normalizer.normalize(theString, Normalizer.Form.NFD);
int j = 0;
for (int i = 0, n = theString.length(); i < n; ++i) {
char c = theString.charAt(i);
if (c <= '\u007F') {
out[j++] = c;
}
}
try {
retVal.add(read(theId));
} catch (ResourceNotFoundException e) {
// ignore
}
if (retVal.isEmpty()) {
throw new ResourceNotFoundException(theId);
}
return retVal;
return new String(out).toUpperCase();
}
private void populateResourceIntoEntity(T theResource, X retVal) {
@ -820,22 +1080,6 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
}
@PostConstruct
public void postConstruct() throws Exception {
myResourceType = myTableType.newInstance().getResourceType();
myCtx = new FhirContext(myResourceType);
myResourceName = myCtx.getResourceDefinition(myResourceType).getName();
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public T read(IdDt theId) {
X entity = readEntity(theId);
T retVal = toResource(entity);
return retVal;
}
private X readEntity(IdDt theId) {
X entity = (X) myEntityManager.find(myTableType, theId.asLong());
if (entity == null) {
@ -844,132 +1088,6 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
return entity;
}
@Override
public List<T> search(Map<String, IQueryParameterType> theParams) {
SearchParameterMap map = new SearchParameterMap();
for (Entry<String, IQueryParameterType> nextEntry : theParams.entrySet()) {
map.put(nextEntry.getKey(), new ArrayList<List<IQueryParameterType>>());
map.get(nextEntry.getKey()).add(Collections.singletonList(nextEntry.getValue()));
}
return search(map);
}
@Override
public List<T> search(String theParameterName, IQueryParameterType theValue) {
return search(Collections.singletonMap(theParameterName, theValue));
}
@Override
public Set<Long> searchForIds(Map<String, IQueryParameterType> theParams) {
Map<String, List<List<IQueryParameterType>>> map = new HashMap<String, List<List<IQueryParameterType>>>();
for (Entry<String, IQueryParameterType> nextEntry : theParams.entrySet()) {
map.put(nextEntry.getKey(), new ArrayList<List<IQueryParameterType>>());
map.get(nextEntry.getKey()).add(Collections.singletonList(nextEntry.getValue()));
}
return searchForIdsWithAndOr(map);
}
@Override
public Set<Long> searchForIdsWithAndOr(Map<String, List<List<IQueryParameterType>>> theParams) {
Map<String, List<List<IQueryParameterType>>> params = theParams;
if (params == null) {
params = Collections.emptyMap();
}
RuntimeResourceDefinition resourceDef = myCtx.getResourceDefinition(myResourceType);
Set<Long> pids = new HashSet<Long>();
for (Entry<String, List<List<IQueryParameterType>>> nextParamEntry : params.entrySet()) {
String nextParamName = nextParamEntry.getKey();
RuntimeSearchParam nextParamDef = resourceDef.getSearchParam(nextParamName);
if (nextParamDef != null) {
if (nextParamDef.getParamType() == SearchParamTypeEnum.TOKEN) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateToken(pids, nextAnd);
if (pids.isEmpty()) {
return new HashSet<Long>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.STRING) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateString(pids, nextAnd);
if (pids.isEmpty()) {
return new HashSet<Long>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.QUANTITY) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateQuantity(pids, nextAnd);
if (pids.isEmpty()) {
return new HashSet<Long>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.DATE) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateDate(pids, nextAnd);
if (pids.isEmpty()) {
return new HashSet<Long>();
}
}
} else if (nextParamDef.getParamType() == SearchParamTypeEnum.REFERENCE) {
for (List<IQueryParameterType> nextAnd : nextParamEntry.getValue()) {
pids = addPredicateReference(nextParamName, pids, nextAnd);
if (pids.isEmpty()) {
return new HashSet<Long>();
}
}
} else {
throw new IllegalArgumentException("Don't know how to handle parameter of type: " + nextParamDef.getParamType());
}
}
}
return pids;
}
@Override
public Set<Long> searchForIds(String theParameterName, IQueryParameterType theValue) {
return searchForIds(Collections.singletonMap(theParameterName, theValue));
}
@Override
public List<T> search(SearchParameterMap theParams) {
Set<Long> pids;
if (theParams.isEmpty()) {
pids = null;
} else {
pids = searchForIdsWithAndOr(theParams);
if (pids.isEmpty()) {
return new ArrayList<>();
}
}
// Execute the query and make sure we return distinct results
{
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<X> cq = builder.createQuery(myTableType);
Root<X> from = cq.from(myTableType);
if (!theParams.isEmpty()) {
cq.where(from.get("myId").in(pids));
}
TypedQuery<X> q = myEntityManager.createQuery(cq);
List<T> retVal = new ArrayList<>();
for (X next : q.getResultList()) {
T resource = toResource(next);
retVal.add(resource);
}
return retVal;
}
}
@Required
public void setTableType(Class<X> theTableType) {
myTableType = theTableType;
}
private X toEntity(T theResource) {
X retVal;
try {
@ -1034,27 +1152,4 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
}
return retVal;
}
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public MethodOutcome update(final T theResource, final IdDt theId) {
TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
X savedEntity = template.execute(new TransactionCallback<X>() {
@Override
public X doInTransaction(TransactionStatus theStatus) {
final X entity = readEntity(theId);
final ResourceHistoryTable existing = entity.toHistory(myCtx);
populateResourceIntoEntity(theResource, entity);
myEntityManager.persist(existing);
entity.setUpdated(new Date());
myEntityManager.persist(entity);
return entity;
}
});
return toMethodOutcome(savedEntity);
}
}

View File

@ -5,15 +5,17 @@ import java.util.HashMap;
import java.util.List;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterOr;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.param.DateRangeParam;
public class SearchParameterMap extends HashMap<String, List<List<IQueryParameterType>>> {
private static final long serialVersionUID = 1L;
public void add(String theName, IQueryParameterType theParam) {
if (theParam == null) {
return;
}
if (!containsKey(theName)) {
put(theName, new ArrayList<List<IQueryParameterType>>());
}
@ -22,13 +24,19 @@ public class SearchParameterMap extends HashMap<String, List<List<IQueryParamete
get(theName).add(list);
}
public void add(String theName, IQueryParameterAnd theBirthdate) {
public void add(String theName, IQueryParameterAnd theAnd) {
if (theAnd==null) {
return;
}
if (!containsKey(theName)) {
put(theName, new ArrayList<List<IQueryParameterType>>());
}
for (QualifiedParamList next : theBirthdate.getValuesAsQueryTokens()) {
next.get
for (IQueryParameterOr next : theAnd.getValuesAsQueryTokens()) {
if (next==null) {
continue;
}
get(theName).add(next.getValuesAsQueryTokens());
}
}

View File

@ -28,17 +28,29 @@ import ca.uhn.fhir.model.primitive.IdDt;
@DiscriminatorColumn(name = "SVCVER_TYPE", length = 20, discriminatorType = DiscriminatorType.STRING)
public abstract class BaseResourceTable<T extends IResource> extends BaseHasResource {
@Column(name = "SP_HAS_LINKS")
private boolean myHasLinks;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "RES_ID")
private Long myId;
@OneToMany(mappedBy = "myTargetResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
private Collection<ResourceLink> myIncomingResourceLinks;
@OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
private Collection<ResourceIndexedSearchParamDate> myParamsDate;
@Column(name = "SP_DATE_PRESENT")
private boolean myParamsDatePopulated;
@OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
private Collection<ResourceIndexedSearchParamNumber> myParamsNumber;
@Column(name = "SP_NUMBER_PRESENT")
private boolean myParamsNumberPopulated;
@OneToMany(mappedBy = "myResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
private Collection<ResourceIndexedSearchParamString> myParamsString;
@ -51,6 +63,9 @@ public abstract class BaseResourceTable<T extends IResource> extends BaseHasReso
@Column(name = "SP_TOKEN_PRESENT")
private boolean myParamsTokenPopulated;
@OneToMany(mappedBy = "mySourceResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
private Collection<ResourceLink> myResourceLinks;
@OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Collection<ResourceTag> myTags;
@ -67,14 +82,14 @@ public abstract class BaseResourceTable<T extends IResource> extends BaseHasReso
getTags().add(new ResourceTag(this, theTerm, theLabel, theScheme));
}
public Long getIdAsLong() {
return myId;
}
public IdDt getId() {
return new IdDt(myId);
}
public Long getIdAsLong() {
return myId;
}
public Collection<ResourceIndexedSearchParamDate> getParamsDate() {
if (myParamsDate == null) {
myParamsDate = new ArrayList<>();
@ -82,6 +97,13 @@ public abstract class BaseResourceTable<T extends IResource> extends BaseHasReso
return myParamsDate;
}
public Collection<ResourceIndexedSearchParamNumber> getParamsNumber() {
if (myParamsNumber == null) {
myParamsNumber = new ArrayList<>();
}
return myParamsNumber;
}
public Collection<ResourceIndexedSearchParamString> getParamsString() {
if (myParamsString == null) {
myParamsString = new ArrayList<>();
@ -96,6 +118,13 @@ public abstract class BaseResourceTable<T extends IResource> extends BaseHasReso
return myParamsToken;
}
public Collection<ResourceLink> getResourceLinks() {
if (myResourceLinks == null) {
myResourceLinks = new ArrayList<>();
}
return myResourceLinks;
}
public abstract Class<T> getResourceType();
public Collection<ResourceTag> getTags() {
@ -109,10 +138,18 @@ public abstract class BaseResourceTable<T extends IResource> extends BaseHasReso
return new IdDt(myVersion);
}
public boolean isHasLinks() {
return myHasLinks;
}
public boolean isParamsDatePopulated() {
return myParamsDatePopulated;
}
public boolean isParamsNumberPopulated() {
return myParamsNumberPopulated;
}
public boolean isParamsStringPopulated() {
return myParamsStringPopulated;
}
@ -121,6 +158,10 @@ public abstract class BaseResourceTable<T extends IResource> extends BaseHasReso
return myParamsTokenPopulated;
}
public void setHasLinks(boolean theHasLinks) {
myHasLinks = theHasLinks;
}
public void setId(IdDt theId) {
myId = theId.asLong();
}
@ -133,6 +174,10 @@ public abstract class BaseResourceTable<T extends IResource> extends BaseHasReso
myParamsDatePopulated = theParamsDatePopulated;
}
public void setParamsNumberPopulated(boolean theParamsNumberPopulated) {
myParamsNumberPopulated = theParamsNumberPopulated;
}
public void setParamsString(Collection<ResourceIndexedSearchParamString> theParamsString) {
myParamsString = theParamsString;
}

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.entity;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@ -26,7 +27,7 @@ public class ResourceHistoryTag extends BaseTag implements Serializable {
@JoinColumn(name="RES_TYPE", referencedColumnName="RES_TYPE"),
@JoinColumn(name="PID", referencedColumnName="PID"),
@JoinColumn(name="VERSION", referencedColumnName="VERSION")
})
}, foreignKey=@ForeignKey(name="FK_HT_RT"))
private ResourceHistoryTable myResourceHistory;
public ResourceHistoryTag() {

View File

@ -4,6 +4,8 @@ import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@ -11,7 +13,7 @@ import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
@Table(name = "IDX_SP_DATE")
@Table(name = "SPIDX_DATE", indexes= {@Index(name="IDX_SP_DATE", columnList="myValueLow,myValueHigh")})
public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchParam {
private static final long serialVersionUID = 1L;
@ -25,7 +27,7 @@ public class ResourceIndexedSearchParamDate extends BaseResourceIndexedSearchPar
public Date myValueLow;
@ManyToOne(optional = false)
@JoinColumn(name = "RESOURCE_PID", nullable = false)
@JoinColumn(name = "RESOURCE_PID", nullable = false, foreignKey=@ForeignKey(name="FK_ISD_RESOURCE"))
private BaseResourceTable<?> myResource;
@Column(name = "RESOURCE_PID", insertable = false, updatable = false)

View File

@ -4,12 +4,14 @@ import java.math.BigDecimal;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "IDX_SP_NUMBER")
@Table(name = "SPIDX_NUMBER", indexes= {@Index(name="IDX_SP_NUMBER", columnList="myValue")})
public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchParam {
private static final long serialVersionUID = 1L;
@ -24,7 +26,7 @@ public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchP
public BigDecimal myValue;
@ManyToOne(optional = false)
@JoinColumn(name = "RESOURCE_PID", nullable = false)
@JoinColumn(name = "RESOURCE_PID", nullable = false, foreignKey=@ForeignKey(name="FK_ISN_RESOURCE"))
private BaseResourceTable<?> myResource;
@Column(name = "RESOURCE_PID", insertable = false, updatable = false)

View File

@ -3,18 +3,20 @@ package ca.uhn.fhir.jpa.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "IDX_SP_STRING")
@Table(name = "SPIDX_STRING", indexes= {@Index(name="IDX_SP_STRING", columnList="myValueNormalized")})
public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchParam {
private static final long serialVersionUID = 1L;
@ManyToOne(optional = false, cascade = {}, fetch = FetchType.LAZY)
@JoinColumn(name = "RESOURCE_PID", nullable = false)
@JoinColumn(name = "RESOURCE_PID", nullable = false, foreignKey=@ForeignKey(name="FK_ISS_RESOURCE"))
private BaseResourceTable<?> myResource;
@Column(name = "RESOURCE_PID", insertable=false, updatable=false)

View File

@ -2,18 +2,20 @@ package ca.uhn.fhir.jpa.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "IDX_SP_TOKEN")
@Table(name = "SPIDX_TOKEN", indexes= {@Index(name="IDX_SP_STRING", columnList="mySystem,myValue")})
public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchParam {
private static final long serialVersionUID = 1L;
@ManyToOne(optional = false)
@JoinColumn(name = "RESOURCE_PID", nullable = false)
@JoinColumn(name = "RESOURCE_PID", nullable = false, foreignKey=@ForeignKey(name="FK_IST_RESOURCE"))
private BaseResourceTable<?> myResource;
@Column(name = "RESOURCE_PID", insertable=false, updatable=false)
@ -33,6 +35,9 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
mySystem = theSystem;
}
public ResourceIndexedSearchParamToken() {
}
public ResourceIndexedSearchParamToken(String theName, String theSystem, String theValue) {
setName(theName);
setSystem(theSystem);

View File

@ -1,6 +1,7 @@
package ca.uhn.fhir.jpa.entity;
import javax.persistence.Entity;
import javax.persistence.ForeignKey;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@ -19,7 +20,7 @@ public class ResourceTag extends BaseTag {
private Long myId;
@ManyToOne(cascade= {})
@JoinColumn(name = "RESOURCE_PID", nullable=false)
@JoinColumn(name = "RESOURCE_PID", nullable=false, foreignKey=@ForeignKey(name="FK_RESTAG_RESOURCE"))
private BaseResourceTable<?> myResource;
public ResourceTag() {

View File

@ -1,18 +1,14 @@
package ca.uhn.fhir.jpa.dao;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hamcrest.core.StringContains;
import org.junit.AfterClass;
@ -190,7 +186,7 @@ public class FhirResourceDaoTest {
found = ourPatientDao.search(Patient.SP_GENDER, new IdentifierDt(null, "F"));
assertEquals(0, found.size());
Map<String, List<List<IQueryParameterType>>> map = new HashMap<>();
SearchParameterMap map = new SearchParameterMap();
map.put(Patient.SP_IDENTIFIER, new ArrayList<List<IQueryParameterType>>());
map.get(Patient.SP_IDENTIFIER).add(new ArrayList<IQueryParameterType>());
map.get(Patient.SP_IDENTIFIER).get(0).add(new IdentifierDt("urn:system", "001testPersistSearchParams"));
@ -201,7 +197,7 @@ public class FhirResourceDaoTest {
assertEquals(1, found.size());
assertEquals(id, found.get(0).getId().asLong().longValue());
map = new HashMap<>();
map = new SearchParameterMap();
map.put(Patient.SP_IDENTIFIER, new ArrayList<List<IQueryParameterType>>());
map.get(Patient.SP_IDENTIFIER).add(new ArrayList<IQueryParameterType>());
map.get(Patient.SP_IDENTIFIER).get(0).add(new IdentifierDt("urn:system", "001testPersistSearchParams"));
@ -358,7 +354,7 @@ public class FhirResourceDaoTest {
result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(Patient.SP_NAME, "testSearchResourceLinkWithChainWithMultipleTypesYY"));
assertEquals(0,result.size());
result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam(Patient.class, Patient.SP_NAME, "testSearchResourceLinkWithChainWithMultipleTypes01"));
result = ourObservationDao.search(Observation.SP_SUBJECT, new ReferenceParam("Patient", Patient.SP_NAME, "testSearchResourceLinkWithChainWithMultipleTypes01"));
assertEquals(1,result.size());
assertEquals(obsId01, result.get(0).getId());
@ -505,6 +501,36 @@ public class FhirResourceDaoTest {
}
@Test
public void testUpdateMaintainsSearchParams() throws InterruptedException {
Patient p1 = new Patient();
p1.addIdentifier("urn:system", "testUpdateMaintainsSearchParamsAAA");
p1.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsAAA");
IdDt p1id = ourPatientDao.create(p1).getId();
Patient p2 = new Patient();
p2.addIdentifier("urn:system", "testUpdateMaintainsSearchParamsBBB");
p2.addName().addFamily("Tester").addGiven("testUpdateMaintainsSearchParamsBBB");
IdDt p2id = ourPatientDao.create(p2).getId();
Set<Long> ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsAAA"));
assertEquals(1,ids.size());
assertThat(ids, contains(p1id.asLong()));
// Update the name
p1.getNameFirstRep().getGivenFirstRep().setValue("testUpdateMaintainsSearchParamsBBB");
ourPatientDao.update(p1, p1id);
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsAAA"));
assertEquals(0,ids.size());
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsBBB"));
assertEquals(2,ids.size());
}
@AfterClass
public static void afterClass() {
ourCtx.close();
@ -514,7 +540,7 @@ public class FhirResourceDaoTest {
@BeforeClass
public static void beforeClass() {
ourTestStarted = new Date();
ourCtx = new ClassPathXmlApplicationContext("fhir-spring-test-config.xml");
ourCtx = new ClassPathXmlApplicationContext("fhir-jpabase-spring-test-config.xml");
ourPatientDao = ourCtx.getBean("myPatientDao", IFhirResourceDao.class);
ourObservationDao = ourCtx.getBean("myObservationDao", IFhirResourceDao.class);
ourDiagnosticReportDao = ourCtx.getBean("myDiagnosticReportDao", IFhirResourceDao.class);

View File

@ -1,8 +1,9 @@
package ca.uhn.fhir.jpa.entity;
package ca.uhn.fhir.jpa.testentity;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import ca.uhn.fhir.jpa.entity.BaseResourceTable;
import ca.uhn.fhir.model.dstu.resource.Device;
@Entity

View File

@ -1,8 +1,9 @@
package ca.uhn.fhir.jpa.entity;
package ca.uhn.fhir.jpa.testentity;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import ca.uhn.fhir.jpa.entity.BaseResourceTable;
import ca.uhn.fhir.model.dstu.resource.DiagnosticReport;
@Entity

View File

@ -1,8 +1,9 @@
package ca.uhn.fhir.jpa.entity;
package ca.uhn.fhir.jpa.testentity;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import ca.uhn.fhir.jpa.entity.BaseResourceTable;
import ca.uhn.fhir.model.dstu.resource.Location;
@Entity

View File

@ -1,8 +1,9 @@
package ca.uhn.fhir.jpa.entity;
package ca.uhn.fhir.jpa.testentity;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import ca.uhn.fhir.jpa.entity.BaseResourceTable;
import ca.uhn.fhir.model.dstu.resource.Observation;
@Entity

View File

@ -1,8 +1,9 @@
package ca.uhn.fhir.jpa.entity;
package ca.uhn.fhir.jpa.testentity;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import ca.uhn.fhir.jpa.entity.BaseResourceTable;
import ca.uhn.fhir.model.dstu.resource.Organization;
@Entity

View File

@ -1,8 +1,9 @@
package ca.uhn.fhir.jpa.entity;
package ca.uhn.fhir.jpa.testentity;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import ca.uhn.fhir.jpa.entity.BaseResourceTable;
import ca.uhn.fhir.model.dstu.resource.Patient;
@Entity

View File

@ -1,8 +1,9 @@
package ca.uhn.fhir.jpa.entity;
package ca.uhn.fhir.jpa.testentity;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import ca.uhn.fhir.jpa.entity.BaseResourceTable;
import ca.uhn.fhir.model.dstu.resource.Questionnaire;
@Entity

View File

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="FHIR_UT" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- <class>ca.uhn.fhir.jpa.entity.PatientResourceTable</class> -->
<class>ca.uhn.fhir.jpa.testentity.DeviceResourceTable</class>
<class>ca.uhn.fhir.jpa.testentity.DiagnosticReportResourceTable</class>
<class>ca.uhn.fhir.jpa.testentity.LocationResourceTable</class>
<class>ca.uhn.fhir.jpa.testentity.ObservationResourceTable</class>
<class>ca.uhn.fhir.jpa.testentity.OrganizationResourceTable</class>
<class>ca.uhn.fhir.jpa.testentity.PatientResourceTable</class>
<class>ca.uhn.fhir.jpa.testentity.QuestionnaireResourceTable</class>
<class>ca.uhn.fhir.jpa.testentity.QuestionnaireResourceTable</class>
<class>ca.uhn.fhir.jpa.entity.ResourceHistoryTable</class>
<class>ca.uhn.fhir.jpa.entity.ResourceHistoryTag</class>
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate</class>
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber</class>
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString</class>
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken</class>
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<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.connection.username" value="sa" />
<property name="hibernate.connection.password" value="" />
<property name="hibernate.jdbc.batch_size" value="0" />
<property name="hibernate.cache.use_minimal_puts" value="false" />
<property name="hibernate.show_sql" value="false" />
<property name="hibernate.cache.use_query_cache" value="false" />
<property name="hibernate.cache.use_second_level_cache" value="false" />
<property name="hibernate.cache.use_structured_entries" value="false" />
<property name="hibernate.ejb.naming_strategy" value="ca.uhn.fhir.jpa.util.CustomNamingStrategy" />
<!--
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory" />
-->
</properties>
</persistence-unit>
</persistence>

View File

@ -14,22 +14,22 @@
<context:mbean-server />
<bean id="myPatientDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="tableType" value="ca.uhn.fhir.jpa.entity.PatientResourceTable"/>
<property name="tableType" value="ca.uhn.fhir.jpa.testentity.PatientResourceTable"/>
</bean>
<bean id="myObservationDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="tableType" value="ca.uhn.fhir.jpa.entity.ObservationResourceTable"/>
<property name="tableType" value="ca.uhn.fhir.jpa.testentity.ObservationResourceTable"/>
</bean>
<bean id="myDiagnosticReportDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="tableType" value="ca.uhn.fhir.jpa.entity.DiagnosticReportResourceTable"/>
<property name="tableType" value="ca.uhn.fhir.jpa.testentity.DiagnosticReportResourceTable"/>
</bean>
<bean id="myDeviceDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="tableType" value="ca.uhn.fhir.jpa.entity.DeviceResourceTable"/>
<property name="tableType" value="ca.uhn.fhir.jpa.testentity.DeviceResourceTable"/>
</bean>
<bean id="myLocationDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="tableType" value="ca.uhn.fhir.jpa.entity.LocationResourceTable"/>
<property name="tableType" value="ca.uhn.fhir.jpa.testentity.LocationResourceTable"/>
</bean>
<bean id="myOrganizationDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="tableType" value="ca.uhn.fhir.jpa.entity.OrganizationResourceTable"/>
<property name="tableType" value="ca.uhn.fhir.jpa.testentity.OrganizationResourceTable"/>
</bean>
<bean id="myPersistenceDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true">

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry excluding="**/*.java" kind="src" output="target/test-classes" path="src/test/resources"/>
<classpathentry kind="src" path="target/generated-sources/tinder"/>
<classpathentry including="**/*.java" kind="src" path="src/main/java"/>
<classpathentry excluding="**/*.java" kind="src" path="src/main/resources"/>
<classpathentry kind="var" path="M2_REPO/javax/activation/activation/1.1/activation-1.1.jar" sourcepath="M2_REPO/javax/activation/activation/1.1/activation-1.1-sources.jar"/>

View File

@ -80,6 +80,33 @@
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-tinder-plugin</artifactId>
<version>0.3</version>
<executions>
<execution>
<id>buildclient</id>
<goals>
<goal>generate-jparest-server</goal>
</goals>
<configuration>
<packageBase>ca.uhn.test.jpasrv</packageBase>
<baseResourceNames>
<baseResourceName>device</baseResourceName>
<baseResourceName>location</baseResourceName>
<baseResourceName>observation</baseResourceName>
<baseResourceName>organization</baseResourceName>
<baseResourceName>patient</baseResourceName>
<baseResourceName>practitioner</baseResourceName>
<baseResourceName>questionnaire</baseResourceName>
<baseResourceName>valueset</baseResourceName>
</baseResourceNames>
<buildDatatypes>true</buildDatatypes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>

View File

@ -0,0 +1,177 @@
package ca.uhn.fhir.jpa.test;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Organization;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.resource.Questionnaire;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.client.IGenericClient;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.tester.RestfulServerTesterServlet;
import ca.uhn.test.jpasrv.ObservationResourceProvider;
import ca.uhn.test.jpasrv.OrganizationResourceProvider;
import ca.uhn.test.jpasrv.PatientResourceProvider;
public class CompleteResourceProviderTest {
private static ClassPathXmlApplicationContext ourAppCtx;
private static FhirContext ourCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CompleteResourceProviderTest.class);
private static Server ourServer;
private static IFhirResourceDao<Patient> patientDao;
private static IFhirResourceDao<Questionnaire> questionnaireDao;
private static IGenericClient ourClient;
private static IFhirResourceDao<Observation> observationDao;
// @Test
// public void test01UploadTestResources() throws Exception {
//
// IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:8888/fhir/context");
//
// File[] files = new File("src/test/resources/resources").listFiles(new PatternFilenameFilter(".*patient.*"));
// for (File file : files) {
// ourLog.info("Uploading: {}", file);
// Patient patient = ourCtx.newXmlParser().parseResource(Patient.class, new FileReader(file));
// client.create(patient);
// }
//
// files = new File("src/test/resources/resources").listFiles(new PatternFilenameFilter(".*questionnaire.*"));
// for (File file : files) {
// ourLog.info("Uploading: {}", file);
// Questionnaire patient = ourCtx.newXmlParser().parseResource(Questionnaire.class, new FileReader(file));
// client.create(patient);
// }
//
// }
@Test
public void testSearchByIdentifier() {
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByIdentifier01");
p1.addName().addFamily("testSearchByIdentifierFamily01").addGiven("testSearchByIdentifierGiven01");
IdDt p1Id = ourClient.create(p1).getId();
Patient p2 = new Patient();
p2.addIdentifier().setSystem("urn:system").setValue("testSearchByIdentifier02");
p2.addName().addFamily("testSearchByIdentifierFamily01").addGiven("testSearchByIdentifierGiven02");
ourClient.create(p2).getId();
Bundle actual = ourClient.search().forResource(Patient.class).where(Patient.IDENTIFIER.exactly().systemAndCode("urn:system", "testSearchByIdentifier01")).encodedJson().prettyPrint().execute();
assertEquals(1, actual.size());
assertEquals(p1Id, actual.getEntries().get(0).getId());
}
@Test
public void testSearchByResourceChain() {
Organization o1 = new Organization();
o1.setName("testSearchByResourceChainName01");
IdDt o1id = ourClient.create(o1).getId();
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByResourceChain01");
p1.addName().addFamily("testSearchByResourceChainFamily01").addGiven("testSearchByResourceChainGiven01");
p1.setManagingOrganization(new ResourceReferenceDt(Organization.class, o1id));
IdDt p1Id = ourClient.create(p1).getId();
//@formatter:off
Bundle actual = ourClient.search()
.forResource(Patient.class)
.where(Patient.PROVIDER.hasId(o1id))
.encodedJson().prettyPrint().execute();
//@formatter:on
assertEquals(1, actual.size());
assertEquals(p1Id, actual.getEntries().get(0).getId());
}
@Test
public void testInsertBadReference() {
Patient p1 = new Patient();
p1.addIdentifier().setSystem("urn:system").setValue("testSearchByResourceChain01");
p1.addName().addFamily("testSearchByResourceChainFamily01").addGiven("testSearchByResourceChainGiven01");
p1.setManagingOrganization(new ResourceReferenceDt(Organization.class, "132312323"));
try {
ourClient.create(p1).getId();
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), containsString("Organization/132312323"));
}
}
@AfterClass
public static void afterClass() throws Exception {
ourServer.stop();
ourAppCtx.stop();
}
@SuppressWarnings("unchecked")
@BeforeClass
public static void beforeClass() throws Exception {
ourAppCtx = new ClassPathXmlApplicationContext("fhir-spring-test-config.xml");
patientDao = (IFhirResourceDao<Patient>) ourAppCtx.getBean("myPatientDao", IFhirResourceDao.class);
PatientResourceProvider patientRp = new PatientResourceProvider();
patientRp.setDao(patientDao);
questionnaireDao = (IFhirResourceDao<Questionnaire>) ourAppCtx.getBean("myQuestionnaireDao", IFhirResourceDao.class);
QuestionnaireResourceProvider questionnaireRp = new QuestionnaireResourceProvider();
questionnaireRp.setDao(questionnaireDao);
observationDao = (IFhirResourceDao<Observation>) ourAppCtx.getBean("myObservationDao", IFhirResourceDao.class);
ObservationResourceProvider observationRp = new ObservationResourceProvider();
observationRp.setDao(observationDao);
IFhirResourceDao<Organization> organizationDao = (IFhirResourceDao<Organization>) ourAppCtx.getBean("myOrganizationDao", IFhirResourceDao.class);
OrganizationResourceProvider organizationRp = new OrganizationResourceProvider();
organizationRp.setDao(organizationDao);
RestfulServer restServer = new RestfulServer();
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
int myPort = 8888;
ourServer = new Server(myPort);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
RestfulServerTesterServlet testerServlet = new RestfulServerTesterServlet();
String serverBase = "http://localhost:" + myPort + "/fhir/context";
testerServlet.setServerBase(serverBase);
// testerServlet.setServerBase("http://fhir.healthintersections.com.au/open");
ServletHolder handler = new ServletHolder();
handler.setServlet(testerServlet);
proxyHandler.addServlet(handler, "/fhir/tester/*");
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(restServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
ourServer.setHandler(proxyHandler);
ourServer.start();
ourCtx = restServer.getFhirContext();
ourClient = ourCtx.newRestfulGenericClient(serverBase);
}
}

View File

@ -10,6 +10,7 @@ import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.dstu.resource.Questionnaire;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.tester.RestfulServerTesterServlet;
import ca.uhn.test.jpasrv.PatientResourceProvider;
public class JpaTestApp {

View File

@ -1,46 +0,0 @@
package ca.uhn.fhir.jpa.test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import ca.uhn.fhir.jpa.dao.BaseJpaResourceProvider;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.rest.annotation.OptionalParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
public class PatientResourceProvider extends BaseJpaResourceProvider<Patient> {
@Override
public Class<? extends IResource> getResourceType() {
return Patient.class;
}
@Search
public List<Patient> searchByName(
@Description(shortDefinition="Matches the patient's family (last) name")
@RequiredParam(name=Patient.SP_NAME) StringDt theFamily,
@Description(shortDefinition="Matches the patient's given (first) name")
@OptionalParam(name=Patient.SP_GIVEN) StringDt theGiven) {
Map<String, IQueryParameterType> params = new HashMap<>();
params.put(Patient.SP_NAME, theFamily);
params.put(Patient.SP_GIVEN, theGiven);
return getDao().search(params);
}
@Search
public List<Patient> searchByGiven(
@Description(shortDefinition="Matches the patient's given (first) name")
@RequiredParam(name=Patient.SP_GIVEN) StringDt theValue
) {
return getDao().search(Patient.SP_GIVEN, theValue);
}
}

View File

@ -14,18 +14,29 @@
<context:mbean-server />
<bean id="myPatientDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="tableType" value="ca.uhn.fhir.jpa.entity.PatientResourceTable"/>
<property name="tableType" value="ca.uhn.test.jpasrv.PatientResourceTable"/>
</bean>
<bean id="myObservationDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="tableType" value="ca.uhn.test.jpasrv.ObservationResourceTable"/>
</bean>
<bean id="myOrganizationDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="tableType" value="ca.uhn.test.jpasrv.OrganizationResourceTable"/>
</bean>
<bean id="myLocationDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="tableType" value="ca.uhn.test.jpasrv.LocationResourceTable"/>
</bean>
<bean id="myQuestionnaireDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="tableType" value="ca.uhn.fhir.jpa.entity.QuestionnaireResourceTable"/>
<property name="tableType" value="ca.uhn.test.jpasrv.QuestionnaireResourceTable"/>
</bean>
<bean id="myPersistenceDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true">
<property name="url" value="jdbc:derby:directory:myUnitTestDB;create=true" />
<property name="url" value="jdbc:derby:memory:myUnitTestDB;create=true" />
<!-- <property name="url" value="jdbc:derby:directory:myUnitTestDB;create=true" /> -->
</bean>
<bean id="myEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="myPersistenceDataSource" />
<property name="persistenceXmlLocation" value="classpath:fhir_jpatest_persistence.xml" />
<property name="persistenceUnitName" value="FHIR_UT" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">

View File

@ -6,8 +6,21 @@
<persistence-unit name="FHIR_UT" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<!-- <class>ca.uhn.fhir.jpa.entity.PatientResourceTable</class> -->
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<class>ca.uhn.test.jpasrv.PatientResourceTable</class>
<class>ca.uhn.test.jpasrv.LocationResourceTable</class>
<class>ca.uhn.test.jpasrv.ObservationResourceTable</class>
<class>ca.uhn.test.jpasrv.OrganizationResourceTable</class>
<class>ca.uhn.test.jpasrv.QuestionnaireResourceTable</class>
<class>ca.uhn.fhir.jpa.entity.ResourceHistoryTable</class>
<class>ca.uhn.fhir.jpa.entity.ResourceHistoryTag</class>
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate</class>
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber</class>
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString</class>
<class>ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken</class>
<class>ca.uhn.fhir.jpa.entity.ResourceLink</class>
<class>ca.uhn.fhir.jpa.entity.ResourceTag</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.connection.url" value="jdbc:hsqldb:mem:unit-testing-jpa" />
<property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" />

View File

@ -45,7 +45,7 @@ public class TinderJpaRestServerMojo extends AbstractMojo {
private String packageBase;
@Parameter(required = true)
private List<String> resources;
private List<String> baseResourceNames;
@Override
public void execute() throws MojoExecutionException, MojoFailureException {
@ -54,14 +54,20 @@ public class TinderJpaRestServerMojo extends AbstractMojo {
directoryBase.mkdirs();
ResourceGeneratorUsingSpreadsheet gen = new ResourceGeneratorUsingSpreadsheet();
gen.setBaseResourceNames(resources);
gen.setBaseResourceNames(baseResourceNames);
gen.setFilenameSuffix("ResourceProvider");
gen.setTemplate("/vm/jpa_resource_provider.vm");
try {
gen.parse();
gen.setFilenameSuffix("ResourceProvider");
gen.setTemplate("/vm/jpa_resource_provider.vm");
gen.writeAll(directoryBase, packageBase);
gen.setFilenameSuffix("ResourceTable");
gen.setTemplate("/vm/jpa_resource_table.vm");
gen.writeAll(directoryBase, packageBase);
} catch (Exception e) {
throw new MojoFailureException("Failed to generate server",e);
}
@ -114,7 +120,7 @@ public class TinderJpaRestServerMojo extends AbstractMojo {
TinderJpaRestServerMojo mojo = new TinderJpaRestServerMojo();
mojo.packageBase = "ca.uhn.test";
mojo.resources =java.util.Collections.singletonList("patient");
mojo.baseResourceNames =java.util.Collections.singletonList("patient");
mojo.targetDirectory = new File("target/gen");
mojo.execute();
}

View File

@ -25,6 +25,16 @@ public abstract class BaseRootType extends BaseElement {
return mySearchParameters;
}
public List<SearchParameter> getSearchParametersWithoutComposite() {
ArrayList<SearchParameter> retVal = new ArrayList<SearchParameter>();
for(SearchParameter next:getSearchParameters()) {
if(!next.getType().equals("composite")) {
retVal.add(next);
}
}
return retVal;
}
@Override
public String getTypeSuffix() {
return "";

View File

@ -50,6 +50,7 @@ public abstract class BaseStructureParser {
private TreeSet<String> myImports = new TreeSet<String>();
private Map<String, String> myLocallyDefinedClassNames = new HashMap<String, String>();
private List<BaseRootType> myResources = new ArrayList<BaseRootType>();
private boolean myImportsResolved;
public void addResource(BaseRootType theResource) {
myResources.add(theResource);
@ -286,6 +287,7 @@ public abstract class BaseStructureParser {
ctx.put("resourceBlockChildren", theResource.getResourceBlockChildren());
ctx.put("childExtensionTypes", ObjectUtils.defaultIfNull(myExtensions, new ArrayList<Extension>()));
ctx.put("searchParams", (theResource.getSearchParameters()));
ctx.put("searchParamsWithoutComposite", (theResource.getSearchParametersWithoutComposite()));
VelocityEngine v = new VelocityEngine();
v.setProperty("resource.loader", "cp");
@ -319,9 +321,12 @@ public abstract class BaseStructureParser {
throw new MojoFailureException(theOutputDirectory + " is not a directory");
}
for (BaseRootType next : myResources) {
ourLog.info("Scanning resource for imports {}", next.getName());
scanForImportsNames(next);
if (!myImportsResolved) {
for (BaseRootType next : myResources) {
ourLog.info("Scanning resource for imports {}", next.getName());
scanForImportsNames(next);
}
myImportsResolved = true;
}
for (BaseRootType next : myResources) {
@ -330,7 +335,8 @@ public abstract class BaseStructureParser {
scanForTypeNameConflicts(next);
fixResourceReferenceClassNames(next, thePackageBase);
// File f = new File(theOutputDirectory, (next.getDeclaringClassNameComplete()) /*+ getFilenameSuffix()*/ + ".java");
// File f = new File(theOutputDirectory, (next.getDeclaringClassNameComplete()) /*+ getFilenameSuffix()*/ +
// ".java");
File f = new File(theOutputDirectory, (next.getElementName()) + getFilenameSuffix() + ".java");
try {
write(next, f, thePackageBase);
@ -348,9 +354,8 @@ public abstract class BaseStructureParser {
// }
/**
* Example: Encounter has an internal block class named "Location", but it
* also has a reference to the Location resource type, so we need to use the
* fully qualified name for that resource reference
* Example: Encounter has an internal block class named "Location", but it also has a reference to the Location
* resource type, so we need to use the fully qualified name for that resource reference
*/
private void fixResourceReferenceClassNames(BaseElement theNext, String thePackageBase) {
for (BaseElement next : theNext.getChildren()) {

View File

@ -23,8 +23,8 @@ public class ${className}ResourceProvider extends BaseJpaResourceProvider<${clas
}
@Search()
List<${className}> search(
#foreach ( $param in $searchParams ) #{if}(true) #{end}
public List<${className}> search(
#foreach ( $param in $searchParamsWithoutComposite ) #{if}(true) #{end}
@Description(shortDefinition="${param.description}")
@OptionalParam(name="${param.name}")
@ -40,12 +40,14 @@ public class ${className}ResourceProvider extends BaseJpaResourceProvider<${clas
QuantityDt the${param.nameCapitalized} #{if}($foreach.hasNext), #{end}
#elseif (${param.type} == 'reference' )
ReferenceParam the${param.nameCapitalized} #{if}($foreach.hasNext), #{end}
#elseif (${param.type} == 'composite' )
ReferenceParam the${param.nameCapitalized} #{if}($foreach.hasNext), #{end}
#end
#end
) {
SearchParameterMap paramMap = new SearchParameterMap();
#foreach ( $param in $searchParams )
#foreach ( $param in $searchParamsWithoutComposite )
paramMap.add("${param.name}", the${param.nameCapitalized});
#end

View File

@ -3,13 +3,13 @@ package ${packageBase};
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import ca.uhn.fhir.model.dstu.resource.*;
public class ${className}ResourceTable extends BaseResourceTable<${className}> {
import ca.uhn.fhir.jpa.entity.BaseResourceTable;
import ca.uhn.fhir.model.dstu.resource.*;
@Entity
@DiscriminatorValue("${className}")
public class PatientResourceTable extends BaseResourceTable<Patient> {
public class ${className}ResourceTable extends BaseResourceTable<${className}> {
@Override
public Class<${className}> getResourceType() {