diff --git a/.gitignore b/.gitignore index 2a64f04d425..3f1190ec6c7 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ Servers/ *.log* nohup.out .DS_Store +*.orig # Vagrant stuff. .vagrant diff --git a/examples/src/main/java/example/AuthorizingTesterUiClientFactory.java b/examples/src/main/java/example/AuthorizingTesterUiClientFactory.java new file mode 100644 index 00000000000..996d28f66d5 --- /dev/null +++ b/examples/src/main/java/example/AuthorizingTesterUiClientFactory.java @@ -0,0 +1,23 @@ +package example; + +import javax.servlet.http.HttpServletRequest; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.client.IGenericClient; +import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; +import ca.uhn.fhir.util.ITestingUiClientFactory; + +public class AuthorizingTesterUiClientFactory implements ITestingUiClientFactory { + + @Override + public IGenericClient newClient(FhirContext theFhirContext, HttpServletRequest theRequest, String theServerBaseUrl) { + // Create a client + IGenericClient client = theFhirContext.newRestfulGenericClient(theServerBaseUrl); + + // Register an interceptor which adds credentials + client.registerInterceptor(new BasicAuthInterceptor("someusername", "somepassword")); + + return client; + } + +} diff --git a/examples/src/main/java/example/ClientExamples.java b/examples/src/main/java/example/ClientExamples.java index fcbec0495f6..7631f07c514 100644 --- a/examples/src/main/java/example/ClientExamples.java +++ b/examples/src/main/java/example/ClientExamples.java @@ -33,6 +33,22 @@ public class ClientExamples { // END SNIPPET: proxy } + @SuppressWarnings("unused") + public void createTimeouts() { + // START SNIPPET: timeouts + FhirContext ctx = new FhirContext(); + + // Set how long to try and establish the initial TCP connection (in ms) + ctx.getRestfulClientFactory().setConnectTimeout(20 * 1000); + + // Set how long to block for individual read/write operations (in ms) + ctx.getRestfulClientFactory().setSocketTimeout(20 * 1000); + + // Create the client + IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir"); + // END SNIPPET: timeouts + } + @SuppressWarnings("unused") public void createSecurity() { // START SNIPPET: security diff --git a/examples/src/main/java/example/GenericClientExample.java b/examples/src/main/java/example/GenericClientExample.java index 1c0b4de6569..9b3ad44a95d 100644 --- a/examples/src/main/java/example/GenericClientExample.java +++ b/examples/src/main/java/example/GenericClientExample.java @@ -3,6 +3,8 @@ package example; import java.util.ArrayList; import java.util.List; +import org.hl7.fhir.instance.model.IBaseResource; + import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.IResource; @@ -74,30 +76,30 @@ public class GenericClientExample { // END SNIPPET: create } { - Patient patient = new Patient(); - // START SNIPPET: createConditional - // One form - MethodOutcome outcome = client.create() - .resource(patient) - .conditionalByUrl("Patient?identifier=system%7C00001") - .execute(); + Patient patient = new Patient(); + // START SNIPPET: createConditional + // One form + MethodOutcome outcome = client.create() + .resource(patient) + .conditionalByUrl("Patient?identifier=system%7C00001") + .execute(); - // Another form - MethodOutcome outcome2 = client.create() - .resource(patient) - .conditional() - .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001")) - .execute(); - - // This will return true if the server responded with an HTTP 201 created, - // otherwise it will return null. - Boolean created = outcome.getCreated(); - - // The ID of the created, or the pre-existing resource - IdDt id = outcome.getId(); - // END SNIPPET: createConditional + // Another form + MethodOutcome outcome2 = client.create() + .resource(patient) + .conditional() + .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001")) + .execute(); + + // This will return true if the server responded with an HTTP 201 created, + // otherwise it will return null. + Boolean created = outcome.getCreated(); + + // The ID of the created, or the pre-existing resource + IdDt id = outcome.getId(); + // END SNIPPET: createConditional } - { + { // START SNIPPET: update Patient patient = new Patient(); // ..populate the patient object.. @@ -125,21 +127,21 @@ public class GenericClientExample { // END SNIPPET: update } { - Patient patient = new Patient(); - // START SNIPPET: updateConditional - client.update() - .resource(patient) - .conditionalByUrl("Patient?identifier=system%7C00001") - .execute(); - - client.update() - .resource(patient) - .conditional() - .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001")) - .execute(); - // END SNIPPET: updateConditional + Patient patient = new Patient(); + // START SNIPPET: updateConditional + client.update() + .resource(patient) + .conditionalByUrl("Patient?identifier=system%7C00001") + .execute(); + + client.update() + .resource(patient) + .conditional() + .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001")) + .execute(); + // END SNIPPET: updateConditional } - { + { // START SNIPPET: etagupdate // First, let's retrive the latest version of a resource // from the server @@ -176,26 +178,26 @@ public class GenericClientExample { } { // START SNIPPET: delete - BaseOperationOutcome resp = client.delete().resourceById(new IdDt("Patient", "1234")).execute(); + BaseOperationOutcome resp = client.delete().resourceById(new IdDt("Patient", "1234")).execute(); // outcome may be null if the server didn't return one - if (resp != null) { - OperationOutcome outcome = (OperationOutcome) resp; + if (resp != null) { + OperationOutcome outcome = (OperationOutcome) resp; System.out.println(outcome.getIssueFirstRep().getDetailsElement().getValue()); } // END SNIPPET: delete } - { - // START SNIPPET: deleteConditional - client.delete() - .resourceConditionalByUrl("Patient?identifier=system%7C00001") - .execute(); - - client.delete() - .resourceConditionalByType("Patient") - .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001")) - .execute(); - // END SNIPPET: deleteConditional + { + // START SNIPPET: deleteConditional + client.delete() + .resourceConditionalByUrl("Patient?identifier=system%7C00001") + .execute(); + + client.delete() + .resourceConditionalByType("Patient") + .where(Patient.IDENTIFIER.exactly().systemAndIdentifier("system", "00001")) + .execute(); + // END SNIPPET: deleteConditional } { // START SNIPPET: search @@ -273,7 +275,7 @@ public class GenericClientExample { // .. populate this list - note that you can also pass in a populated // Bundle if you want to create one manually .. - List response = client.transaction().withResources(resources).execute(); + List response = client.transaction().withResources(resources).execute(); // END SNIPPET: transaction } diff --git a/examples/src/main/java/example/PagingPatientProvider.java b/examples/src/main/java/example/PagingPatientProvider.java index 665fd43652d..b106e9f1f28 100644 --- a/examples/src/main/java/example/PagingPatientProvider.java +++ b/examples/src/main/java/example/PagingPatientProvider.java @@ -2,6 +2,8 @@ package example; import java.util.List; +import org.hl7.fhir.instance.model.IBaseResource; + import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.InstantDt; @@ -43,7 +45,7 @@ public class PagingPatientProvider implements IResourceProvider { } @Override - public List getResources(int theFromIndex, int theToIndex) { + public List getResources(int theFromIndex, int theToIndex) { int end = Math.max(theToIndex, matchingResourceIds.size() - 1); List idsToReturn = matchingResourceIds.subList(theFromIndex, end); return loadResourcesByIds(idsToReturn); @@ -65,7 +67,7 @@ public class PagingPatientProvider implements IResourceProvider { /** * Load a list of patient resources given their IDs */ - private List loadResourcesByIds(List theIdsToReturn) { + private List loadResourcesByIds(List theIdsToReturn) { // .. implement this search against the database .. return null; } diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 5ec4621fa18..906337f3cd4 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -63,20 +63,22 @@ org.slf4j slf4j-android ${slf4j_version} - provided - - + + org.slf4j + slf4j-api + ${slf4j_version} + test + commons-io commons-io ${commons_io_version} + test - + javax.servlet javax.servlet-api @@ -135,11 +137,10 @@ true - + commons-codec:commons-codec - commons-io:commons-io ca.uhn.hapi.fhir:hapi-fhir-base ca.uhn.hapi.fhir:hapi-fhir-structures-dstu ca.uhn.hapi.fhir:hapi-fhir-structures-dstu2 @@ -148,7 +149,7 @@ javax.xml.stream:stax-api javax.servlet:javax.servlet-api org.codehaus.woodstox:stax2-api - + org.apache.commons:* org.apache.httpcomponents:* org.glassfish:javax.json @@ -179,7 +180,7 @@ - + DIST @@ -208,5 +209,5 @@ - + diff --git a/hapi-fhir-android/src/main/java/ca/uhn/fhir/android/AndroidLoader.java b/hapi-fhir-android/src/main/java/ca/uhn/fhir/android/AndroidLoader.java new file mode 100644 index 00000000000..f7e1b685601 --- /dev/null +++ b/hapi-fhir-android/src/main/java/ca/uhn/fhir/android/AndroidLoader.java @@ -0,0 +1,14 @@ +package ca.uhn.fhir.android; + +import ca.uhn.fhir.context.FhirContext; + +public class AndroidLoader { + + public static void main(String[] theArgs) { + FhirContext ctx = FhirContext.forDstu2(); + ctx.newJsonParser(); + ctx.newXmlParser(); + ctx.newRestfulGenericClient(""); + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java index 391be6b0157..afbf7c983be 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/FhirContext.java @@ -368,6 +368,13 @@ public class FhirContext { return new FhirTerser(this); } + /** + * Create a new validator instance. + *

+ * Note on thread safety: Validators are thread safe, you may use a single validator + * in multiple threads. (This is in contrast to parsers) + *

+ */ public FhirValidator newValidator() { return new FhirValidator(this); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java index c86d26bf293..57acdcd8d8c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/BaseElement.java @@ -91,7 +91,7 @@ public abstract class BaseElement implements IElement, ISupportsUndeclaredExtens if (myUndeclaredExtensions == null) { myUndeclaredExtensions = new ArrayList(); } - return Collections.unmodifiableList(myUndeclaredExtensions); + return (myUndeclaredExtensions); } @Override @@ -111,7 +111,7 @@ public abstract class BaseElement implements IElement, ISupportsUndeclaredExtens if (myUndeclaredModifierExtensions == null) { myUndeclaredModifierExtensions = new ArrayList(); } - return Collections.unmodifiableList(myUndeclaredModifierExtensions); + return (myUndeclaredModifierExtensions); } /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Bundle.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Bundle.java index a57b1f79c96..285307e5adc 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Bundle.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/Bundle.java @@ -293,6 +293,7 @@ public class Bundle extends BaseBundle /* implements IElement */{ return myLinkSelf; } + /* public InstantDt getPublished() { InstantDt retVal = (InstantDt) getResourceMetadata().get(ResourceMetadataKeyEnum.PUBLISHED); if (retVal == null) { @@ -301,6 +302,7 @@ public class Bundle extends BaseBundle /* implements IElement */{ } return retVal; } + */ /** * Retrieves a resource from a bundle given its logical ID. @@ -394,9 +396,11 @@ public class Bundle extends BaseBundle /* implements IElement */{ myCategories = theCategories; } + /* public void setPublished(InstantDt thePublished) { getResourceMetadata().put(ResourceMetadataKeyEnum.PUBLISHED, thePublished); } + /* public void setType(BoundCodeDt theType) { myType = theType; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ISupportsUndeclaredExtensions.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ISupportsUndeclaredExtensions.java index 1c34760264b..73817e4e2cb 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ISupportsUndeclaredExtensions.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/ISupportsUndeclaredExtensions.java @@ -27,22 +27,28 @@ import org.hl7.fhir.instance.model.api.IBaseDatatype; public interface ISupportsUndeclaredExtensions extends IElement { /** - * Returns a list containing all undeclared non-modifier extensions + * Returns a list containing all undeclared non-modifier extensions. The returned list + * is mutable, so it may be modified (e.g. to add or remove an extension). */ List getUndeclaredExtensions(); /** - * Returns a list containing all undeclared extensions (modifier and non-modifier) by extension URL + * Returns an immutable list containing all undeclared extensions (modifier and non-modifier) by extension URL + * + * @see #getUndeclaredExtensions() To return a mutable list which may be used to remove extensions */ List getUndeclaredExtensionsByUrl(String theUrl); /** - * Returns an immutable list containing all extensions (modifier and non-modifier) + * Returns an immutable list containing all extensions (modifier and non-modifier). + * + * @see #getUndeclaredExtensions() To return a mutable list which may be used to remove extensions */ List getAllUndeclaredExtensions(); /** - * Returns a list containing all undeclared modifier extensions + * Returns a list containing all undeclared modifier extensions. The returned list + * is mutable, so it may be modified (e.g. to add or remove an extension). */ List getUndeclaredModifierExtensions(); @@ -65,6 +71,8 @@ public interface ISupportsUndeclaredExtensions extends IElement { /** * Adds an extension to this object + * + * @see #getUndeclaredExtensions() To return a mutable list which may be used to remove extensions */ ExtensionDt addUndeclaredExtension(boolean theIsModifier, String theUrl, IBaseDatatype theValue); @@ -72,6 +80,8 @@ public interface ISupportsUndeclaredExtensions extends IElement { * Adds an extension to this object. This method is intended for use when * an extension is being added which will contain child extensions, as opposed to * a datatype. + * + * @see #getUndeclaredExtensions() To return a mutable list which may be used to remove extensions */ ExtensionDt addUndeclaredExtension(boolean theIsModifier, String theUrl); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java index ea3a7b463b0..bc44b064bf6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BaseDateTimeDt.java @@ -28,6 +28,7 @@ import static ca.uhn.fhir.model.api.TemporalPrecisionEnum.YEAR; import java.text.ParseException; import java.util.ArrayList; +import java.util.Calendar; import java.util.Collections; import java.util.Date; import java.util.GregorianCalendar; @@ -64,7 +65,9 @@ public abstract class BaseDateTimeDt extends BasePrimitive { private static final FastDateFormat ourYearMonthDayTimeZoneFormat = FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ssZZ"); private static final FastDateFormat ourYearMonthFormat = FastDateFormat.getInstance("yyyy-MM"); private static final FastDateFormat ourYearMonthNoDashesFormat = FastDateFormat.getInstance("yyyyMM"); - private static final Pattern ourYearMonthPattern = Pattern.compile("[0-9]{4}[0-9]{2}"); + private static final FastDateFormat ourHumanDateTimeFormat = FastDateFormat.getDateTimeInstance(FastDateFormat.MEDIUM, FastDateFormat.MEDIUM); + private static final FastDateFormat ourHumanDateFormat = FastDateFormat.getDateInstance(FastDateFormat.MEDIUM); + private static final Pattern ourYearMonthPattern = Pattern.compile("[0-9]{4}[0-9]{2}"); private static final Pattern ourYearPattern = Pattern.compile("[0-9]{4}"); static { @@ -89,7 +92,55 @@ public abstract class BaseDateTimeDt extends BasePrimitive { private boolean myTimeZoneZulu = false; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseDateTimeDt.class); - /** + /** + * Returns a human readable version of this date/time using the system local format. + *

+ * Note on time zones: This method renders the value using the time zone + * that is contained within the value. For example, if this date object contains the + * value "2012-01-05T12:00:00-08:00", the human display will be rendered as "12:00:00" + * even if the application is being executed on a system in a different time zone. If + * this behaviour is not what you want, use {@link #toHumanDisplayLocalTimezone()} + * instead. + *

+ */ + public String toHumanDisplay() { + TimeZone tz = getTimeZone(); + Calendar value = tz != null ? Calendar.getInstance(tz) : Calendar.getInstance(); + value.setTime(getValue()); + + switch (getPrecision()) { + case YEAR: + case MONTH: + case DAY: + return ourHumanDateFormat.format(value); + case MILLI: + case SECOND: + default: + return ourHumanDateTimeFormat.format(value); + } + } + + /** + * Returns a human readable version of this date/time using the system local format, + * converted to the local timezone if neccesary. + * + * @see #toHumanDisplay() for a method which does not convert the time to the local + * timezone before rendering it. + */ + public String toHumanDisplayLocalTimezone() { + switch (getPrecision()) { + case YEAR: + case MONTH: + case DAY: + return ourHumanDateFormat.format(getValue()); + case MILLI: + case SECOND: + default: + return ourHumanDateTimeFormat.format(getValue()); + } + } + + /** * Constructor */ public BaseDateTimeDt() { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/CodeDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/CodeDt.java index 089e1117df6..c006b57f9ec 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/CodeDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/CodeDt.java @@ -24,7 +24,6 @@ import static org.apache.commons.lang3.StringUtils.*; import ca.uhn.fhir.model.api.BasePrimitive; import ca.uhn.fhir.model.api.annotation.DatatypeDef; import ca.uhn.fhir.model.api.annotation.SimpleSetter; -import ca.uhn.fhir.parser.DataFormatException; @DatatypeDef(name = "code") public class CodeDt extends BasePrimitive implements ICodedDatatype, Comparable { @@ -44,6 +43,11 @@ public class CodeDt extends BasePrimitive implements ICodedDatatype, Com setValue(theCode); } + @Override + public boolean isEmpty() { + return super.isBaseEmpty() && isBlank(getValueAsString()); + } + @Override public int compareTo(CodeDt theCode) { if (theCode == null) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/XhtmlDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/XhtmlDt.java index 5ec2f7a4bb1..acd0aae6b95 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/XhtmlDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/XhtmlDt.java @@ -48,6 +48,11 @@ public class XhtmlDt extends BasePrimitive> { // nothing } + @Override + public boolean isEmpty() { + return super.isBaseEmpty() && (getValue() == null || getValue().isEmpty()); + } + /** * Constructor which accepts a string code * diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java index a5ac3b2a5ca..79c3a32dcec 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java @@ -95,19 +95,20 @@ import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.base.composite.BaseContainedDt; import ca.uhn.fhir.model.base.composite.BaseNarrativeDt; import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt; +import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.DecimalDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.IntegerDt; import ca.uhn.fhir.model.primitive.StringDt; +import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.XhtmlDt; import ca.uhn.fhir.narrative.INarrativeGenerator; import ca.uhn.fhir.util.ElementUtil; import ca.uhn.fhir.util.UrlUtil; /** - * This class is the FHIR JSON parser/encoder. Users should not interact with this class directly, but should use - * {@link FhirContext#newJsonParser()} to get an instance. + * This class is the FHIR JSON parser/encoder. Users should not interact with this class directly, but should use {@link FhirContext#newJsonParser()} to get an instance. */ public class JsonParser extends BaseParser implements IParser { @@ -196,7 +197,6 @@ public class JsonParser extends BaseParser implements IParser { writeTagWithTextNode(eventWriter, "title", theBundle.getTitle()); writeTagWithTextNode(eventWriter, "id", theBundle.getBundleId()); writeOptionalTagWithTextNode(eventWriter, "updated", theBundle.getUpdated()); - writeOptionalTagWithTextNode(eventWriter, "published", theBundle.getPublished()); boolean linkStarted = false; linkStarted = writeAtomLinkInDstu1Format(eventWriter, "self", theBundle.getLinkSelf(), linkStarted); @@ -353,7 +353,8 @@ public class JsonParser extends BaseParser implements IParser { theEventWriter.writeEnd(); } - private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theWriter, IBase theNextValue, BaseRuntimeElementDefinition theChildDef, String theChildName, boolean theIsSubElementWithinResource) throws IOException { + private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theWriter, IBase theNextValue, + BaseRuntimeElementDefinition theChildDef, String theChildName, boolean theContainedResource) throws IOException { switch (theChildDef.getChildType()) { case ID_DATATYPE: { @@ -414,7 +415,7 @@ public class JsonParser extends BaseParser implements IParser { if (theNextValue instanceof IBaseExtension) { theWriter.write("url", ((IBaseExtension) theNextValue).getUrl()); } - encodeCompositeElementToStreamWriter(theResDef, theResource, theNextValue, theWriter, childCompositeDef, theIsSubElementWithinResource); + encodeCompositeElementToStreamWriter(theResDef, theResource, theNextValue, theWriter, childCompositeDef, theContainedResource); theWriter.writeEnd(); break; } @@ -488,7 +489,8 @@ public class JsonParser extends BaseParser implements IParser { } - private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, List theChildren, boolean theIsSubElementWithinResource) throws IOException { + private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, + List theChildren, boolean theContainedResource) throws IOException { for (BaseRuntimeChildDefinition nextChild : theChildren) { if (nextChild.getElementName().equals("extension") || nextChild.getElementName().equals("modifierExtension")) { continue; @@ -503,19 +505,19 @@ public class JsonParser extends BaseParser implements IParser { if (gen != null) { BaseNarrativeDt narr = ((IResource) theResource).getText(); gen.generateNarrative(theResDef.getResourceProfile(), theResource, narr); - if (narr != null) { + if (narr != null && !narr.isEmpty()) { RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild; String childName = nextChild.getChildNameByDatatype(child.getDatatype()); BaseRuntimeElementDefinition type = child.getChildByName(childName); - encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, narr, type, childName, theIsSubElementWithinResource); + encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, narr, type, childName, theContainedResource); continue; } } } else if (nextChild instanceof RuntimeChildContainedResources) { - if (theIsSubElementWithinResource == false) { + if (theContainedResource == false) { String childName = nextChild.getValidChildNames().iterator().next(); BaseRuntimeElementDefinition child = nextChild.getChildByName(childName); - encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, null, child, childName, theIsSubElementWithinResource); + encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, null, child, childName, theContainedResource); } continue; } @@ -535,7 +537,7 @@ public class JsonParser extends BaseParser implements IParser { for (IBase nextValue : values) { if (nextValue == null || nextValue.isEmpty()) { if (nextValue instanceof BaseContainedDt) { - if (theIsSubElementWithinResource || getContainedResources().isEmpty()) { + if (theContainedResource || getContainedResources().isEmpty()) { continue; } } else { @@ -551,7 +553,7 @@ public class JsonParser extends BaseParser implements IParser { } boolean primitive = childDef.getChildType() == ChildTypeEnum.PRIMITIVE_DATATYPE; - if ((childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES||childDef.getChildType()==ChildTypeEnum.CONTAINED_RESOURCE_LIST) && theIsSubElementWithinResource) { + if (childDef.getChildType() == ChildTypeEnum.CONTAINED_RESOURCES && theContainedResource) { continue; } @@ -573,15 +575,15 @@ public class JsonParser extends BaseParser implements IParser { if (nextChild.getMax() > 1 || nextChild.getMax() == Child.MAX_UNLIMITED) { theEventWriter.writeStartArray(childName); inArray = true; - encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theIsSubElementWithinResource); - } else if (nextChild instanceof RuntimeChildNarrativeDefinition && theIsSubElementWithinResource) { + encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource); + } else if (nextChild instanceof RuntimeChildNarrativeDefinition && theContainedResource) { // suppress narratives from contained resources } else { - encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, childName, theIsSubElementWithinResource); + encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, childName, theContainedResource); } currentChildName = childName; } else { - encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theIsSubElementWithinResource); + encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource); } if (primitive) { @@ -655,13 +657,15 @@ public class JsonParser extends BaseParser implements IParser { } } - private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, BaseRuntimeElementCompositeDefinition resDef, boolean theIsSubElementWithinResource) throws IOException, DataFormatException { + private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonGenerator theEventWriter, + BaseRuntimeElementCompositeDefinition resDef, boolean theContainedResource) throws IOException, DataFormatException { extractAndWriteExtensionsAsDirectChild(theNextValue, theEventWriter, resDef, theResDef, theResource, null); - encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getExtensions(), theIsSubElementWithinResource); - encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getChildren(), theIsSubElementWithinResource); + encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getExtensions(), theContainedResource); + encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, resDef.getChildren(), theContainedResource); } - private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theContainedResource) throws IOException { + private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonGenerator theEventWriter, String theObjectNameOrNull, boolean theContainedResource) + throws IOException { String resourceId = null; if (theResource instanceof IResource) { IResource res = (IResource) theResource; @@ -674,7 +678,7 @@ public class JsonParser extends BaseParser implements IParser { } } else if (theResource instanceof IAnyResource) { IAnyResource res = (IAnyResource) theResource; - if (StringUtils.isNotBlank(res.getId().getIdPart())) { + if (theContainedResource && StringUtils.isNotBlank(res.getId().getIdPart())) { resourceId = res.getId().getIdPart(); } } @@ -702,20 +706,22 @@ public class JsonParser extends BaseParser implements IParser { if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1) && theResource instanceof IResource) { IResource resource = (IResource) theResource; + // Object securityLabelRawObj = + List securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS); + List profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES); + TagList tags = ResourceMetadataKeyEnum.TAG_LIST.get(resource); InstantDt updated = (InstantDt) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED); IdDt resourceId = resource.getId(); String versionIdPart = resourceId.getVersionIdPart(); if (isBlank(versionIdPart)) { versionIdPart = ResourceMetadataKeyEnum.VERSION.get(resource); } - List securityLabels = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.SECURITY_LABELS); - List profiles = extractMetadataListNotNull(resource, ResourceMetadataKeyEnum.PROFILES); - TagList tags = ResourceMetadataKeyEnum.TAG_LIST.get(resource); + if (ElementUtil.isEmpty(versionIdPart, updated, securityLabels, profiles) == false) { theEventWriter.writeStartObject("meta"); - writeOptionalTagWithTextNode(theEventWriter, "versionId", resource.getId().getVersionIdPart()); - writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", ResourceMetadataKeyEnum.UPDATED.get(resource)); + writeOptionalTagWithTextNode(theEventWriter, "versionId", versionIdPart); + writeOptionalTagWithTextNode(theEventWriter, "lastUpdated", updated); if (profiles != null && profiles.isEmpty() == false) { theEventWriter.writeStartArray("profile"); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java index 9e6fd5ef497..a2b83d03ab7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/ParserState.java @@ -683,8 +683,6 @@ class ParserState { public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException { if ("entry".equals(theLocalPart) && verifyNamespace(XmlParser.ATOM_NS, theNamespaceURI)) { push(new AtomEntryState(myInstance, myResourceType)); - } else if (theLocalPart.equals("published")) { - push(new AtomPrimitiveState(myInstance.getPublished())); } else if (theLocalPart.equals("title")) { push(new AtomPrimitiveState(myInstance.getTitle())); } else if ("id".equals(theLocalPart)) { @@ -1619,12 +1617,12 @@ class ParserState { return; } case RESOURCE: { - if (myInstance instanceof IResource) { + if (myInstance instanceof IResource || myInstance instanceof IElement) { ParserState.PreResourceStateHapi state = new PreResourceStateHapi(myInstance, child.getMutator(), null); push(state); } else { - ParserState.PreResourceStateHl7Org state = new PreResourceStateHl7Org(myInstance, child.getMutator(), null); - push(state); + ParserState.PreResourceStateHl7Org state = new PreResourceStateHl7Org(myInstance, child.getMutator(), null); + push(state); } return; } @@ -1732,7 +1730,7 @@ class ParserState { private class SecurityLabelElementStateHapi extends ElementCompositeState { - public SecurityLabelElementStateHapi(ParserState.PreResourceState thePreResourceState,BaseRuntimeElementCompositeDefinition theDef, BaseCodingDt codingDt) { + public SecurityLabelElementStateHapi(ParserState.PreResourceState thePreResourceState, BaseRuntimeElementCompositeDefinition theDef, BaseCodingDt codingDt) { super(thePreResourceState, theDef, codingDt); } @@ -1774,7 +1772,7 @@ class ParserState { securityLabels = new ArrayList(); myMap.put(ResourceMetadataKeyEnum.SECURITY_LABELS, securityLabels); } - BaseCodingDt securityLabel= myContext.getVersion().newCodingDt(); + BaseCodingDt securityLabel = myContext.getVersion().newCodingDt(); BaseRuntimeElementCompositeDefinition codinfDef = (BaseRuntimeElementCompositeDefinition) myContext.getElementDefinition(securityLabel.getClass()); push(new SecurityLabelElementStateHapi(getPreResourceState(), codinfDef, securityLabel)); securityLabels.add(securityLabel); @@ -1900,7 +1898,7 @@ class ParserState { @Override public void wereBack() { super.wereBack(); - if (myEntry == null) { + if (myEntry == null && myMutator == null) { myObject = (T) getCurrentElement(); } @@ -2068,7 +2066,7 @@ class ParserState { terser.visit(myInstance, new IModelVisitor() { @Override - public void acceptElement(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition) { + public void acceptElement(IBase theElement, List thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition) { if (theElement instanceof BaseResourceReferenceDt) { BaseResourceReferenceDt nextRef = (BaseResourceReferenceDt) theElement; String ref = nextRef.getReference().getValue(); @@ -2099,8 +2097,8 @@ class ParserState { } @Override - public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition, ExtensionDt theNextExt) { - acceptElement(theNextExt.getValue(), null, null); + public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, List thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition, ExtensionDt theNextExt) { + acceptElement(theNextExt.getValue(), null, null, null); } }); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java index e513f6035f9..7f304958395 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java @@ -271,7 +271,6 @@ public class XmlParser extends BaseParser implements IParser { } writeOptionalTagWithTextNode(eventWriter, "updated", theBundle.getUpdated()); - writeOptionalTagWithTextNode(eventWriter, "published", theBundle.getPublished()); if (StringUtils.isNotBlank(theBundle.getAuthorName().getValue())) { eventWriter.writeStartElement("author"); @@ -804,6 +803,7 @@ public class XmlParser extends BaseParser implements IParser { if (updated != null) { writeOptionalTagWithValue(theEventWriter, "lastUpdated", updated.getValueAsString()); } + for (IdDt profile : profiles) { theEventWriter.writeStartElement("profile"); theEventWriter.writeAttribute("value", profile.getValue()); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java index 63e58755f95..70cf63a0f84 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/MethodOutcome.java @@ -135,7 +135,7 @@ public class MethodOutcome { } /** - * This will be set to {@link Boolean#TRUE} for instance of MethodOutcome which are + * This will be set to {@link Boolean#TRUE} for instance of MethodOutcome which are * returned to client instances, if the server has responded with an HTTP 201 Created. */ public Boolean getCreated() { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java index 1dd36b03c4b..a7b20c73c5d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/BaseClient.java @@ -153,10 +153,14 @@ public abstract class BaseClient implements IRestfulClient { return invokeClient(theContext, binding, clientInvocation, null, null, theLogRequestAndResponse); } + void forceConformanceCheck() { + myFactory.validateServerBase(myUrlBase, myClient, this); + } + T invokeClient(FhirContext theContext, IClientResponseHandler binding, BaseHttpClientInvocation clientInvocation, EncodingEnum theEncoding, Boolean thePrettyPrint, boolean theLogRequestAndResponse) { if (!myDontValidateConformance) { - myFactory.validateServerBaseIfConfiguredToDoSo(myUrlBase, myClient); + myFactory.validateServerBaseIfConfiguredToDoSo(myUrlBase, myClient, this); } // TODO: handle non 2xx status codes by throwing the correct exception, @@ -369,7 +373,7 @@ public abstract class BaseClient implements IRestfulClient { } /** - * This method is an internal part of the HAPI API andmay change, use with caution. If you + * This method is an internal part of the HAPI API and may change, use with caution. If you * want to disable the loading of conformance statements, use {@link IRestfulClientFactory#setServerValidationModeEnum(ServerValidationModeEnum)} */ public void setDontValidateConformance(boolean theDontValidateConformance) { @@ -441,4 +445,8 @@ public abstract class BaseClient implements IRestfulClient { return reader; } + public List getInterceptors() { + return Collections.unmodifiableList(myInterceptors); + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java index c2a595f64b6..aa43b5def18 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/GenericClient.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.http.client.HttpClient; @@ -144,7 +145,6 @@ public class GenericClient extends BaseClient implements IGenericClient { super(theHttpClient, theServerBase, theFactory); myContext = theContext; } - @Override public BaseConformance conformance() { @@ -165,6 +165,11 @@ public class GenericClient extends BaseClient implements IGenericClient { return resp; } + @Override + public void forceConformanceCheck() { + super.forceConformanceCheck(); + } + @Override public ICreate create() { return new CreateInternal(); @@ -590,23 +595,14 @@ public class GenericClient extends BaseClient implements IGenericClient { } protected IBaseResource parseResourceBody(String theResourceBody) { - EncodingEnum encoding = null; - for (int i = 0; i < theResourceBody.length() && encoding == null; i++) { - switch (theResourceBody.charAt(i)) { - case '<': - encoding = EncodingEnum.XML; - break; - case '{': - encoding = EncodingEnum.JSON; - break; - } - } + EncodingEnum encoding = MethodUtil.detectEncodingNoDefault(theResourceBody); if (encoding == null) { throw new InvalidRequestException("FHIR client can't determine resource encoding"); } return encoding.newParser(myContext).parseResource(theResourceBody); } + @SuppressWarnings("unchecked") @Override public T prettyPrint() { @@ -615,7 +611,7 @@ public class GenericClient extends BaseClient implements IGenericClient { } } - + private final class BundleResponseHandler implements IClientResponseHandler { private Class myType; @@ -636,6 +632,15 @@ public class GenericClient extends BaseClient implements IGenericClient { } } + private final class StringResponseHandler implements IClientResponseHandler { + + @Override + public String invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws IOException, + BaseServerResponseException { + return IOUtils.toString(theResponseReader); + } + } + private class CreateInternal extends BaseClientExecutable implements ICreate, ICreateTyped, ICreateWithQuery, ICreateWithQueryTyped { private CriterionList myCriterionList; @@ -1574,14 +1579,16 @@ public class GenericClient extends BaseClient implements IGenericClient { private final class TransactionExecutable extends BaseClientExecutable, T> implements ITransactionTyped { private Bundle myBundle; - private List myResources; + private List myResources; private IBaseBundle myBaseBundle; + private String myRawBundle; + private EncodingEnum myRawBundleEncoding; public TransactionExecutable(Bundle theResources) { myBundle = theResources; } - public TransactionExecutable(List theResources) { + public TransactionExecutable(List theResources) { myResources = theResources; } @@ -1589,6 +1596,14 @@ public class GenericClient extends BaseClient implements IGenericClient { myBaseBundle = theBundle; } + public TransactionExecutable(String theBundle) { + myRawBundle = theBundle; + myRawBundleEncoding = MethodUtil.detectEncodingNoDefault(myRawBundle); + if (myRawBundleEncoding == null) { + throw new IllegalArgumentException("Can not determine encoding of raw resource body"); + } + } + @SuppressWarnings({ "unchecked", "rawtypes" }) @Override public T execute() { @@ -1601,6 +1616,19 @@ public class GenericClient extends BaseClient implements IGenericClient { ResourceResponseHandler binding = new ResourceResponseHandler(myBaseBundle.getClass(), null); BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myBaseBundle, myContext); return (T) invoke(params, binding, invocation); + } else if (myRawBundle != null) { + StringResponseHandler binding = new StringResponseHandler(); + /* + * If the user has explicitly requested a given encoding, we may need to reencode the raw string + */ + if (getParamEncoding() != null) { + if (MethodUtil.detectEncodingNoDefault(myRawBundle) != getParamEncoding()) { + IBaseResource parsed = parseResourceBody(myRawBundle); + myRawBundle = getParamEncoding().newParser(getFhirContext()).encodeResourceToString(parsed); + } + } + BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myRawBundle, myContext); + return (T) invoke(params, binding, invocation); } else { BundleResponseHandler binding = new BundleResponseHandler(null); BaseHttpClientInvocation invocation = TransactionMethodBinding.createTransactionInvocation(myBundle, myContext); @@ -1619,7 +1647,7 @@ public class GenericClient extends BaseClient implements IGenericClient { } @Override - public ITransactionTyped> withResources(List theResources) { + public ITransactionTyped> withResources(List theResources) { Validate.notNull(theResources, "theResources must not be null"); return new TransactionExecutable>(theResources); } @@ -1630,6 +1658,12 @@ public class GenericClient extends BaseClient implements IGenericClient { return new TransactionExecutable(theBundle); } + @Override + public ITransactionTyped withBundle(String theBundle) { + Validate.notBlank(theBundle, "theBundle must not be null"); + return new TransactionExecutable(theBundle); + } + } private class UpdateInternal extends BaseClientExecutable implements IUpdate, IUpdateTyped, IUpdateExecutable, IUpdateWithQuery, IUpdateWithQueryTyped { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java index ae7f01030b3..996e6f7dfb3 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IGenericClient.java @@ -34,6 +34,8 @@ import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.api.IRestfulClient; +import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException; +import ca.uhn.fhir.rest.client.exceptions.FhirClientInnapropriateForServerException; import ca.uhn.fhir.rest.gclient.ICreate; import ca.uhn.fhir.rest.gclient.IDelete; import ca.uhn.fhir.rest.gclient.IFetchConformanceUntyped; @@ -109,6 +111,16 @@ public interface IGenericClient extends IRestfulClient { @Deprecated MethodOutcome delete(Class theType, String theId); + /** + * Force the client to fetch the server's conformance statement and validate that it is appropriate for this client. + * + * @throws FhirClientConnectionException + * if the conformance statement cannot be read, or if the client + * @throws FhirClientInnapropriateForServerException + * If the conformance statement indicates that the server is inappropriate for this client (e.g. it implements the wrong version of FHIR) + */ + void forceConformanceCheck() throws FhirClientConnectionException; + /** * Fluent method for the "get tags" operation */ @@ -123,17 +135,15 @@ public interface IGenericClient extends IRestfulClient { * Implementation of the "history instance" method. * * @param theType - * The type of resource to return the history for, or - * null to search for history across all resources + * The type of resource to return the history for, or null to search for history across all resources * @param theId - * The ID of the resource to return the history for, or null to search for all resource - * instances. Note that if this param is not null, theType must also not be null + * The ID of the resource to return the history for, or null to search for all resource instances. Note that if this param is not null, theType must also not + * be null * @param theSince * If not null, request that the server only return resources updated since this time * @param theLimit - * If not null, request that the server return no more than this number of resources. Note that the - * server may return less even if more are available, but should not return more according to the FHIR - * specification. + * If not null, request that the server return no more than this number of resources. Note that the server may return less even if more are available, but should not return more + * according to the FHIR specification. * @return A bundle containing returned resources * @deprecated As of 0.9, use the fluent {@link #history()} method instead */ @@ -144,49 +154,46 @@ public interface IGenericClient extends IRestfulClient { * Implementation of the "history instance" method. * * @param theType - * The type of resource to return the history for, or - * null to search for history across all resources + * The type of resource to return the history for, or null to search for history across all resources * @param theId - * The ID of the resource to return the history for, or null to search for all resource - * instances. Note that if this param is not null, theType must also not be null + * The ID of the resource to return the history for, or null to search for all resource instances. Note that if this param is not null, theType must also not + * be null * @param theSince * If not null, request that the server only return resources updated since this time * @param theLimit - * If not null, request that the server return no more than this number of resources. Note that the - * server may return less even if more are available, but should not return more according to the FHIR - * specification. + * If not null, request that the server return no more than this number of resources. Note that the server may return less even if more are available, but should not return more + * according to the FHIR specification. * @return A bundle containing returned resources * @deprecated As of 0.9, use the fluent {@link #history()} method instead */ @Deprecated Bundle history(Class theType, String theId, DateTimeDt theSince, Integer theLimit); + // /** + // * Implementation of the "instance read" method. This method will only ever do a "read" for the latest version of a + // * given resource instance, even if the ID passed in contains a version. If you wish to request a specific version + // * of a resource (the "vread" operation), use {@link #vread(Class, IdDt)} instead. + // *

+ // * Note that if an absolute resource ID is passed in (i.e. a URL containing a protocol and host as well as the + // * resource type and ID) the server base for the client will be ignored, and the URL passed in will be queried. + // *

+ // * + // * @param theType + // * The type of resource to load + // * @param theId + // * The ID to load, including the resource ID and the resource version ID. Valid values include + // * "Patient/123/_history/222", or "http://example.com/fhir/Patient/123/_history/222" + // * @return The resource + // */ + // T read(Class theType, IdDt theId); + /** - * Loads the previous/next bundle of resources from a paged set, using the link specified in the "link type=next" - * tag within the atom bundle. + * Loads the previous/next bundle of resources from a paged set, using the link specified in the "link type=next" tag within the atom bundle. * * @see Bundle#getLinkNext() */ IGetPage loadPage(); -// /** -// * Implementation of the "instance read" method. This method will only ever do a "read" for the latest version of a -// * given resource instance, even if the ID passed in contains a version. If you wish to request a specific version -// * of a resource (the "vread" operation), use {@link #vread(Class, IdDt)} instead. -// *

-// * Note that if an absolute resource ID is passed in (i.e. a URL containing a protocol and host as well as the -// * resource type and ID) the server base for the client will be ignored, and the URL passed in will be queried. -// *

-// * -// * @param theType -// * The type of resource to load -// * @param theId -// * The ID to load, including the resource ID and the resource version ID. Valid values include -// * "Patient/123/_history/222", or "http://example.com/fhir/Patient/123/_history/222" -// * @return The resource -// */ -// T read(Class theType, IdDt theId); - /** * Implementation of the FHIR "extended operations" action */ @@ -229,8 +236,7 @@ public interface IGenericClient extends IRestfulClient { IBaseResource read(UriDt theUrl); /** - * Register a new interceptor for this client. An interceptor can be used to add additional logging, or add security - * headers, or pre-process responses, etc. + * Register a new interceptor for this client. An interceptor can be used to add additional logging, or add security headers, or pre-process responses, etc. */ void registerInterceptor(IClientInterceptor theInterceptor); @@ -259,8 +265,8 @@ public interface IGenericClient extends IRestfulClient { Bundle search(UriDt theUrl); /** - * If set to true, the client will log all requests and all responses. This is probably not a good - * production setting since it will result in a lot of extra logging, but it can be useful for troubleshooting. + * If set to true, the client will log all requests and all responses. This is probably not a good production setting since it will result in a lot of extra logging, but it can be + * useful for troubleshooting. * * @param theLogRequestAndResponse * Should requests and responses be logged @@ -277,8 +283,7 @@ public interface IGenericClient extends IRestfulClient { * * @param theResources * The resources to create/update in a single transaction - * @return A list of resource stubs (these will not be fully populated) containing IDs and other - * {@link IResource#getResourceMetadata() metadata} + * @return A list of resource stubs (these will not be fully populated) containing IDs and other {@link IResource#getResourceMetadata() metadata} * @deprecated Use {@link #transaction()} * */ @@ -286,8 +291,7 @@ public interface IGenericClient extends IRestfulClient { List transaction(List theResources); /** - * Remove an intercaptor that was previously registered using - * {@link IRestfulClient#registerInterceptor(IClientInterceptor)} + * Remove an intercaptor that was previously registered using {@link IRestfulClient#registerInterceptor(IClientInterceptor)} */ void unregisterInterceptor(IClientInterceptor theInterceptor); @@ -328,18 +332,16 @@ public interface IGenericClient extends IRestfulClient { MethodOutcome validate(IResource theResource); /** - * Implementation of the "instance vread" method. Note that this method expects theId to contain a - * resource ID as well as a version ID, and will fail if it does not. + * Implementation of the "instance vread" method. Note that this method expects theId to contain a resource ID as well as a version ID, and will fail if it does not. *

- * Note that if an absolute resource ID is passed in (i.e. a URL containing a protocol and host as well as the - * resource type and ID) the server base for the client will be ignored, and the URL passed in will be queried. + * Note that if an absolute resource ID is passed in (i.e. a URL containing a protocol and host as well as the resource type and ID) the server base for the client will be ignored, and the URL + * passed in will be queried. *

* * @param theType * The type of resource to load * @param theId - * The ID to load, including the resource ID and the resource version ID. Valid values include - * "Patient/123/_history/222", or "http://example.com/fhir/Patient/123/_history/222" + * The ID to load, including the resource ID and the resource version ID. Valid values include "Patient/123/_history/222", or "http://example.com/fhir/Patient/123/_history/222" * @return The resource */ T vread(Class theType, IdDt theId); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IRestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IRestfulClientFactory.java index 35bb74541a0..6af4fed94a9 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IRestfulClientFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/IRestfulClientFactory.java @@ -72,6 +72,12 @@ public interface IRestfulClientFactory { */ HttpClient getHttpClient(); + /** + * @deprecated Use {@link #getServerValidationMode()} instead + */ + @Deprecated + ServerValidationModeEnum getServerValidationModeEnum(); + /** * Gets the server validation mode for any clients created from this factory. Server * validation involves the client requesting the server's conformance statement @@ -79,8 +85,10 @@ public interface IRestfulClientFactory { *

* The default value for this setting is defined by {@link #DEFAULT_SERVER_VALIDATION_MODE} *

+ * + * @since 1.0 */ - ServerValidationModeEnum getServerValidationModeEnum(); + ServerValidationModeEnum getServerValidationMode(); /** * Gets the socket timeout, in milliseconds. This is the SO_TIMEOUT time, which is the amount of time that a @@ -158,6 +166,12 @@ public interface IRestfulClientFactory { */ void setProxyCredentials(String theUsername, String thePassword); + /** + * @deprecated Use {@link #setServerValidationMode(ServerValidationModeEnum)} instead. This method was incorrectly named. + */ + @Deprecated + void setServerValidationModeEnum(ServerValidationModeEnum theServerValidationMode); + /** * Sets the server validation mode for any clients created from this factory. Server * validation involves the client requesting the server's conformance statement @@ -165,8 +179,10 @@ public interface IRestfulClientFactory { *

* The default value for this setting is defined by {@link #DEFAULT_SERVER_VALIDATION_MODE} *

+ * + * @since 1.0 */ - void setServerValidationModeEnum(ServerValidationModeEnum theServerValidationMode); + void setServerValidationMode(ServerValidationModeEnum theServerValidationMode); /** * Sets the socket timeout, in milliseconds. This is the SO_TIMEOUT time, which is the amount of time that a diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java index 021816d07a9..d113e2f16ec 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/RestfulClientFactory.java @@ -51,6 +51,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.rest.client.api.IRestfulClient; import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException; +import ca.uhn.fhir.rest.client.exceptions.FhirClientInnapropriateForServerException; import ca.uhn.fhir.rest.method.BaseMethodBinding; import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.util.FhirTerser; @@ -137,7 +138,7 @@ public class RestfulClientFactory implements IRestfulClientFactory { } @Override - public ServerValidationModeEnum getServerValidationModeEnum() { + public ServerValidationModeEnum getServerValidationMode() { return myServerValidationMode; } @@ -195,25 +196,29 @@ public class RestfulClientFactory implements IRestfulClientFactory { /** * This method is internal to HAPI - It may change in future versions, use with caution. */ - public void validateServerBaseIfConfiguredToDoSo(String theServerBase, HttpClient theHttpClient) { - String serverBase = theServerBase; - if (!serverBase.endsWith("/")) { - serverBase = serverBase + "/"; - } + public void validateServerBaseIfConfiguredToDoSo(String theServerBase, HttpClient theHttpClient, BaseClient theClient) { + String serverBase = normalizeBaseUrlForMap(theServerBase); switch (myServerValidationMode) { case NEVER: break; case ONCE: if (!myValidatedServerBaseUrls.contains(serverBase)) { - validateServerBase(serverBase, theHttpClient); - myValidatedServerBaseUrls.add(serverBase); + validateServerBase(serverBase, theHttpClient, theClient); } break; } } + private String normalizeBaseUrlForMap(String theServerBase) { + String serverBase = theServerBase; + if (!serverBase.endsWith("/")) { + serverBase = serverBase + "/"; + } + return serverBase; + } + @Override public synchronized void setConnectionRequestTimeout(int theConnectionRequestTimeout) { myConnectionRequestTimeout = theConnectionRequestTimeout; @@ -258,7 +263,7 @@ public class RestfulClientFactory implements IRestfulClientFactory { } @Override - public void setServerValidationModeEnum(ServerValidationModeEnum theServerValidationMode) { + public void setServerValidationMode(ServerValidationModeEnum theServerValidationMode) { Validate.notNull(theServerValidationMode, "theServerValidationMode may not be null"); myServerValidationMode = theServerValidationMode; } @@ -269,10 +274,12 @@ public class RestfulClientFactory implements IRestfulClientFactory { myHttpClient = null; } - @SuppressWarnings("unchecked") - private void validateServerBase(String theServerBase, HttpClient theHttpClient) { + void validateServerBase(String theServerBase, HttpClient theHttpClient, BaseClient theClient) { GenericClient client = new GenericClient(myContext, theHttpClient, theServerBase, this); + for (IClientInterceptor interceptor : theClient.getInterceptors()) { + client.registerInterceptor(interceptor); + } client.setDontValidateConformance(true); IBaseResource conformance; @@ -296,7 +303,9 @@ public class RestfulClientFactory implements IRestfulClientFactory { } else { if (serverFhirVersionString.startsWith("0.80") || serverFhirVersionString.startsWith("0.0.8")) { serverFhirVersionEnum = FhirVersionEnum.DSTU1; - } else if (serverFhirVersionString.startsWith("0.4") || serverFhirVersionString.startsWith("0.5")) { + } else if (serverFhirVersionString.startsWith("0.4")) { + serverFhirVersionEnum = FhirVersionEnum.DSTU2; + } else if (serverFhirVersionString.startsWith("0.5")) { serverFhirVersionEnum = FhirVersionEnum.DSTU2; } else { // we'll be lenient and accept this @@ -307,9 +316,22 @@ public class RestfulClientFactory implements IRestfulClientFactory { if (serverFhirVersionEnum != null) { FhirVersionEnum contextFhirVersion = myContext.getVersion().getVersion(); if (!contextFhirVersion.isEquivalentTo(serverFhirVersionEnum)) { - throw new FhirClientConnectionException(myContext.getLocalizer().getMessage(RestfulClientFactory.class, "wrongVersionInConformance", theServerBase + Constants.URL_TOKEN_METADATA, serverFhirVersionString, serverFhirVersionEnum, contextFhirVersion)); + throw new FhirClientInnapropriateForServerException(myContext.getLocalizer().getMessage(RestfulClientFactory.class, "wrongVersionInConformance", theServerBase + Constants.URL_TOKEN_METADATA, serverFhirVersionString, serverFhirVersionEnum, contextFhirVersion)); } } + + myValidatedServerBaseUrls.add(normalizeBaseUrlForMap(theServerBase)); + + } + + @Override + public ServerValidationModeEnum getServerValidationModeEnum() { + return getServerValidationMode(); + } + + @Override + public void setServerValidationModeEnum(ServerValidationModeEnum theServerValidationMode) { + setServerValidationMode(theServerValidationMode); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/FhirClientInnapropriateForServerException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/FhirClientInnapropriateForServerException.java new file mode 100644 index 00000000000..1e59d6dc0d9 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/client/exceptions/FhirClientInnapropriateForServerException.java @@ -0,0 +1,46 @@ +package ca.uhn.fhir.rest.client.exceptions; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2015 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; + +/** + * This exception will be thrown by FHIR clients if the client attempts to + * communicate with a server which is a valid FHIR server but is incompatible + * with this client for some reason. + */ +public class FhirClientInnapropriateForServerException extends BaseServerResponseException { + + private static final long serialVersionUID = 1L; + + public FhirClientInnapropriateForServerException(Throwable theCause) { + super(0, theCause); + } + + public FhirClientInnapropriateForServerException(String theMessage, Throwable theCause) { + super(0, theMessage, theCause); + } + + public FhirClientInnapropriateForServerException(String theMessage) { + super(0, theMessage); + } + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ITransaction.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ITransaction.java index cbed783919a..584230acd39 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ITransaction.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ITransaction.java @@ -32,7 +32,7 @@ public interface ITransaction { /** * Use a list of resources as the transaction input */ - ITransactionTyped> withResources(List theResources); + ITransactionTyped> withResources(List theResources); /** * Use a DSTU1 Bundle (Atom) as the transaction input @@ -44,9 +44,9 @@ public interface ITransaction { */ ITransactionTyped withBundle(T theBundleResource); - // ***** - // TODO: add withString version - // If we add a withString version, make sure to auto-detect content type! - // ***** - + /** + * Use the given raw text (should be a Bundle resource) as the transaction input + */ + ITransactionTyped withBundle(String theBundle); + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java index 475709fdf1e..f0b4c16e4c0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseHttpClientInvocationWithContents.java @@ -37,6 +37,7 @@ import org.hl7.fhir.instance.model.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseBinary; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.valueset.BundleTypeEnum; @@ -47,6 +48,7 @@ import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.validation.FhirValidator; /** * @author James Agnew @@ -63,7 +65,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca private String myIfNoneExistString; private Map> myParams; private final IBaseResource myResource; - private final List myResources; + private final List myResources; private final TagList myTagList; private final String myUrlPath; @@ -103,7 +105,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca myBundleType = null; } - public BaseHttpClientInvocationWithContents(FhirContext theContext, List theResources, BundleTypeEnum theBundleType) { + public BaseHttpClientInvocationWithContents(FhirContext theContext, List theResources, BundleTypeEnum theBundleType) { myContext = theContext; myResource = null; myTagList = null; @@ -270,7 +272,7 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca } } else if (myContents != null) { contents = myContents; - if (myContentsIsBundle) { + if (myContentsIsBundle && myContext.getVersion().getVersion().equals(FhirVersionEnum.DSTU1)) { contentType = encoding.getBundleContentType(); } else { contentType = encoding.getResourceContentType(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java index 5013174f13d..7e157aed3ac 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseMethodBinding.java @@ -173,6 +173,25 @@ public abstract class BaseMethodBinding implements IClientResponseHandler */ public abstract String getResourceName(); + /** + * Returns the value of {@link #getResourceOperationType()} or {@link #getSystemOperationType()} or {@link #getOtherOperationType()} + */ + public String getResourceOrSystemOperationType() { + Enum retVal = getResourceOperationType(); + if (retVal != null) { + return retVal.name(); + } + retVal = getSystemOperationType(); + if (retVal != null) { + return retVal.name(); + } + retVal = getOtherOperationType(); + if (retVal != null) { + return retVal.name(); + } + return null; + } + public abstract RestfulOperationTypeEnum getResourceOperationType(); public abstract RestfulOperationSystemEnum getSystemOperationType(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java index 73a1db2f730..15631b9e6f3 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/BaseOutcomeReturningMethodBinding.java @@ -20,9 +20,12 @@ package ca.uhn.fhir.rest.method; * #L% */ +import static org.apache.commons.lang3.StringUtils.isBlank; + import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; +import java.io.StringReader; import java.io.Writer; import java.lang.reflect.Method; import java.util.Enumeration; @@ -32,6 +35,7 @@ import java.util.Set; import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.IBaseResource; @@ -53,6 +57,7 @@ import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServerUtils; import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding { @@ -65,7 +70,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding> theHeaders) throws IOException, BaseServerResponseException { + public MethodOutcome invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map> theHeaders) throws IOException, + BaseServerResponseException { switch (theResponseStatusCode) { case Constants.STATUS_HTTP_200_OK: case Constants.STATUS_HTTP_201_CREATED: @@ -198,7 +204,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding wantedResourceType = requestContainsResourceType(); IBaseResource retVal; @@ -305,7 +342,8 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding includes = getRequestIncludesFromParams(params); @@ -312,7 +312,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding theResources, BundleTypeEnum theBundleType) { + public HttpPostClientInvocation(FhirContext theContext, List theResources, BundleTypeEnum theBundleType) { super(theContext, theResources, theBundleType); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java index ecbcc898831..a78eceab910 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/MethodUtil.java @@ -249,15 +249,26 @@ public class MethodUtil { } public static EncodingEnum detectEncoding(String theBody) { - for (int i = 0; i < theBody.length(); i++) { + EncodingEnum retVal = detectEncodingNoDefault(theBody); + if (retVal == null) { + retVal = EncodingEnum.XML; + } + return retVal; + } + + public static EncodingEnum detectEncodingNoDefault(String theBody) { + EncodingEnum retVal = null; + for (int i = 0; i < theBody.length() && retVal == null; i++) { switch (theBody.charAt(i)) { - case '<': - return EncodingEnum.XML; - case '{': - return EncodingEnum.JSON; + case '<': + retVal = EncodingEnum.XML; + break; + case '{': + retVal = EncodingEnum.JSON; + break; } } - return EncodingEnum.XML; + return retVal; } public static void extractDescription(SearchParameter theParameter, Annotation[] theAnnotations) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java index 5240981738e..dd84b803b3d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java @@ -132,6 +132,11 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { return RestfulOperationTypeEnum.SEARCH_TYPE; } + @Override + protected BundleTypeEnum getResponseBundleType() { + return BundleTypeEnum.SEARCHSET; + } + @Override public ReturnTypeEnum getReturnType() { return ReturnTypeEnum.BUNDLE; @@ -289,13 +294,9 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { } - public void setResourceType(Class resourceType) { - this.myDeclaredResourceType = resourceType; - } - @Override - public String toString() { - return getMethod().toString(); + protected boolean isAddContentLocationHeader() { + return false; } private List processWhitelistAndBlacklist(List theQualifiedNames, Set theQualifierWhitelist, Set theQualifierBlacklist) { @@ -313,6 +314,15 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { return retVal; } + public void setResourceType(Class resourceType) { + this.myDeclaredResourceType = resourceType; + } + + @Override + public String toString() { + return getMethod().toString(); + } + public static BaseHttpClientInvocation createSearchInvocation(FhirContext theContext, String theResourceName, Map> theParameters, IdDt theId, String theCompartmentName, SearchStyleEnum theSearchStyle) { SearchStyleEnum searchStyle = theSearchStyle; @@ -473,9 +483,4 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding { } - @Override - protected BundleTypeEnum getResponseBundleType() { - return BundleTypeEnum.SEARCHSET; - } - } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java index 83fa8dfe9fa..6f08e795d0c 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/TransactionMethodBinding.java @@ -195,8 +195,12 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding return new HttpPostClientInvocation(theContext, theBundle); } - public static BaseHttpClientInvocation createTransactionInvocation(List theResources, FhirContext theContext) { + public static BaseHttpClientInvocation createTransactionInvocation(List theResources, FhirContext theContext) { return new HttpPostClientInvocation(theContext, theResources, BundleTypeEnum.TRANSACTION); } + public static BaseHttpClientInvocation createTransactionInvocation(String theRawBundle, FhirContext theContext) { + return new HttpPostClientInvocation(theContext, theRawBundle, true, ""); + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Dstu1BundleFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Dstu1BundleFactory.java index f27e6c5bc39..b27618874a9 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Dstu1BundleFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Dstu1BundleFactory.java @@ -203,8 +203,6 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory { addResourcesToBundle(new ArrayList(resourceList), theBundleType, theServerBase, theServer.getBundleInclusionRule(), theIncludes); addRootPropertiesToBundle(null, theServerBase, theCompleteUrl, theResult.size(), theBundleType); - myBundle.setPublished(theResult.getPublished()); - if (theServer.getPagingProvider() != null) { int limit; limit = theLimit != null ? theLimit : theServer.getPagingProvider().getDefaultPageSize(); @@ -232,10 +230,6 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory { myBundle.getBundleId().setValue(UUID.randomUUID().toString()); } - if (myBundle.getPublished().isEmpty()) { - myBundle.getPublished().setToCurrentTimeInLocalTimeZone(); - } - if (myBundle.getLinkBase().isEmpty()) { myBundle.getLinkBase().setValue(theServerBase); } @@ -264,12 +258,11 @@ public class Dstu1BundleFactory implements IVersionSpecificBundleFactory { } @Override - public void initializeBundleFromResourceList(String theAuthor, List theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) { + public void initializeBundleFromResourceList(String theAuthor, List theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) { myBundle = new Bundle(); myBundle.getAuthorName().setValue(theAuthor); myBundle.getBundleId().setValue(UUID.randomUUID().toString()); - myBundle.getPublished().setToCurrentTimeInLocalTimeZone(); myBundle.getLinkBase().setValue(theServerBase); myBundle.getLinkSelf().setValue(theCompleteUrl); myBundle.getType().setValueAsEnum(theBundleType); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IVersionSpecificBundleFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IVersionSpecificBundleFactory.java index 77f379bc75f..9537ef033d9 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IVersionSpecificBundleFactory.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IVersionSpecificBundleFactory.java @@ -46,7 +46,7 @@ public interface IVersionSpecificBundleFactory { IBaseResource getResourceBundle(); - void initializeBundleFromResourceList(String theAuthor, List theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType); + void initializeBundleFromResourceList(String theAuthor, List theResult, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType); void initializeWithBundleResource(IBaseResource theResource); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategy.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategy.java index 6bcdacd5b22..793e80e23bd 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategy.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategy.java @@ -30,10 +30,19 @@ import org.apache.commons.lang3.StringUtils; */ public class IncomingRequestAddressStrategy implements IServerAddressStrategy { + private String myServletPath; + @Override public String determineServerBase(ServletContext theServletContext, HttpServletRequest theRequest) { String requestFullPath = StringUtils.defaultString(theRequest.getRequestURI()); - String servletPath = StringUtils.defaultString(theRequest.getServletPath()); + + String servletPath; + if (myServletPath != null) { + servletPath = myServletPath; + } else { + servletPath = StringUtils.defaultString(theRequest.getServletPath()); + } + StringBuffer requestUrl = theRequest.getRequestURL(); String servletContextPath = ""; if (theServletContext != null) { @@ -48,7 +57,9 @@ public class IncomingRequestAddressStrategy implements IServerAddressStrategy { } int startOfPath = requestUrl.indexOf("//"); - if (startOfPath != -1 && (startOfPath + 2) < requestUrl.length()) { + int requestUrlLength = requestUrl.length(); + + if (startOfPath != -1 && (startOfPath + 2) < requestUrlLength) { startOfPath = requestUrl.indexOf("/", startOfPath + 2); } if (startOfPath == -1) { @@ -56,9 +67,9 @@ public class IncomingRequestAddressStrategy implements IServerAddressStrategy { } int contextIndex; - if (servletPath.length() == 0) { + if (servletPath.length() == 0 || servletPath.equals("/")) { if (requestPath.length() == 0) { - contextIndex = requestUrl.length(); + contextIndex = requestUrlLength; } else { contextIndex = requestUrl.indexOf(requestPath, startOfPath); } @@ -68,8 +79,30 @@ public class IncomingRequestAddressStrategy implements IServerAddressStrategy { String fhirServerBase; int length = contextIndex + servletPath.length(); + if (length > requestUrlLength) { + length = requestUrlLength; + } fhirServerBase = requestUrl.substring(0, length); return fhirServerBase; } + /** + * If set to a non-null value (default is null), this address strategy assumes that the FHIR endpoint is deployed to the given servlet path within the context. This is useful in some + * deployments where it isn't obvious to the servlet which part of the path is actually the root path to reach the servlet. + *

+ * Example values could be: + *

    + *
  • null
  • + *
  • /
  • + *
  • /base
  • + *
+ *

+ *

+ * Wildcards are not supported! + *

+ */ + public void setServletPath(String theServletPath) { + myServletPath = theServletPath; + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ResponseResourceList.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ResponseResourceList.java index 3e52314ae25..926d3b55873 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ResponseResourceList.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/ResponseResourceList.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.rest.server; +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2015 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import java.util.ArrayList; import java.util.List; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 557a2d10b3e..5580492d90f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -490,7 +490,7 @@ public class RestfulServer extends HttpServlet { return; } } - RestfulServerUtils.streamResponseAsResource(this, theResponse, (IResource) resBundle, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), theRequest.getFhirServerBase()); + RestfulServerUtils.streamResponseAsResource(this, theResponse, (IResource) resBundle, responseEncoding, prettyPrint, requestIsBrowser, narrativeMode, Constants.STATUS_HTTP_200_OK, theRequest.isRespondGzip(), theRequest.getFhirServerBase(), false); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java index 26daa5255cd..8c777eab3aa 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServerUtils.java @@ -76,10 +76,10 @@ public class RestfulServerUtils { } public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IBaseResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, - boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip, String theServerBase) throws IOException { + boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip, String theServerBase, boolean theAddContentLocationHeader) throws IOException { theHttpResponse.setStatus(stausCode); - if (theResource.getId() != null && theResource.getId().hasIdPart() && isNotBlank(theServerBase)) { + if (theAddContentLocationHeader && theResource.getId() != null && theResource.getId().hasIdPart() && isNotBlank(theServerBase)) { String resName = theServer.getFhirContext().getResourceDefinition(theResource).getName(); IIdType fullId = theResource.getId().withServerBase(theServerBase, resName); theHttpResponse.addHeader(Constants.HEADER_CONTENT_LOCATION, fullId.getValue()); @@ -205,9 +205,18 @@ public class RestfulServerUtils { } public static EncodingEnum determineRequestEncoding(Request theReq) { + EncodingEnum retVal = determineRequestEncodingNoDefault(theReq); + if (retVal != null) { + return retVal; + } + return EncodingEnum.XML; + } + + public static EncodingEnum determineRequestEncodingNoDefault(Request theReq) { + EncodingEnum retVal = null; Enumeration acceptValues = theReq.getServletRequest().getHeaders(Constants.HEADER_CONTENT_TYPE); if (acceptValues != null) { - while (acceptValues.hasMoreElements()) { + while (acceptValues.hasMoreElements() && retVal == null) { String nextAcceptHeaderValue = acceptValues.nextElement(); if (nextAcceptHeaderValue != null && isNotBlank(nextAcceptHeaderValue)) { for (String nextPart : nextAcceptHeaderValue.split(",")) { @@ -219,15 +228,15 @@ public class RestfulServerUtils { nextPart = nextPart.substring(0, scIdx); } nextPart = nextPart.trim(); - EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart); + retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextPart); if (retVal != null) { - return retVal; + break; } } } } } - return EncodingEnum.XML; + return retVal; } public static String createPagingLink(Set theIncludes, String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) { @@ -413,12 +422,12 @@ public class RestfulServerUtils { } } - public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IBaseResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, - boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, boolean theRespondGzip, String theServerBase) throws IOException { - int stausCode = 200; - RestfulServerUtils.streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip, - theServerBase); - } +// public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, +// boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, boolean theRespondGzip, String theServerBase) throws IOException { +// int stausCode = 200; +// RestfulServerUtils.streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip, +// theServerBase); +// } public static void validateResourceListNotNull(List theResourceList) { if (theResourceList == null) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ResourceNotFoundException.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ResourceNotFoundException.java index 600cdd4d0c0..2d89461d86d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ResourceNotFoundException.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/exceptions/ResourceNotFoundException.java @@ -20,6 +20,8 @@ package ca.uhn.fhir.rest.server.exceptions; * #L% */ +import org.hl7.fhir.instance.model.api.IIdType; + import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.base.composite.BaseIdentifierDt; import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; @@ -43,6 +45,14 @@ public class ResourceNotFoundException extends BaseServerResponseException { super(STATUS_CODE, createErrorMessage(theClass, theId), theOperationOutcome); } + public ResourceNotFoundException(Class theClass, IIdType theId) { + super(STATUS_CODE, createErrorMessage(theClass, theId)); + } + + public ResourceNotFoundException(Class theClass, IIdType theId, BaseOperationOutcome theOperationOutcome) { + super(STATUS_CODE, createErrorMessage(theClass, theId), theOperationOutcome); + } + /** * Constructor * @@ -66,6 +76,10 @@ public class ResourceNotFoundException extends BaseServerResponseException { super(STATUS_CODE, createErrorMessage(theId)); } + public ResourceNotFoundException(IIdType theId) { + super(STATUS_CODE, createErrorMessage(theId)); + } + public ResourceNotFoundException(IdDt theId, BaseOperationOutcome theOperationOutcome) { super(STATUS_CODE, createErrorMessage(theId), theOperationOutcome); } @@ -74,11 +88,11 @@ public class ResourceNotFoundException extends BaseServerResponseException { super(STATUS_CODE, theMessage); } - private static String createErrorMessage(Class theClass, IdDt theId) { + private static String createErrorMessage(Class theClass, IIdType theId) { return "Resource of type " + theClass.getSimpleName() + " with ID " + theId + " is not known"; } - private static String createErrorMessage(IdDt theId) { + private static String createErrorMessage(IIdType theId) { return "Resource " + (theId != null ? theId.getValue() : "") + " is not known"; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java index a8987477d3c..8737104ff11 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java @@ -129,7 +129,7 @@ public class ExceptionHandlingInterceptor extends InterceptorAdapter { boolean requestIsBrowser = RestfulServer.requestIsBrowser(theRequest); String fhirServerBase = ((Request) theRequestDetails).getFhirServerBase(); RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), theResponse, oo, RestfulServerUtils.determineResponseEncodingNoDefault(theRequest), true, requestIsBrowser, - NarrativeModeEnum.NORMAL, statusCode, false, fhirServerBase); + NarrativeModeEnum.NORMAL, statusCode, false, fhirServerBase, false); // theResponse.setStatus(statusCode); // theRequestDetails.getServer().addHeadersToResponse(theResponse); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java.orig b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java.orig deleted file mode 100644 index 9e81a04ae44..00000000000 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/ExceptionHandlingInterceptor.java.orig +++ /dev/null @@ -1,172 +0,0 @@ -package ca.uhn.fhir.rest.server.interceptor; - -/* - * #%L - * HAPI FHIR - Core Library - * %% - * Copyright (C) 2014 - 2015 University Health Network - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ - -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import java.io.IOException; -import java.util.Map; -import java.util.Map.Entry; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang3.exception.ExceptionUtils; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.base.resource.BaseOperationOutcome; -import ca.uhn.fhir.model.base.resource.BaseOperationOutcome.BaseIssue; -import ca.uhn.fhir.rest.method.Request; -import ca.uhn.fhir.rest.method.RequestDetails; -import ca.uhn.fhir.rest.server.Constants; -import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum; -import ca.uhn.fhir.rest.server.RestfulServerUtils; -import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; - -public class ExceptionHandlingInterceptor extends InterceptorAdapter { - - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExceptionHandlingInterceptor.class); - private Class[] myReturnStackTracesForExceptionTypes; - - /** - * If any server methods throw an exception which extends any of the given exception types, the exception - * stack trace will be returned to the user. This can be useful for helping to diagnose issues, but may - * not be desirable for production situations. - * - * @param theExceptionTypes The exception types for which to return the stack trace to the user. - * @return Returns an instance of this interceptor, to allow for easy method chaining. - */ - public ExceptionHandlingInterceptor setReturnStackTracesForExceptionTypes(Class... theExceptionTypes) { - myReturnStackTracesForExceptionTypes = theExceptionTypes; - return this; - } - - @Override -<<<<<<< HEAD - public boolean handleException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException { -======= - public boolean handleException(RestfulServer theRestfulServer, RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theRequest, HttpServletResponse theResponse) throws ServletException, IOException { - ourLog.error("AA", theException); ->>>>>>> 5956ab75fd9186ccc8b9836b60bc421b19d3d288 - BaseOperationOutcome oo = null; - int statusCode = Constants.STATUS_HTTP_500_INTERNAL_ERROR; - - FhirContext ctx = theRestfulServer.getFhirContext(); - - if (theException instanceof BaseServerResponseException) { - oo = ((BaseServerResponseException) theException).getOperationOutcome(); - statusCode = ((BaseServerResponseException) theException).getStatusCode(); - } - - /* - * Generate an OperationOutcome to return, unless the exception throw by the resource provider had one - */ - if (oo == null) { - try { - oo = (BaseOperationOutcome) ctx.getResourceDefinition("OperationOutcome").getImplementingClass().newInstance(); - } catch (Exception e1) { - ourLog.error("Failed to instantiate OperationOutcome resource instance", e1); - throw new ServletException("Failed to instantiate OperationOutcome resource instance", e1); - } - - BaseIssue issue = oo.addIssue(); - issue.getSeverityElement().setValue("error"); - if (theException instanceof InternalErrorException) { - ourLog.error("Failure during REST processing", theException); - populateDetails(theException, issue); - } else if (theException instanceof BaseServerResponseException) { - ourLog.warn("Failure during REST processing: {}", theException); - BaseServerResponseException baseServerResponseException = (BaseServerResponseException) theException; - statusCode = baseServerResponseException.getStatusCode(); - populateDetails(theException, issue); - if (baseServerResponseException.getAdditionalMessages() != null) { - for (String next : baseServerResponseException.getAdditionalMessages()) { - BaseIssue issue2 = oo.addIssue(); - issue2.getSeverityElement().setValue("error"); - issue2.setDetails(next); - } - } - } else { - ourLog.error("Failure during REST processing: " + theException.toString(), theException); - populateDetails(theException, issue); - statusCode = Constants.STATUS_HTTP_500_INTERNAL_ERROR; - } - } else { - ourLog.error("Unknown error during processing", theException); - } - - // Add headers associated with the specific error code - if (theException instanceof BaseServerResponseException) { - Map additional = ((BaseServerResponseException) theException).getAssociatedHeaders(); - if (additional != null) { - for (Entry next : additional.entrySet()) { - if (isNotBlank(next.getKey()) && next.getValue() != null) { - String nextKey = next.getKey(); - for (String nextValue : next.getValue()) { - theResponse.addHeader(nextKey, nextValue); - } - } - } - } - } - - boolean requestIsBrowser = RestfulServer.requestIsBrowser(theRequest); - String fhirServerBase = theRestfulServer.getServerBaseForRequest(theRequest); - - RestfulServerUtils.streamResponseAsResource(theRestfulServer, theResponse, oo, RestfulServerUtils.determineResponseEncodingNoDefault(theRequest), true, requestIsBrowser, - NarrativeModeEnum.NORMAL, statusCode, false, fhirServerBase); - -<<<<<<< HEAD -// theResponse.setStatus(statusCode); -// theRequestDetails.getServer().addHeadersToResponse(theResponse); -// theResponse.setContentType("text/plain"); -// theResponse.setCharacterEncoding("UTF-8"); -// theResponse.getWriter().append(theException.getMessage()); -// theResponse.getWriter().close(); -======= - theResponse.setStatus(statusCode); - theRestfulServer.addHeadersToResponse(theResponse); - theResponse.setContentType("text/plain"); - theResponse.setCharacterEncoding("UTF-8"); - theResponse.getWriter().append(theException.getMessage()); - theResponse.getWriter().close(); ->>>>>>> 5956ab75fd9186ccc8b9836b60bc421b19d3d288 - - return false; - } - - private void populateDetails(Throwable theException, BaseIssue issue) { - if (myReturnStackTracesForExceptionTypes != null) { - for (Class next : myReturnStackTracesForExceptionTypes) { - if (next.isAssignableFrom(theException.getClass())) { - issue.getDetailsElement().setValue(theException.getMessage() + "\n\n" + ExceptionUtils.getStackTrace(theException)); - return; - } - } - } - - issue.getDetailsElement().setValue(theException.getMessage()); - } - -} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java index f3d5a40984c..8e401dbe271 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/FhirTerser.java @@ -61,7 +61,7 @@ public class FhirTerser { if (theElement instanceof ISupportsUndeclaredExtensions) { ISupportsUndeclaredExtensions containingElement = (ISupportsUndeclaredExtensions) theElement; for (ExtensionDt nextExt : containingElement.getUndeclaredExtensions()) { - theCallback.acceptUndeclaredExtension(containingElement, theChildDefinition, theDefinition, nextExt); + theCallback.acceptUndeclaredExtension(containingElement, null, theChildDefinition, theDefinition, nextExt); addUndeclaredExtensions(nextExt, theDefinition, theChildDefinition, theCallback); } } @@ -90,10 +90,10 @@ public class FhirTerser { public List getAllPopulatedChildElementsOfType(IBaseResource theResource, final Class theType) { final ArrayList retVal = new ArrayList(); BaseRuntimeElementCompositeDefinition def = myContext.getResourceDefinition(theResource); - visit(theResource, null, def, new IModelVisitor() { + visit(theResource, null, null, def, new IModelVisitor() { @SuppressWarnings("unchecked") @Override - public void acceptElement(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition) { + public void acceptElement(IBase theElement, List thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition) { if (theElement == null || theElement.isEmpty()) { return; } @@ -105,7 +105,8 @@ public class FhirTerser { @SuppressWarnings("unchecked") @Override - public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition, ExtensionDt theNextExt) { + public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, List thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition, + ExtensionDt theNextExt) { if (theType.isAssignableFrom(theNextExt.getClass())) { retVal.add((T) theNextExt); } @@ -117,39 +118,32 @@ public class FhirTerser { return retVal; } - public List getAllResourceReferences(final IBaseResource theResource) { - final ArrayList retVal = new ArrayList(); - BaseRuntimeElementCompositeDefinition def = myContext.getResourceDefinition(theResource); - visit(theResource, null, def, new IModelVisitor() { - @Override - public void acceptElement(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition) { - if (theElement == null || theElement.isEmpty()) { - return; - } - String name = null; - if (theChildDefinition != null) { - name = theChildDefinition.getElementName(); - } - if (BaseResourceReferenceDt.class.isAssignableFrom(theElement.getClass())) { - retVal.add(new ResourceReferenceInfo(theResource, name, (BaseResourceReferenceDt) theElement)); - } - } + public List getAllResourceReferences(final IBaseResource theResource) { + final ArrayList retVal = new ArrayList(); + BaseRuntimeElementCompositeDefinition def = myContext.getResourceDefinition(theResource); + visit(theResource, null, null, def, new IModelVisitor() { + @Override + public void acceptElement(IBase theElement, List thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition) { + if (theElement == null || theElement.isEmpty()) { + return; + } + if (BaseResourceReferenceDt.class.isAssignableFrom(theElement.getClass())) { + retVal.add(new ResourceReferenceInfo(theResource, thePathToElement, (BaseResourceReferenceDt)theElement)); + } + } - @Override - public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition, ExtensionDt theNextExt) { - String name = null; - if (theChildDefinition != null) { - name = theChildDefinition.getElementName(); - } - if (theNextExt.getValue() != null && BaseResourceReferenceDt.class.isAssignableFrom(theNextExt.getValue().getClass())) { - retVal.add(new ResourceReferenceInfo(theResource, name, (BaseResourceReferenceDt) theNextExt.getValue())); - } - } - }); - return retVal; - } + @Override + public void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, List thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition, + ExtensionDt theNextExt) { + if (theNextExt.getValue() != null && BaseResourceReferenceDt.class.isAssignableFrom(theNextExt.getValue().getClass())) { + retVal.add(new ResourceReferenceInfo(theResource, thePathToElement, (BaseResourceReferenceDt)theNextExt.getValue())); + } + } + }); + return retVal; + } - private BaseRuntimeChildDefinition getDefinition(BaseRuntimeElementCompositeDefinition theCurrentDef, List theSubList) { + private BaseRuntimeChildDefinition getDefinition(BaseRuntimeElementCompositeDefinition theCurrentDef, List theSubList) { BaseRuntimeChildDefinition nextDef = theCurrentDef.getChildByNameOrThrowDataFormatException(theSubList.get(0)); if (theSubList.size() == 1) { @@ -222,8 +216,19 @@ public class FhirTerser { } - private void visit(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition, IModelVisitor theCallback) { - theCallback.acceptElement(theElement, theChildDefinition, theDefinition); + private List addNameToList(List theCurrentList, BaseRuntimeChildDefinition theChildDefinition) { + if (theChildDefinition == null) + return null; + if (theCurrentList== null || theCurrentList.isEmpty()) + return new ArrayList(Arrays.asList(theChildDefinition.getElementName())); + List newList = new ArrayList(theCurrentList); + newList.add(theChildDefinition.getElementName()); + return newList; + } + + private void visit(IBase theElement, List thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition, IModelVisitor theCallback) { + List pathToElement = addNameToList(thePathToElement, theChildDefinition); + theCallback.acceptElement(theElement, pathToElement, theChildDefinition, theDefinition); addUndeclaredExtensions(theElement, theDefinition, theChildDefinition, theCallback); // if (theElement.isEmpty()) { @@ -243,7 +248,7 @@ public class FhirTerser { IBaseResource theResource = resRefDt.getResource(); if (theResource.getId() == null || theResource.getId().isEmpty() || theResource.getId().isLocal()) { BaseRuntimeElementCompositeDefinition def = myContext.getResourceDefinition(theResource); - visit(theResource, null, def, theCallback); + visit(theResource, pathToElement, null, def, theCallback); } } break; @@ -285,9 +290,9 @@ public class FhirTerser { if (nextChild instanceof RuntimeChildDirectResource) { // Don't descend into embedded resources - theCallback.acceptElement(nextValue, nextChild, childElementDef); + theCallback.acceptElement(nextValue, null, nextChild, childElementDef); } else { - visit(nextValue, nextChild, childElementDef, theCallback); + visit(nextValue, pathToElement, nextChild, childElementDef, theCallback); } } } @@ -298,7 +303,7 @@ public class FhirTerser { BaseContainedDt value = (BaseContainedDt) theElement; for (IResource next : value.getContainedResources()) { BaseRuntimeElementCompositeDefinition def = myContext.getResourceDefinition(next); - visit(next, null, def, theCallback); + visit(next, pathToElement, null, def, theCallback); } break; } @@ -309,7 +314,7 @@ public class FhirTerser { case CONTAINED_RESOURCE_LIST: if (theElement != null) { BaseRuntimeElementDefinition def = myContext.getElementDefinition(theElement.getClass()); - visit(theElement, null, def, theCallback); + visit(theElement, null, theChildDefinition, def, theCallback); } break; } @@ -454,7 +459,7 @@ public class FhirTerser { */ public void visit(IBaseResource theResource, IModelVisitor theVisitor) { BaseRuntimeElementCompositeDefinition def = myContext.getResourceDefinition(theResource); - visit(theResource, null, def, theVisitor); + visit(theResource, null, null, def, theVisitor); } /** diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/IModelVisitor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/IModelVisitor.java index f183f8ee4d4..518080aab93 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/IModelVisitor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/IModelVisitor.java @@ -28,6 +28,8 @@ import ca.uhn.fhir.context.BaseRuntimeElementDefinition; import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions; +import java.util.List; + /** * @see FhirTerser#visit(IBaseResource, IModelVisitor) */ @@ -39,7 +41,7 @@ public interface IModelVisitor { * @param theChildDefinition May be null if this is a root element * @param theDefinition */ - void acceptElement(IBase theElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition); + void acceptElement(IBase theElement, List thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition); /** * @@ -48,7 +50,7 @@ public interface IModelVisitor { * @param theDefinition * @param theNextExt */ - void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition, ExtensionDt theNextExt); + void acceptUndeclaredExtension(ISupportsUndeclaredExtensions theContainingElement, List thePathToElement, BaseRuntimeChildDefinition theChildDefinition, BaseRuntimeElementDefinition theDefinition, ExtensionDt theNextExt); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ITestingUiClientFactory.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ITestingUiClientFactory.java new file mode 100644 index 00000000000..2ef7b1e6b08 --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ITestingUiClientFactory.java @@ -0,0 +1,39 @@ +package ca.uhn.fhir.util; + +/* + * #%L + * HAPI FHIR - Core Library + * %% + * Copyright (C) 2014 - 2015 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import javax.servlet.http.HttpServletRequest; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.rest.client.IGenericClient; + +/** + * This interface isn't used by hapi-fhir-base, but is used by the + * Web Testing UI + */ +public interface ITestingUiClientFactory { + + /** + * Instantiate a new client + */ + IGenericClient newClient(FhirContext theFhirContext, HttpServletRequest theRequest, String theServerBaseUrl); + +} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ResourceReferenceInfo.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ResourceReferenceInfo.java index 1c634d695fa..fd50f9f5a81 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ResourceReferenceInfo.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ResourceReferenceInfo.java @@ -28,58 +28,71 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import org.hl7.fhir.instance.model.IBaseResource; +import java.util.Iterator; +import java.util.List; import java.util.Set; /** * Created by Bill de Beaubien on 2/26/2015. */ public class ResourceReferenceInfo { - private String myOwningResource; - private String myName; - private BaseResourceReferenceDt myResource; + private String myOwningResource; + private String myName; + private BaseResourceReferenceDt myResource; - public ResourceReferenceInfo(IBaseResource theOwningResource, String theName, BaseResourceReferenceDt theResource) { - myOwningResource = theOwningResource.getClass().getAnnotation(ResourceDef.class).name(); - myName = theName; - myResource = theResource; - } + public ResourceReferenceInfo(IBaseResource theOwningResource, List thePathToElement, BaseResourceReferenceDt theResource) { + myOwningResource = theOwningResource.getClass().getAnnotation(ResourceDef.class).name(); + myResource = theResource; + if (thePathToElement != null && !thePathToElement.isEmpty()) { + StringBuilder sb = new StringBuilder(); + thePathToElement.iterator(); + for (Iterator iterator = thePathToElement.iterator(); iterator.hasNext(); ) { + sb.append(iterator.next()); + if (iterator.hasNext()) + sb.append("."); + } + myName = sb.toString(); + } else { + myName = null; + } + } - @Override - public String toString() { - ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); - b.append("name", myName); - b.append("resource", myResource.getReference()); - return b.build(); - } + @Override + public String toString() { + ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); + b.append("name", myName); + b.append("resource", myResource.getReference()); + return b.build(); + } - public String getName() { - return myName; - } + public String getName() { + return myName; + } - public BaseResourceReferenceDt getResourceReference() { - return myResource; - } + public BaseResourceReferenceDt getResourceReference() { + return myResource; + } - public boolean matchesIncludeSet(Set theIncludes) { - if (theIncludes == null) - return false; - for (Include include : theIncludes) { - if (matchesInclude(include)) - return true; - } - return false; - } + public boolean matchesIncludeSet(Set theIncludes) { + if (theIncludes == null) + return false; + for (Include include : theIncludes) { + if (matchesInclude(include)) + return true; + } + return false; + } - public boolean matchesInclude(Include theInclude) { - if (theInclude.getValue().equals("*")) { - return true; - } - if (theInclude.getValue().indexOf(':') != -1) { - // DSTU2 style - return (theInclude.getValue().equals(myOwningResource + ':' + myName)); - } else { - // DSTU1 style - return (theInclude.getValue().equals(myOwningResource + '.' + myName)); - } - } + public boolean matchesInclude(Include theInclude) { + if (theInclude.getValue().equals("*")) { + return true; + } + if (theInclude.getValue().indexOf(':') != -1) { + // DSTU2 style + return (theInclude.getValue().equals(myOwningResource + ':' + myName)); + } else { + // DSTU1 style + return (theInclude.getValue().equals(myOwningResource + '.' + myName)); + } + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java index 133f7357c1d..258b4e61a03 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/validation/FhirValidator.java @@ -25,6 +25,7 @@ import java.util.Iterator; import java.util.List; import org.apache.commons.lang3.Validate; +import org.hl7.fhir.instance.model.IBaseResource; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; @@ -199,10 +200,10 @@ public class FhirValidator { * @return the results of validation * @since 0.7 */ - public ValidationResult validateWithResult(IResource theResource) { + public ValidationResult validateWithResult(IBaseResource theResource) { Validate.notNull(theResource, "theResource must not be null"); - ValidationContext ctx = ValidationContext.forResource(myContext, theResource); + ValidationContext ctx = ValidationContext.forResource(myContext, (IResource) theResource); for (IValidator next : myValidators) { next.validateResource(ctx); diff --git a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IIdType.java b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IIdType.java index de8b50555c6..97eb992b832 100644 --- a/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IIdType.java +++ b/hapi-fhir-base/src/main/java/org/hl7/fhir/instance/model/api/IIdType.java @@ -1,6 +1,5 @@ package org.hl7.fhir.instance.model.api; -import ca.uhn.fhir.model.primitive.IdDt; /* * #%L @@ -69,4 +68,8 @@ public interface IIdType { boolean isAbsolute(); + boolean isIdPartValidLong(); + + Long getIdPartAsLong(); + } diff --git a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties index ed11a144c98..3be62aef4ce 100644 --- a/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties +++ b/hapi-fhir-base/src/main/resources/ca/uhn/fhir/i18n/hapi-messages.properties @@ -12,6 +12,9 @@ ca.uhn.fhir.rest.client.GenericClient.cannotDetermineResourceTypeFromUri=Unable ca.uhn.fhir.rest.client.RestfulClientFactory.failedToRetrieveConformance=Failed to retrieve the server's metadata statement during client initialization. URL used was: {0} ca.uhn.fhir.rest.client.RestfulClientFactory.wrongVersionInConformance=The server at base URL "{0}" returned a conformance statement indicating that it supports FHIR version "{1}" which corresponds to {2}, but this client is configured to use {3} (via the FhirContext). +ca.uhn.fhir.rest.method.BaseOutcomeReturningMethodBinding.noContentTypeInRequest=No Content-Type header was provided in the request. This is required for "{0}" operation +ca.uhn.fhir.rest.method.BaseOutcomeReturningMethodBinding.invalidContentTypeInRequest=Incorrect Content-Type header value of "{0}" was provided in the request. A FHIR Content-Type is required for "{1}" operation + ca.uhn.fhir.rest.method.OperationMethodBinding.methodNotSupported=HTTP Method {0} is not allowed for this operation. Allowed method(s): {1} ca.uhn.fhir.rest.method.OperationParamBinder.urlParamNotPrimitive=Can not invoke operation {0} using HTTP GET because parameter {1} is not a primitive datatype ca.uhn.fhir.rest.method.IncludeParameter.invalidIncludeNameInRequest=Invalid {2} parameter value: "{0}". Valid values are: {1} diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 69f149a2f48..f47ef7a4829 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -43,11 +43,13 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu 1.0-SNAPSHOT + true
ca.uhn.hapi.fhir hapi-fhir-structures-dstu2 1.0-SNAPSHOT + true diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java index 6f9b45405c2..b8d08abbf78 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirDao.java @@ -54,6 +54,7 @@ import org.apache.commons.lang3.Validate; import org.apache.http.NameValuePair; import org.apache.http.client.utils.URLEncodedUtils; import org.hl7.fhir.instance.model.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionStatus; @@ -444,12 +445,12 @@ public abstract class BaseFhirDao implements IDao { } @Override - public List getResources(final int theFromIndex, final int theToIndex) { + public List getResources(final int theFromIndex, final int theToIndex) { final StopWatch timer = new StopWatch(); TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager); - return template.execute(new TransactionCallback>() { + return template.execute(new TransactionCallback>() { @Override - public List doInTransaction(TransactionStatus theStatus) { + public List doInTransaction(TransactionStatus theStatus) { List resEntities = Lists.newArrayList(); List tupleSubList = tuples.subList(theFromIndex, theToIndex); @@ -471,7 +472,7 @@ public abstract class BaseFhirDao implements IDao { resEntities = resEntities.subList(0, limit); } - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList(); for (BaseHasResource next : resEntities) { RuntimeResourceDefinition type; try { @@ -503,7 +504,7 @@ public abstract class BaseFhirDao implements IDao { }; } - protected boolean isValidPid(IdDt theId) { + protected boolean isValidPid(IIdType theId) { String idPart = theId.getIdPart(); for (int i = 0; i < idPart.length(); i++) { char nextChar = idPart.charAt(i); @@ -514,9 +515,9 @@ public abstract class BaseFhirDao implements IDao { return true; } - protected List loadResourcesById(Set theIncludePids) { + protected List loadResourcesById(Set theIncludePids) { Set pids = new HashSet(); - for (IdDt next : theIncludePids) { + for (IIdType next : theIncludePids) { if (next.isIdPartValidLong()) { pids.add(next.getIdPartAsLong()); } else { @@ -538,7 +539,7 @@ public abstract class BaseFhirDao implements IDao { // } TypedQuery q = myEntityManager.createQuery(cq); - ArrayList retVal = new ArrayList(); + ArrayList retVal = new ArrayList(); for (ResourceTable next : q.getResultList()) { IResource resource = (IResource) toResource(next); retVal.add(resource); @@ -956,7 +957,7 @@ public abstract class BaseFhirDao implements IDao { return myContext.getResourceDefinition(theResource).getName(); } - protected Long translateForcedIdToPid(IdDt theId) { + protected Long translateForcedIdToPid(IIdType theId) { if (isValidPid(theId)) { return theId.getIdPartAsLong(); } else { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirResourceDao.java index 935df209c6d..bb4c2f05977 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseFhirResourceDao.java @@ -56,6 +56,7 @@ import javax.persistence.criteria.Root; import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.instance.model.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Required; import org.springframework.transaction.PlatformTransactionManager; @@ -660,12 +661,12 @@ public abstract class BaseFhirResourceDao extends BaseFhirD return new HashSet(q.getResultList()); } - private List addResourcesAsIncludesById(List theListToPopulate, Set includePids, List resources) { + private List addResourcesAsIncludesById(List theListToPopulate, Set includePids, List resources) { if (!includePids.isEmpty()) { ourLog.info("Loading {} included resources", includePids.size()); resources = loadResourcesById(includePids); - for (IResource next : resources) { - ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put(next, BundleEntrySearchModeEnum.INCLUDE); + for (IBaseResource next : resources) { + ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.put((IResource) next, BundleEntrySearchModeEnum.INCLUDE); } theListToPopulate.addAll(resources); } @@ -1000,7 +1001,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD return tags; } - protected abstract List getIncludeValues(FhirTerser theTerser, Include theInclude, IResource theResource, RuntimeResourceDefinition theResourceDef); + protected abstract List getIncludeValues(FhirTerser theTerser, Include theInclude, IBaseResource theResource, RuntimeResourceDefinition theResourceDef); public Class getResourceType() { return myResourceType; @@ -1074,8 +1075,8 @@ public abstract class BaseFhirResourceDao extends BaseFhirD } @Override - public List getResources(int theFromIndex, int theToIndex) { - ArrayList retVal = new ArrayList(); + public List getResources(int theFromIndex, int theToIndex) { + ArrayList retVal = new ArrayList(); if (theFromIndex == 0 && current != null) { retVal.add(current); } @@ -1128,7 +1129,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD return retVal; } - private void loadResourcesByPid(Collection theIncludePids, List theResourceListToPopulate, BundleEntrySearchModeEnum theBundleEntryStatus) { + private void loadResourcesByPid(Collection theIncludePids, List theResourceListToPopulate, BundleEntrySearchModeEnum theBundleEntryStatus) { if (theIncludePids.isEmpty()) { return; } @@ -1520,15 +1521,15 @@ public abstract class BaseFhirResourceDao extends BaseFhirD } @Override - public List getResources(final int theFromIndex, final int theToIndex) { + public List getResources(final int theFromIndex, final int theToIndex) { TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager); - return template.execute(new TransactionCallback>() { + return template.execute(new TransactionCallback>() { @Override - public List doInTransaction(TransactionStatus theStatus) { + public List doInTransaction(TransactionStatus theStatus) { List pidsSubList = pids.subList(theFromIndex, theToIndex); // Execute the query and make sure we return distinct results - List retVal = new ArrayList(); + List retVal = new ArrayList(); loadResourcesByPid(pidsSubList, retVal, BundleEntrySearchModeEnum.MATCH); /* @@ -1537,19 +1538,19 @@ public abstract class BaseFhirResourceDao extends BaseFhirD * so they are loaded outside the bundle provider */ if (theParams.getIncludes() != null && theParams.getIncludes().isEmpty() == false) { - Set previouslyLoadedPids = new HashSet(); - for (IResource next : retVal) { + Set previouslyLoadedPids = new HashSet(); + for (IBaseResource next : retVal) { previouslyLoadedPids.add(next.getId().toUnqualifiedVersionless()); } Set includePids = new HashSet(); - List resources = retVal; + List resources = retVal; do { includePids.clear(); FhirTerser t = getContext().newTerser(); for (Include next : theParams.getIncludes()) { - for (IResource nextResource : resources) { + for (IBaseResource nextResource : resources) { RuntimeResourceDefinition def = getContext().getResourceDefinition(nextResource); List values = getIncludeValues(t, next, nextResource, def); @@ -1875,7 +1876,7 @@ public abstract class BaseFhirResourceDao extends BaseFhirD if (Character.isDigit(theResource.getId().getIdPart().charAt(0))) { throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseFhirResourceDao.class, "failedToCreateWithClientAssignedNumericId", theResource.getId().getIdPart())); } - return doCreate(theResource, null, true); + return doCreate(theResource, null, thePerformIndexing); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu1.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu1.java index aca6944ae15..ed62cab3467 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu1.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu1.java @@ -24,6 +24,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.hl7.fhir.instance.model.IBaseResource; + import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.Include; @@ -32,7 +34,7 @@ import ca.uhn.fhir.util.FhirTerser; public class FhirResourceDaoDstu1 extends BaseFhirResourceDao { - protected List getIncludeValues(FhirTerser t, Include next, IResource nextResource, RuntimeResourceDefinition def) { + protected List getIncludeValues(FhirTerser t, Include next, IBaseResource nextResource, RuntimeResourceDefinition def) { List values; if ("*".equals(next.getValue())) { values = new ArrayList(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java index 56eeeddc196..074439e1b94 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java @@ -24,6 +24,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.hl7.fhir.instance.model.IBaseResource; + import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.model.api.IResource; @@ -33,7 +35,7 @@ import ca.uhn.fhir.util.FhirTerser; public class FhirResourceDaoDstu2 extends BaseFhirResourceDao { - protected List getIncludeValues(FhirTerser theTerser, Include theInclude, IResource theResource, RuntimeResourceDefinition theResourceDef) { + protected List getIncludeValues(FhirTerser theTerser, Include theInclude, IBaseResource theResource, RuntimeResourceDefinition theResourceDef) { List values; if ("*".equals(theInclude.getValue())) { values = new ArrayList(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java index 779dee09501..d8401aa03d4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java @@ -31,6 +31,7 @@ import java.util.Set; import javax.persistence.TypedQuery; +import org.hl7.fhir.instance.model.IBaseResource; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @@ -241,9 +242,9 @@ public class FhirSystemDaoDstu2 extends BaseFhirSystemDao { if (bundle.size() > configuredMax) { oo.addIssue().setSeverity(IssueSeverityEnum.WARNING).setDetails("Search nested within transaction found more than " + configuredMax + " matches, but paging is not supported in nested transactions"); } - List resourcesToAdd = bundle.getResources(0, Math.min(bundle.size(), configuredMax)); - for (IResource next : resourcesToAdd) { - searchBundle.addEntry().setResource(next); + List resourcesToAdd = bundle.getResources(0, Math.min(bundle.size(), configuredMax)); + for (IBaseResource next : resourcesToAdd) { + searchBundle.addEntry().setResource((IResource) next); } response.addEntry().setResource(searchBundle); diff --git a/hapi-fhir-jpaserver-base/src/main/resources/fhir-spring-config.xml b/hapi-fhir-jpaserver-base/src/main/resources/fhir-spring-config.xml index dc3d023d2c3..125a777c617 100644 --- a/hapi-fhir-jpaserver-base/src/main/resources/fhir-spring-config.xml +++ b/hapi-fhir-jpaserver-base/src/main/resources/fhir-spring-config.xml @@ -27,7 +27,7 @@ --> - + @@ -39,9 +39,11 @@ + - + + \ No newline at end of file diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java index 654dd8e442b..f7c0300a6cb 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2Test.java @@ -27,6 +27,7 @@ import java.util.Set; import org.apache.commons.lang3.RandomStringUtils; import org.hamcrest.core.StringContains; +import org.hl7.fhir.instance.model.IBaseResource; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -453,8 +454,8 @@ public class FhirResourceDaoDstu2Test { IBundleProvider history = ourPatientDao.history(null); assertEquals(4 + initialHistory, history.size()); - List resources = history.getResources(0, 4); - assertNotNull(resources.get(0).getResourceMetadata().get(ResourceMetadataKeyEnum.DELETED_AT)); + List resources = history.getResources(0, 4); + assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) resources.get(0))); try { ourPatientDao.delete(id2); @@ -534,9 +535,9 @@ public class FhirResourceDaoDstu2Test { IBundleProvider history = ourPatientDao.history(id, null); assertEquals(2, history.size()); - assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get(history.getResources(0, 0).get(0))); - assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get(history.getResources(0, 0).get(0)).getValue()); - assertNull(ResourceMetadataKeyEnum.DELETED_AT.get(history.getResources(1, 1).get(0))); + assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 0).get(0))); + assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 0).get(0)).getValue()); + assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(1, 1).get(0))); } @@ -879,7 +880,7 @@ public class FhirResourceDaoDstu2Test { map.setRevIncludes(Collections.singleton(Patient.INCLUDE_ORGANIZATION)); IBundleProvider resultsP = ourOrganizationDao.search(map); assertEquals(2, resultsP.size()); - List results = resultsP.getResources(0, resultsP.size()); + List results = resultsP.getResources(0, resultsP.size()); assertEquals(2, results.size()); assertEquals(Organization.class, results.get(0).getClass()); assertEquals(Patient.class, results.get(1).getClass()); @@ -2162,15 +2163,15 @@ public class FhirResourceDaoDstu2Test { assertEquals(2, historyBundle.size()); - List history = historyBundle.getResources(0, 2); + List history = historyBundle.getResources(0, 2); assertEquals("1", history.get(1).getId().getVersionIdPart()); assertEquals("2", history.get(0).getId().getVersionIdPart()); - assertEquals(published, history.get(1).getResourceMetadata().get(ResourceMetadataKeyEnum.PUBLISHED)); - assertEquals(published, history.get(1).getResourceMetadata().get(ResourceMetadataKeyEnum.PUBLISHED)); - assertEquals(updated, history.get(1).getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED)); + assertEquals(published, ResourceMetadataKeyEnum.PUBLISHED.get((IResource) history.get(1))); + assertEquals(published, ResourceMetadataKeyEnum.PUBLISHED.get((IResource) history.get(1))); + assertEquals(updated, ResourceMetadataKeyEnum.UPDATED.get((IResource) history.get(1))); assertEquals("001", ((Patient) history.get(1)).getIdentifierFirstRep().getValue()); - assertEquals(published2, history.get(0).getResourceMetadata().get(ResourceMetadataKeyEnum.PUBLISHED)); - assertEquals(updated2, history.get(0).getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED)); + assertEquals(published2, ResourceMetadataKeyEnum.PUBLISHED.get( (IResource) history.get(0))); + assertEquals(updated2, ResourceMetadataKeyEnum.UPDATED.get((IResource) history.get(0))); assertEquals("002", ((Patient) history.get(0)).getIdentifierFirstRep().getValue()); } @@ -2313,8 +2314,8 @@ public class FhirResourceDaoDstu2Test { private List toUnqualifiedVersionlessIds(IBundleProvider theFound) { List retVal = new ArrayList(); - for (IResource next : theFound.getResources(0, theFound.size())) { - retVal.add(next.getId().toUnqualifiedVersionless()); + for (IBaseResource next : theFound.getResources(0, theFound.size())) { + retVal.add((IdDt) next.getId().toUnqualifiedVersionless()); } return retVal; } @@ -2353,9 +2354,9 @@ public class FhirResourceDaoDstu2Test { IBundleProvider value = ourDeviceDao.search(new SearchParameterMap()); ourLog.info("Initial size: " + value.size()); - for (IResource next : value.getResources(0, value.size())) { + for (IBaseResource next : value.getResources(0, value.size())) { ourLog.info("Deleting: {}", next.getId()); - ourDeviceDao.delete(next.getId()); + ourDeviceDao.delete((IdDt) next.getId()); } value = ourDeviceDao.search(new SearchParameterMap()); @@ -2365,7 +2366,7 @@ public class FhirResourceDaoDstu2Test { } assertEquals(0, value.size()); - List res = value.getResources(0, 0); + List res = value.getResources(0, 0); assertTrue(res.isEmpty()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1Test.java index 1bc0af2afb3..6a1ac5c1dae 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu1Test.java @@ -11,6 +11,8 @@ import java.util.Date; import java.util.List; import java.util.Map; +import org.hl7.fhir.instance.model.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -93,7 +95,7 @@ public class FhirSystemDaoDstu1Test { IBundleProvider values = ourSystemDao.history(start); assertEquals(4, values.size()); - List res = values.getResources(0, 4); + List res = values.getResources(0, 4); assertEquals(newpid3, res.get(0).getId()); assertEquals(newpid2, res.get(1).getId()); assertEquals(newpid, res.get(2).getId()); @@ -146,7 +148,7 @@ public class FhirSystemDaoDstu1Test { IBundleProvider patResults = ourPatientDao.search(Patient.SP_IDENTIFIER, new IdentifierDt("urn:system", "testPersistWithSimpleLinkP01")); assertEquals(1, obsResults.size()); - IdDt foundPatientId = patResults.getResources(0, 1).get(0).getId(); + IIdType foundPatientId = patResults.getResources(0, 1).get(0).getId(); ResourceReferenceDt subject = obs.getSubject(); assertEquals(foundPatientId.getIdPart(), subject.getReference().getIdPart()); @@ -371,7 +373,7 @@ public class FhirSystemDaoDstu1Test { */ res = new ArrayList(); - List existing = results.getResources(0, 3); + List existing = results.getResources(0, 3); p1 = new Patient(); p1.setId(existing.get(0).getId()); @@ -391,7 +393,7 @@ public class FhirSystemDaoDstu1Test { IBundleProvider results2 = ourPatientDao.search(Patient.SP_IDENTIFIER, new TokenParam("urn:system", "testTransactionWithDelete")); assertEquals(1, results2.size()); - List existing2 = results2.getResources(0, 1); + List existing2 = results2.getResources(0, 1); assertEquals(existing2.get(0).getId(), existing.get(2).getId()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java index bf55e35584e..82122c3fb5e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2Test.java @@ -4,6 +4,7 @@ import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyString; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; @@ -11,9 +12,12 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; +import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import org.apache.commons.io.IOUtils; +import org.hl7.fhir.instance.model.IBaseResource; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -22,10 +26,12 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.jpa.entity.TagTypeEnum; +import ca.uhn.fhir.jpa.provider.SystemProviderTest; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.base.composite.BaseCodingDt; +import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; @@ -94,6 +100,30 @@ public class FhirSystemDaoDstu2Test { } + @Test + public void testTransactionFromBundle() throws Exception { + + InputStream bundleRes = SystemProviderTest.class.getResourceAsStream("/transaction_link_patient_eve.xml"); + String bundleStr = IOUtils.toString(bundleRes); + Bundle bundle = ourFhirContext.newXmlParser().parseResource(Bundle.class, bundleStr); + + Bundle resp = ourSystemDao.transaction(bundle); + + ourLog.info(ourFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(resp)); + + OperationOutcome oo = (OperationOutcome) resp.getEntry().get(0).getResource(); + assertThat(oo.getIssue().get(0).getDetailsElement().getValue(), containsString("Transaction completed")); + + assertThat(resp.getEntry().get(1).getTransactionResponse().getLocation(), startsWith("Patient/a555-44-4444/_history/")); + assertThat(resp.getEntry().get(2).getTransactionResponse().getLocation(), startsWith("Patient/temp6789/_history/")); + assertThat(resp.getEntry().get(3).getTransactionResponse().getLocation(), startsWith("Organization/GHH/_history/")); + + Patient p = ourPatientDao.read(new IdDt("Patient/a555-44-4444/_history/1")); + assertEquals("Patient/temp6789", p.getLink().get(0).getOther().getReference().getValue()); + } + + + @Test public void testTransactionReadAndSearch() { String methodName = "testTransactionReadAndSearch"; @@ -262,9 +292,9 @@ public class FhirSystemDaoDstu2Test { IBundleProvider history = ourPatientDao.history(id, null); assertEquals(2, history.size()); - assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get(history.getResources(0, 0).get(0))); - assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get(history.getResources(0, 0).get(0)).getValue()); - assertNull(ResourceMetadataKeyEnum.DELETED_AT.get(history.getResources(1, 1).get(0))); + assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 0).get(0))); + assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(0, 0).get(0)).getValue()); + assertNull(ResourceMetadataKeyEnum.DELETED_AT.get((IResource) history.getResources(1, 1).get(0))); } @@ -769,9 +799,9 @@ public class FhirSystemDaoDstu2Test { static void doDeleteEverything(IFhirSystemDao systemDao) { IBundleProvider all = systemDao.history(null); - List allRes = all.getResources(0, all.size()); - for (IResource iResource : allRes) { - if (ResourceMetadataKeyEnum.DELETED_AT.get(iResource) == null) { + List allRes = all.getResources(0, all.size()); + for (IBaseResource iResource : allRes) { + if (ResourceMetadataKeyEnum.DELETED_AT.get((IResource) iResource) == null) { ourLog.info("Deleting: {}", iResource.getId()); Bundle b = new Bundle(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java index 567d8b1899f..1af70280804 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/ResourceProviderDstu2Test.java @@ -273,6 +273,20 @@ public class ResourceProviderDstu2Test { } } + @Test + public void testSearchBundleDoesntIncludeTextElement() throws Exception { + HttpGet read = new HttpGet(ourServerBase + "/Patient?_format=json"); + CloseableHttpResponse response = ourHttpClient.execute(read); + try { + String text = IOUtils.toString(response.getEntity().getContent()); + ourLog.info(text); + assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatusLine().getStatusCode()); + assertThat(text, not(containsString("\"text\",\"type\""))); + } finally { + response.close(); + } + } + @Test public void testSearchWithInclude() throws Exception { Organization org = new Organization(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTest.java index 974b0564bc9..aa1a953a7d2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/SystemProviderTest.java @@ -1,9 +1,8 @@ package ca.uhn.fhir.jpa.provider; import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.List; +import org.apache.commons.io.IOUtils; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; @@ -14,15 +13,10 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu1; import ca.uhn.fhir.jpa.rp.dstu.ObservationResourceProvider; import ca.uhn.fhir.jpa.rp.dstu.OrganizationResourceProvider; import ca.uhn.fhir.jpa.rp.dstu.PatientResourceProvider; -import ca.uhn.fhir.jpa.provider.QuestionnaireResourceProvider; import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider; -import ca.uhn.fhir.model.api.Bundle; -import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.dstu.resource.Observation; import ca.uhn.fhir.model.dstu.resource.Organization; import ca.uhn.fhir.model.dstu.resource.Patient; @@ -32,7 +26,7 @@ import ca.uhn.fhir.rest.server.RestfulServer; public class SystemProviderTest { - + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SystemProviderTest.class); private static Server ourServer; private static ClassPathXmlApplicationContext ourAppCtx; private static FhirContext ourCtx; @@ -41,12 +35,10 @@ public class SystemProviderTest { @Test public void testTransactionFromBundle() throws Exception { - InputStream bundleRes = SystemProviderTest.class.getResourceAsStream("/test-server-seed-bundle.json"); - Bundle bundle = FhirContext.forDstu1().newJsonParser().parseBundle(new InputStreamReader(bundleRes)); - List res = bundle.toListOfResources(); - - ourClient.transaction().withResources(res).execute(); - + InputStream bundleRes = SystemProviderTest.class.getResourceAsStream("/transaction_link_patient_eve.xml"); + String bundle = IOUtils.toString(bundleRes); + String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute(); + ourLog.info(response); } @@ -59,28 +51,28 @@ public class SystemProviderTest { @SuppressWarnings("unchecked") @BeforeClass public static void beforeClass() throws Exception { - ourAppCtx = new ClassPathXmlApplicationContext("fhir-jpabase-spring-test-config.xml", "hapi-fhir-server-resourceproviders-dstu1.xml"); + ourAppCtx = new ClassPathXmlApplicationContext("fhir-jpabase-spring-test-config.xml", "hapi-fhir-server-resourceproviders-dstu2.xml"); - IFhirResourceDao patientDao = (IFhirResourceDao) ourAppCtx.getBean("myPatientDaoDstu1", IFhirResourceDao.class); + IFhirResourceDao patientDao = (IFhirResourceDao) ourAppCtx.getBean("myPatientDaoDstu2", IFhirResourceDao.class); PatientResourceProvider patientRp = new PatientResourceProvider(); patientRp.setDao(patientDao); - IFhirResourceDao questionnaireDao = (IFhirResourceDao) ourAppCtx.getBean("myQuestionnaireDaoDstu1", IFhirResourceDao.class); + IFhirResourceDao questionnaireDao = (IFhirResourceDao) ourAppCtx.getBean("myQuestionnaireDaoDstu2", IFhirResourceDao.class); QuestionnaireResourceProvider questionnaireRp = new QuestionnaireResourceProvider(); questionnaireRp.setDao(questionnaireDao); - IFhirResourceDao observationDao = (IFhirResourceDao) ourAppCtx.getBean("myObservationDaoDstu1", IFhirResourceDao.class); + IFhirResourceDao observationDao = (IFhirResourceDao) ourAppCtx.getBean("myObservationDaoDstu2", IFhirResourceDao.class); ObservationResourceProvider observationRp = new ObservationResourceProvider(); observationRp.setDao(observationDao); - IFhirResourceDao organizationDao = (IFhirResourceDao) ourAppCtx.getBean("myOrganizationDaoDstu1", IFhirResourceDao.class); + IFhirResourceDao organizationDao = (IFhirResourceDao) ourAppCtx.getBean("myOrganizationDaoDstu2", IFhirResourceDao.class); OrganizationResourceProvider organizationRp = new OrganizationResourceProvider(); organizationRp.setDao(organizationDao); RestfulServer restServer = new RestfulServer(); restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp); - JpaSystemProviderDstu1 systemProv = ourAppCtx.getBean(JpaSystemProviderDstu1.class, "mySystemProviderDstu1"); + JpaSystemProviderDstu2 systemProv = ourAppCtx.getBean(JpaSystemProviderDstu2.class, "mySystemProviderDstu2"); restServer.setPlainProviders(systemProv); int myPort = RandomServerPortProvider.findFreePort(); @@ -95,10 +87,12 @@ public class SystemProviderTest { servletHolder.setServlet(restServer); proxyHandler.addServlet(servletHolder, "/fhir/context/*"); + ourCtx = FhirContext.forDstu2(); + restServer.setFhirContext(ourCtx); + ourServer.setHandler(proxyHandler); ourServer.start(); - ourCtx = restServer.getFhirContext(); ourCtx.getRestfulClientFactory().setSocketTimeout(600 * 1000); ourClient = ourCtx.newRestfulGenericClient(serverBase); diff --git a/hapi-fhir-jpaserver-base/src/test/resources/fhir-jpabase-spring-test-config.xml b/hapi-fhir-jpaserver-base/src/test/resources/fhir-jpabase-spring-test-config.xml index 4358f85d330..2a8f7ae311b 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/fhir-jpabase-spring-test-config.xml +++ b/hapi-fhir-jpaserver-base/src/test/resources/fhir-jpabase-spring-test-config.xml @@ -30,7 +30,7 @@ --> - + @@ -43,7 +43,7 @@ - + diff --git a/hapi-fhir-jpaserver-base/src/test/resources/test-server-seed-bundle.json b/hapi-fhir-jpaserver-base/src/test/resources/test-server-seed-bundle.json deleted file mode 100644 index 420c0dd240f..00000000000 --- a/hapi-fhir-jpaserver-base/src/test/resources/test-server-seed-bundle.json +++ /dev/null @@ -1,690 +0,0 @@ - -{ - "resourceType":"Bundle", - "entry":[ - { - "deleted":null, - "title":"Patient http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.0/Patient/5556918", - "id":"Patient/5556918", - "link":[ - { - "rel":"self", - "href":"Patient/5556918" - } - ], - "published":"2014-05-29T10:49:32-04:00", - "content":{ - "resourceType":"Patient", - "id":"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.0/Patient/5556918", - "text":{ - "status":"generated", - "div":"
Donald null DUCK
Identifier7000135
Address10 Duxon Street
VICTORIA BC Can
Date of birth01 June 1980
" - }, - "identifier":[ - { - "use":"official", - "label":"University Health Network MRN 7000135", - "system":"urn:oid:2.16.840.1.113883.3.239.18.148", - "value":"7000135", - "assigner":{ - "resource":"Organization/1.3.6.1.4.1.12201" - } - } - ], - "name":[ - { - "family":[ - "Duck" - ], - "given":[ - "Donald" - ] - } - ], - "telecom":[ - { - "system":"phone", - "use":"home" - }, - { - "system":"phone", - "use":"work" - }, - { - "system":"phone", - "use":"mobile" - }, - { - "system":"email", - "use":"home" - } - ], - "gender":{ - "coding":[ - { - "system":"http://hl7.org/fhir/v3/AdministrativeGender", - "code":"M" - } - ] - }, - "birthDate":"1980-06-01T00:00:00", - "address":[ - { - "use":"home", - "line":[ - "10 Duxon Street" - ], - "city":"VICTORIA", - "state":"BC", - "zip":"V8N 1Y4", - "country":"Can" - } - ], - "managingOrganization":{ - "resource":"Organization/1.3.6.1.4.1.12201" - } - } - }, - { - "deleted":null, - "title":"DiagnosticReport http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.0/DiagnosticReport/5978827", - "id":"DiagnosticReport/5978827", - "link":[ - { - "rel":"self", - "href":"DiagnosticReport/5978827" - } - ], - "published":"2014-05-29T10:49:33-04:00", - "content":{ - "resourceType":"DiagnosticReport", - "id":"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.0/DiagnosticReport/5978827", - "text":{ - "status":"generated", - "div":"
C&S
Statuspartial
Issued 29 April 2014 17:21:56
NameValueInterpretationReference RangeStatus
Collection Info Spec #102758: 26 Sep 08 1117preliminary
Direct Stain pus cellspreliminary
Header To view Culture & Sensitivity Results, select
Header (Y) Report Query. Do NOT select number beside
Header Prelim or Final Result field, as there is
Header potential for viewing an incomplete report.
Organism Haemophilus influenzaefinal
Qualifier =>10 x E6 cfu/L SIGNIFICANT RESULT. Organisms cultured in quantities =>10 x E6 cfu/L are consistent with pneumonia. beta-lactamase positive result suggests resistance to ampicillin but generally susceptible to amoxicillin- clavulanic and cefuroxime.final
Sensitivities _Beta-lactamase Pos: final
Test Comment =>10 x E6 cfu/L Commensal florafinal
" - }, - "contained":[ - { - "resourceType":"Observation", - "id":"f816a276-cfad-4eca-a9fa-f1dff844a196", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"146151.1" - } - ], - "text":"Collection Info" - }, - "valueString":"Spec #102758: 26 Sep 08 1117", - "interpretation":{ - "coding":[ - { - "code":"N" - } - ] - }, - "issued":"2014-04-29T17:21:56", - "status":"preliminary" - }, - { - "resourceType":"Observation", - "id":"23b55496-1c2a-4d5f-9c24-8ca5042f4027", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"GM.2" - } - ], - "text":"Direct Stain" - }, - "valueString":"pus cells", - "interpretation":{ - "coding":[ - { - "code":"N" - } - ] - }, - "issued":"2014-04-29T17:21:56", - "status":"preliminary" - }, - { - "resourceType":"Observation", - "id":"74e6791a-d810-4545-8410-e9eca41e81d6", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"1H1.3" - } - ], - "text":"Header" - }, - "valueString":"To view Culture & Sensitivity Results, select", - "issued":"2014-04-29T17:21:56" - }, - { - "resourceType":"Observation", - "id":"cd8c6a6c-7ef5-446f-b07b-47a21bfe28ee", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"1H2.4" - } - ], - "text":"Header" - }, - "valueString":"(Y) Report Query. Do NOT select number beside", - "issued":"2014-04-29T17:21:56" - }, - { - "resourceType":"Observation", - "id":"4a3d453d-3a18-432f-8f1f-d7657c50dcd4", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"1H3.5" - } - ], - "text":"Header" - }, - "valueString":"Prelim or Final Result field, as there is", - "issued":"2014-04-29T17:21:56" - }, - { - "resourceType":"Observation", - "id":"0dd6cff6-f9db-42cc-89c9-2cd6ba6fe5af", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"1H4.6" - } - ], - "text":"Header" - }, - "valueString":"potential for viewing an incomplete report.", - "issued":"2014-04-29T17:21:56" - }, - { - "resourceType":"Observation", - "id":"6d6b0117-220f-4b9a-abf3-5faf772cfa61", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"F/O.4" - } - ], - "text":"Organism" - }, - "valueString":"Haemophilus influenzae", - "interpretation":{ - "coding":[ - { - "code":"A" - } - ] - }, - "issued":"2014-04-29T17:21:56", - "status":"final" - }, - { - "resourceType":"Observation", - "id":"64068acf-57f4-42c8-b0e6-416247067b16", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"F/31266.5" - } - ], - "text":"Qualifier" - }, - "valueString":"=>10 x E6 cfu/L SIGNIFICANT RESULT. Organisms cultured in quantities =>10 x E6 cfu/L are consistent with pneumonia. beta-lactamase positive result suggests resistance to ampicillin but generally susceptible to amoxicillin- clavulanic and cefuroxime.", - "interpretation":{ - "coding":[ - { - "code":"A" - } - ] - }, - "issued":"2014-04-29T17:21:56", - "status":"final" - }, - { - "resourceType":"Observation", - "id":"0f9d254f-3ad1-404b-9be9-20258b3c242f", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"F/31415.6" - } - ], - "text":"Sensitivities" - }, - "valueString":"_Beta-lactamase Pos: ", - "interpretation":{ - "coding":[ - { - "code":"A" - } - ] - }, - "issued":"2014-04-29T17:21:56", - "status":"final" - }, - { - "resourceType":"Observation", - "id":"349bb02b-fbbe-4ce0-b190-3f545240dcc0", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"Q.3" - } - ], - "text":"Test Comment" - }, - "valueString":"=>10 x E6 cfu/L Commensal flora", - "interpretation":{ - "coding":[ - { - "code":"N" - } - ] - }, - "issued":"2014-04-29T17:21:56", - "status":"final" - }, - { - "resourceType":"Observation", - "id":"f816a276-cfad-4eca-a9fa-f1dff844a196", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"146151.1" - } - ], - "text":"Collection Info" - }, - "valueString":"Spec #102758: 26 Sep 08 1117", - "interpretation":{ - "coding":[ - { - "code":"N" - } - ] - }, - "issued":"2014-04-29T17:21:56", - "status":"preliminary" - }, - { - "resourceType":"Observation", - "id":"23b55496-1c2a-4d5f-9c24-8ca5042f4027", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"GM.2" - } - ], - "text":"Direct Stain" - }, - "valueString":"pus cells", - "interpretation":{ - "coding":[ - { - "code":"N" - } - ] - }, - "issued":"2014-04-29T17:21:56", - "status":"preliminary" - }, - { - "resourceType":"Observation", - "id":"74e6791a-d810-4545-8410-e9eca41e81d6", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"1H1.3" - } - ], - "text":"Header" - }, - "valueString":"To view Culture & Sensitivity Results, select", - "issued":"2014-04-29T17:21:56" - }, - { - "resourceType":"Observation", - "id":"cd8c6a6c-7ef5-446f-b07b-47a21bfe28ee", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"1H2.4" - } - ], - "text":"Header" - }, - "valueString":"(Y) Report Query. Do NOT select number beside", - "issued":"2014-04-29T17:21:56" - }, - { - "resourceType":"Observation", - "id":"4a3d453d-3a18-432f-8f1f-d7657c50dcd4", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"1H3.5" - } - ], - "text":"Header" - }, - "valueString":"Prelim or Final Result field, as there is", - "issued":"2014-04-29T17:21:56" - }, - { - "resourceType":"Observation", - "id":"0dd6cff6-f9db-42cc-89c9-2cd6ba6fe5af", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"1H4.6" - } - ], - "text":"Header" - }, - "valueString":"potential for viewing an incomplete report.", - "issued":"2014-04-29T17:21:56" - }, - { - "resourceType":"Observation", - "id":"6d6b0117-220f-4b9a-abf3-5faf772cfa61", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"F/O.4" - } - ], - "text":"Organism" - }, - "valueString":"Haemophilus influenzae", - "interpretation":{ - "coding":[ - { - "code":"A" - } - ] - }, - "issued":"2014-04-29T17:21:56", - "status":"final" - }, - { - "resourceType":"Observation", - "id":"64068acf-57f4-42c8-b0e6-416247067b16", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"F/31266.5" - } - ], - "text":"Qualifier" - }, - "valueString":"=>10 x E6 cfu/L SIGNIFICANT RESULT. Organisms cultured in quantities =>10 x E6 cfu/L are consistent with pneumonia. beta-lactamase positive result suggests resistance to ampicillin but generally susceptible to amoxicillin- clavulanic and cefuroxime.", - "interpretation":{ - "coding":[ - { - "code":"A" - } - ] - }, - "issued":"2014-04-29T17:21:56", - "status":"final" - }, - { - "resourceType":"Observation", - "id":"0f9d254f-3ad1-404b-9be9-20258b3c242f", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"F/31415.6" - } - ], - "text":"Sensitivities" - }, - "valueString":"_Beta-lactamase Pos: ", - "interpretation":{ - "coding":[ - { - "code":"A" - } - ] - }, - "issued":"2014-04-29T17:21:56", - "status":"final" - }, - { - "resourceType":"Observation", - "id":"349bb02b-fbbe-4ce0-b190-3f545240dcc0", - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.6", - "code":"Q.3" - } - ], - "text":"Test Comment" - }, - "valueString":"=>10 x E6 cfu/L Commensal flora", - "interpretation":{ - "coding":[ - { - "code":"N" - } - ] - }, - "issued":"2014-04-29T17:21:56", - "status":"final" - } - ], - "name":{ - "coding":[ - { - "system":"urn:oid:1.3.6.1.4.1.12201.102.5", - "code":"4140" - } - ], - "text":"C&S" - }, - "status":"partial", - "issued":"2014-04-29T17:21:56", - "subject":{ - "resource":"Patient/5556918" - }, - "identifier":{ - "value":"2363922" - }, - "diagnosticDateTime":"2014-04-14T00:00:00-04:00", - "result":[ - { - "resource":"#f816a276-cfad-4eca-a9fa-f1dff844a196" - }, - { - "resource":"#23b55496-1c2a-4d5f-9c24-8ca5042f4027" - }, - { - "resource":"#74e6791a-d810-4545-8410-e9eca41e81d6" - }, - { - "resource":"#cd8c6a6c-7ef5-446f-b07b-47a21bfe28ee" - }, - { - "resource":"#4a3d453d-3a18-432f-8f1f-d7657c50dcd4" - }, - { - "resource":"#0dd6cff6-f9db-42cc-89c9-2cd6ba6fe5af" - }, - { - "resource":"#6d6b0117-220f-4b9a-abf3-5faf772cfa61" - }, - { - "resource":"#64068acf-57f4-42c8-b0e6-416247067b16" - }, - { - "resource":"#0f9d254f-3ad1-404b-9be9-20258b3c242f" - }, - { - "resource":"#349bb02b-fbbe-4ce0-b190-3f545240dcc0" - } - ] - } - }, - { - "deleted":null, - "title":"Organization http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.0/Organization/1.3.6.1.4.1.12201", - "id":"Organization/1.3.6.1.4.1.12201", - "link":[ - { - "rel":"self", - "href":"Organization/1.3.6.1.4.1.12201" - } - ], - "published":"2014-05-29T10:49:32-04:00", - "content":{ - "resourceType":"Organization", - "id":"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.0/Organization/1.3.6.1.4.1.12201", - "extension":[ - { - "url":"http://fhir.connectinggta.ca/Profile/organization#providerIdPool", - "valueUri":"1.3.6.1.4.1.12201.1" - } - ], - "text":{ - "status":"empty", - "div":"
No narrative template available for resource profile: http://fhir.connectinggta.ca/Profile/organization
" - }, - "contained":[ - { - "resourceType":"Location", - "id":"1.3.6.1.4.1.12201.100.1", - "identifier":{ - "system":"urn:cgta:facility_ids", - "value":"1.3.6.1.4.1.12201.100.1" - }, - "name":"Toronto General Hospital" - }, - { - "resourceType":"Location", - "id":"1.3.6.1.4.1.12201.100.2", - "identifier":{ - "system":"urn:cgta:facility_ids", - "value":"1.3.6.1.4.1.12201.100.2" - }, - "name":"Toronto Western Hospital" - }, - { - "resourceType":"Location", - "id":"1.3.6.1.4.1.12201.100.3", - "identifier":{ - "system":"urn:cgta:facility_ids", - "value":"1.3.6.1.4.1.12201.100.3" - }, - "name":"Princess Margaret Hospital" - }, - { - "resourceType":"Location", - "id":"1.3.6.1.4.1.12201.100.4", - "identifier":{ - "system":"urn:cgta:facility_ids", - "value":"1.3.6.1.4.1.12201.100.4" - }, - "name":"Toronto Rehab Institute" - }, - { - "resourceType":"Location", - "id":"1.3.6.1.4.1.12201.100.1", - "identifier":{ - "system":"urn:cgta:facility_ids", - "value":"1.3.6.1.4.1.12201.100.1" - }, - "name":"Toronto General Hospital" - }, - { - "resourceType":"Location", - "id":"1.3.6.1.4.1.12201.100.2", - "identifier":{ - "system":"urn:cgta:facility_ids", - "value":"1.3.6.1.4.1.12201.100.2" - }, - "name":"Toronto Western Hospital" - }, - { - "resourceType":"Location", - "id":"1.3.6.1.4.1.12201.100.3", - "identifier":{ - "system":"urn:cgta:facility_ids", - "value":"1.3.6.1.4.1.12201.100.3" - }, - "name":"Princess Margaret Hospital" - }, - { - "resourceType":"Location", - "id":"1.3.6.1.4.1.12201.100.4", - "identifier":{ - "system":"urn:cgta:facility_ids", - "value":"1.3.6.1.4.1.12201.100.4" - }, - "name":"Toronto Rehab Institute" - } - ], - "name":"University Health Network", - "type":{ - "coding":[ - { - "code":"HOSPITAL" - } - ] - }, - "address":[ - { - "line":[ - "R. Fraser Elliott Building, 1st Floor", - "190 Elizabeth St." - ], - "city":"Toronto", - "state":"ON", - "zip":"M5G 2C4" - } - ], - "location":[ - { - "resource":"#1.3.6.1.4.1.12201.100.1" - }, - { - "resource":"#1.3.6.1.4.1.12201.100.2" - }, - { - "resource":"#1.3.6.1.4.1.12201.100.3" - }, - { - "resource":"#1.3.6.1.4.1.12201.100.4" - } - ] - } - } - ] -} \ No newline at end of file diff --git a/hapi-fhir-jpaserver-base/src/test/resources/transaction_link_patient_eve.xml b/hapi-fhir-jpaserver-base/src/test/resources/transaction_link_patient_eve.xml new file mode 100644 index 00000000000..35790c03f00 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/transaction_link_patient_eve.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + + + +
+
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/hapi-fhir-jpaserver-example/pom.xml b/hapi-fhir-jpaserver-example/pom.xml index 26d7232c7c4..e8b4115e242 100644 --- a/hapi-fhir-jpaserver-example/pom.xml +++ b/hapi-fhir-jpaserver-example/pom.xml @@ -2,22 +2,25 @@ 4.0.0 + ca.uhn.hapi.example hapi-fhir-jpaserver-example - 0.9-SNAPSHOT + 1.0-SNAPSHOT war HAPI FHIR JPA Server - Example @@ -228,7 +231,7 @@ org.apache.maven.plugins maven-deploy-plugin - false + true diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java index d989d6aaa60..a204a42f37d 100644 --- a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -4,12 +4,11 @@ import java.util.List; import javax.servlet.ServletException; -import ca.uhn.fhir.context.FhirVersionEnum; - import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.WebApplicationContext; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu1; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; @@ -23,7 +22,7 @@ import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; public class JpaServerDemo extends RestfulServer { @@ -123,12 +122,12 @@ public class JpaServerDemo extends RestfulServer { setPagingProvider(new FifoMemoryPagingProvider(10)); /* - * Do some fancy logging to create a nice access log that has details about each incoming request. + * Load interceptors for the server from Spring (these are defined in hapi-fhir-server-config.xml */ - LoggingInterceptor loggingInterceptor = new LoggingInterceptor(); - loggingInterceptor.setLoggerName("fhir.access"); - loggingInterceptor.setMessageFormat("Path[${servletPath}] Operation[${operationType} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}]"); - this.registerInterceptor(loggingInterceptor); + List interceptorBeans = myAppCtx.getBean("myServerInterceptors", List.class); + for (IServerInterceptor interceptor : interceptorBeans) { + this.registerInterceptor(interceptor); + } } diff --git a/hapi-fhir-jpaserver-example/src/main/webapp/WEB-INF/hapi-fhir-server-config.xml b/hapi-fhir-jpaserver-example/src/main/webapp/WEB-INF/hapi-fhir-server-config.xml index 3922d51ebb8..13219e8e020 100644 --- a/hapi-fhir-jpaserver-example/src/main/webapp/WEB-INF/hapi-fhir-server-config.xml +++ b/hapi-fhir-jpaserver-example/src/main/webapp/WEB-INF/hapi-fhir-server-config.xml @@ -17,8 +17,22 @@
- + + + + + + + + + + + \ No newline at end of file diff --git a/hapi-fhir-jpaserver-example/src/main/webapp/WEB-INF/hapi-fhir-server-database-config.xml b/hapi-fhir-jpaserver-example/src/main/webapp/WEB-INF/hapi-fhir-server-database-config.xml index 0190f7d53cf..8e993d11811 100644 --- a/hapi-fhir-jpaserver-example/src/main/webapp/WEB-INF/hapi-fhir-server-database-config.xml +++ b/hapi-fhir-jpaserver-example/src/main/webapp/WEB-INF/hapi-fhir-server-database-config.xml @@ -32,7 +32,7 @@ database (e.g. Postgres). Consult the Hibernate documentation to see a list of available dialects. --> - + diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java index 33cab9ca62f..1ea6366ceec 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java @@ -7,17 +7,16 @@ import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; -import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; - import org.apache.commons.lang3.StringUtils; import org.springframework.context.ApplicationContext; import org.springframework.web.context.ContextLoaderListener; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu1; +import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu1; +import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.rest.server.ETagSupportEnum; import ca.uhn.fhir.rest.server.EncodingEnum; @@ -25,7 +24,7 @@ import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; public class TestRestfulServer extends RestfulServer { @@ -164,13 +163,12 @@ public class TestRestfulServer extends RestfulServer { setPagingProvider(new FifoMemoryPagingProvider(10)); /* - * Do some fancy logging to create a nice access log that has details - * about each incoming request. + * Load interceptors for the server from Spring (these are defined in hapi-fhir-server-config.xml */ - LoggingInterceptor loggingInterceptor = new LoggingInterceptor(); - loggingInterceptor.setLoggerName("fhirtest.access"); - loggingInterceptor.setMessageFormat("Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}]"); - this.registerInterceptor(loggingInterceptor); + List interceptorBeans = myAppCtx.getBean("myServerInterceptors", List.class); + for (IServerInterceptor interceptor : interceptorBeans) { + this.registerInterceptor(interceptor); + } } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-server-config.xml b/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-server-config.xml index 3922d51ebb8..45432d2ab5c 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-server-config.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-server-config.xml @@ -17,8 +17,22 @@ - + + + + + + + + + + + \ No newline at end of file diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-server-database-config.xml b/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-server-database-config.xml index 80719eaa17a..36e9c165396 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-server-database-config.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-server-database-config.xml @@ -56,7 +56,7 @@ --> - + diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml b/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml index cec7fc15998..a0ba504793c 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml @@ -12,11 +12,12 @@ - home , DSTU1 , UHN/HAPI Server (DSTU1 FHIR) , http://fhirtest.uhn.ca/baseDstu1 home_dev , DSTU2 , UHN/HAPI Server (DSTU2 FHIR) , http://fhirtest.uhn.ca/baseDstu2 - hi , DSTU1 , Health Intersections (DSTU1 FHIR) , http://fhir.healthintersections.com.au/open + home , DSTU1 , UHN/HAPI Server (DSTU1 FHIR) , http://fhirtest.uhn.ca/baseDstu1 hidev , DSTU2 , Health Intersections (DSTU2 FHIR) , http://fhir-dev.healthintersections.com.au/open - furore , DSTU1 , Spark - Furore , http://spark.furore.com/fhir + hi , DSTU1 , Health Intersections (DSTU1 FHIR) , http://fhir.healthintersections.com.au/open + furored2 , DSTU2 , Spark - Furore (DSTU2 FHIR) , http://spark-dstu2.furore.com/fhir + furore , DSTU1 , Spark - Furore (DSTU1 FHIR) , http://spark.furore.com/fhir blaze , DSTU1 , Blaze (Orion Health) , https://fhir.orionhealth.com/blaze/fhir oridashi , DSTU1 , Oridashi , http://demo.oridashi.com.au:8190 diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java b/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java index 5a6aa244fd1..77db4d4fad3 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/test/java/ca/uhn/fhirtest/UhnFhirTestApp.java @@ -66,7 +66,7 @@ public class UhnFhirTestApp { client.create(p1); List resources = ctx.newJsonParser().parseBundle(IOUtils.toString(UhnFhirTestApp.class.getResourceAsStream("/test-server-seed-bundle.json"))).toListOfResources(); - client.transaction(resources); + client.transaction().withResources(resources).execute(); // for (int i = 0; i < 1000; i++) { // diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/test/resources/fhir-spring-test-config.xml b/hapi-fhir-jpaserver-uhnfhirtest/src/test/resources/fhir-spring-test-config.xml index b761e8b75dd..6f24773a275 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/test/resources/fhir-spring-test-config.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/test/resources/fhir-spring-test-config.xml @@ -34,7 +34,7 @@ - + @@ -47,7 +47,7 @@ - + diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java index f1503066ac6..bc3216204a4 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/JsonParserTest.java @@ -601,6 +601,7 @@ public class JsonParserTest { public void testEncodeBundleCategory() { Bundle b = new Bundle(); + BundleEntry e = b.addEntry(); e.setResource(new Patient()); b.addCategory("scheme", "term", "label"); @@ -609,6 +610,7 @@ public class JsonParserTest { ourLog.info(val); assertThat(val, StringContains.containsString("\"category\":[{\"term\":\"term\",\"label\":\"label\",\"scheme\":\"scheme\"}]")); + assertThat(val, not(containsString("text"))); b = new FhirContext().newJsonParser().parseBundle(val); assertEquals(1, b.getEntries().size()); @@ -620,6 +622,7 @@ public class JsonParserTest { } + @Test public void testEncodeBundleEntryCategory() { @@ -1219,7 +1222,6 @@ public class JsonParserTest { Bundle b = new Bundle(); InstantDt pub = InstantDt.withCurrentTime(); - b.setPublished(pub); Thread.sleep(2); Patient p1 = new Patient(); @@ -1245,7 +1247,6 @@ public class JsonParserTest { ourLog.info(bundleString); List strings = new ArrayList(); - strings.addAll(Arrays.asList("\"published\":\"" + pub.getValueAsString() + "\"")); strings.addAll(Arrays.asList("\"id\":\"1\"")); strings.addAll(Arrays.asList("this is the summary")); strings.addAll(Arrays.asList("\"id\":\"2\"", "\"rel\":\"alternate\"", "\"href\":\"http://foo/bar\"")); diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java index ab7dd48a646..db0fbce13e5 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java @@ -314,7 +314,6 @@ public class XmlParserTest { b.getCategories().addTag("http://hl7.org/fhir/tag", "http://hl7.org/fhir/tag/message", "Message"); InstantDt pub = InstantDt.withCurrentTime(); - b.setPublished(pub); Thread.sleep(2); Patient p1 = new Patient(); @@ -340,7 +339,6 @@ public class XmlParserTest { ourLog.info(bundleString); List strings = new ArrayList(); - strings.addAll(Arrays.asList("", pub.getValueAsString(), "")); strings.add(""); strings.addAll(Arrays.asList("", "1", "
", "", "")); strings.addAll(Arrays.asList("", "2", "", "", "")); @@ -380,7 +378,6 @@ public class XmlParserTest { b.getCategories().addTag("http://hl7.org/fhir/tag", "http://hl7.org/fhir/tag/message", "Message"); InstantDt pub = InstantDt.withCurrentTime(); - b.setPublished(pub); Thread.sleep(2); Patient p1 = new Patient(); @@ -406,7 +403,6 @@ public class XmlParserTest { ourLog.info(bundleString); List strings = new ArrayList(); - strings.addAll(Arrays.asList("", pub.getValueAsString(), "")); strings.add(""); strings.addAll(Arrays.asList("", "1", "", "", "")); strings.addAll(Arrays.asList("", "2", "", "", "")); diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientTest.java index 11f66b0026d..7376a97b5b7 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/ClientTest.java @@ -102,8 +102,7 @@ public class ClientTest { "\n" + "<id>d039f91a-cc3c-4013-988e-af4d8d0614bd</id>\n" + "<os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">1</os:totalResults>\n" + - "<published>2014-03-11T16:35:07-04:00</published>\n" + - "<author>\n" + + "<author>\n" + "<name>ca.uhn.fhir.rest.server.DummyRestfulServer</name>\n" + "</author>\n" + "<entry>\n" + @@ -301,7 +300,6 @@ public class ClientTest { String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\"><title/><id>6c1d93be-027f-468d-9d47-f826cd15cf42</id>" + "<link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/>" + "<link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults>" - + "<published>2014-04-13T18:24:50-04:00</published>" + "<author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author>" + "<entry><title>Patient 222222" + ""+date1.getValueAsString()+"" @@ -374,7 +372,6 @@ public class ClientTest { String msg = "<id>6c1d93be-027f-468d-9d47-f826cd15cf42</id>" + "<link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/>" + "<link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults>" - + "<published>2014-04-13T18:24:50-04:00</published>" + "<author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author>" + "<entry><title>Patient 222222" + ""+date1.getValueAsString()+"" @@ -446,7 +443,6 @@ public class ClientTest { String msg = "<id>6c1d93be-027f-468d-9d47-f826cd15cf42</id>" + "<link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/>" + "<link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults>" - + "<published>2014-04-13T18:24:50-04:00</published>" + "<author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author>" + "<entry><title>Patient 222222" + ""+date1.getValueAsString()+"" @@ -511,7 +507,7 @@ public class ClientTest { public void testHistoryWithParams() throws Exception { //@formatter:off - final String msg = "<id>6c1d93be-027f-468d-9d47-f826cd15cf42</id><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/><link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults><published>2014-04-13T18:24:50-04:00</published><author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author><entry><title>Patient 2222221969-12-31T19:00:20.000-05:001969-12-31T19:00:10.000-05:00Patient 2222221969-12-31T19:00:30.000-05:001969-12-31T19:00:10.000-05:00"; + final String msg = "<id>6c1d93be-027f-468d-9d47-f826cd15cf42</id><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/><link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults><author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author><entry><title>Patient 2222221969-12-31T19:00:20.000-05:001969-12-31T19:00:10.000-05:00Patient 2222221969-12-31T19:00:30.000-05:001969-12-31T19:00:10.000-05:00"; //@formatter:on ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/GenericClientTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/GenericClientTest.java index e20ca86e6ca..ad9b9d1df8b 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/GenericClientTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/GenericClientTest.java @@ -85,8 +85,7 @@ public class GenericClientTest { "\n" + "<id>d039f91a-cc3c-4013-988e-af4d8d0614bd</id>\n" + "<os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">1</os:totalResults>\n" + - "<published>2014-03-11T16:35:07-04:00</published>\n" + - "<author>\n" + + "<author>\n" + "<name>ca.uhn.fhir.rest.server.DummyRestfulServer</name>\n" + "</author>\n" + "<entry>\n" + @@ -132,7 +131,7 @@ public class GenericClientTest { ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); - when(myHttpResponse.getAllHeaders()).thenReturn(new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22") }); + when(myHttpResponse.getAllHeaders()).thenReturn(new Header[]{new BasicHeader(Constants.HEADER_LOCATION, "/Patient/44/_history/22")}); when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8"))); @@ -141,14 +140,14 @@ public class GenericClientTest { when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 201, "OK")); MethodOutcome resp = client.create().resource(ourCtx.newXmlParser().encodeResourceToString(p1)).execute(); assertTrue(resp.getCreated()); - + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); resp = client.create().resource(ourCtx.newXmlParser().encodeResourceToString(p1)).execute(); assertNull(resp.getCreated()); - + } - - + + @Test public void testCreateWithStringAutoDetectsEncoding() throws Exception { diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/IncludedResourceStitchingClientTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/IncludedResourceStitchingClientTest.java index 697c06340c9..3068f3674eb 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/IncludedResourceStitchingClientTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/IncludedResourceStitchingClientTest.java @@ -114,8 +114,7 @@ public class IncludedResourceStitchingClientTest { " <link rel=\"self\" href=\"http://localhost:49782/Patient?_query=declaredExtInclude&_pretty=true\"/>\n" + " <link rel=\"fhir-base\" href=\"http://localhost:49782\"/>\n" + " <os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults>\n" + - " <published>2014-08-12T10:22:19.097-04:00</published>\n" + - " <author>\n" + + " <author>\n" + " <name>HAPI FHIR Server</name>\n" + " </author>\n" + " <entry>\n" + @@ -192,8 +191,7 @@ public class IncludedResourceStitchingClientTest { " <link rel=\"self\" href=\"http://localhost:49627/Patient?_query=extInclude&_pretty=true\"/>\n" + " <link rel=\"fhir-base\" href=\"http://localhost:49627\"/>\n" + " <os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults>\n" + - " <published>2014-08-05T15:22:08.512-04:00</published>\n" + - " <author>\n" + + " <author>\n" + " <name>HAPI FHIR Server</name>\n" + " </author>\n" + " <entry>\n" + diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/SearchTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/SearchTest.java index 5c0e3fcde54..7ed69d040bc 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/SearchTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/client/SearchTest.java @@ -63,7 +63,7 @@ public class SearchTest { @Test public void testPostOnLongParamsList() throws Exception { - String retVal = "<feed xmlns=\"http://www.w3.org/2005/Atom\"><title/><id>bc59fca7-0a8f-4caf-abef-45c8d53ece6a</id><link rel=\"self\" href=\"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Encounter?identifier=urn%3Aoid%3A1.3.6.1.4.1.12201.2%7C11410000159&_include=Encounter.participant&_include=Encounter.location.location&_include=Encounter.subject\"/><link rel=\"fhir-base\" href=\"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">1</os:totalResults><published>2014-08-08T14:46:16.497-04:00</published><author><name>HAPI FHIR Server</name></author><entry><title>Encounter 5994268http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Encounter/59942682014-08-05T12:00:11.000-04:002014-08-05T11:59:21.000-04:00
No narrative template available for resource profile: http://fhir.connectinggta.ca/Profile/encounter
Patient 5993715http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Patient/59937152014-08-08T14:46:16-04:00
Person CHA
IdentifierUHN MRN 7018614
Address100 Dundas street west
Toronto ON Can
Date of birth01 January 1988
Practitioner Practitioner/5738815http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Practitioner/57388152014-08-08T13:53:52.000-04:002009-12-04T13:43:11.000-05:00
No narrative template available for resource profile: http://hl7.org/fhir/profiles/Practitioner
Location Location/5994269http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Location/59942692014-08-08T14:46:16-04:00
No narrative template available for resource profile: http://hl7.org/fhir/profiles/Location
"; + String retVal = "<id>bc59fca7-0a8f-4caf-abef-45c8d53ece6a</id><link rel=\"self\" href=\"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Encounter?identifier=urn%3Aoid%3A1.3.6.1.4.1.12201.2%7C11410000159&_include=Encounter.participant&_include=Encounter.location.location&_include=Encounter.subject\"/><link rel=\"fhir-base\" href=\"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">1</os:totalResults><author><name>HAPI FHIR Server</name></author><entry><title>Encounter 5994268http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Encounter/59942682014-08-05T12:00:11.000-04:002014-08-05T11:59:21.000-04:00
No narrative template available for resource profile: http://fhir.connectinggta.ca/Profile/encounter
Patient 5993715http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Patient/59937152014-08-08T14:46:16-04:00
Person CHA
IdentifierUHN MRN 7018614
Address100 Dundas street west
Toronto ON Can
Date of birth01 January 1988
Practitioner Practitioner/5738815http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Practitioner/57388152014-08-08T13:53:52.000-04:002009-12-04T13:43:11.000-05:00
No narrative template available for resource profile: http://hl7.org/fhir/profiles/Practitioner
Location Location/5994269http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Location/59942692014-08-08T14:46:16-04:00
No narrative template available for resource profile: http://hl7.org/fhir/profiles/Location
"; ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); when(ourHttpClient.execute(capt.capture())).thenReturn(ourHttpResponse); @@ -97,7 +97,7 @@ public class SearchTest { @Test public void testReturnTypedList() throws Exception { - String retVal = "<id>bc59fca7-0a8f-4caf-abef-45c8d53ece6a</id><link rel=\"self\" href=\"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Encounter?identifier=urn%3Aoid%3A1.3.6.1.4.1.12201.2%7C11410000159&_include=Encounter.participant&_include=Encounter.location.location&_include=Encounter.subject\"/><link rel=\"fhir-base\" href=\"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">1</os:totalResults><published>2014-08-08T14:46:16.497-04:00</published><author><name>HAPI FHIR Server</name></author><entry><title>Encounter 5994268http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Encounter/59942682014-08-05T12:00:11.000-04:002014-08-05T11:59:21.000-04:00
No narrative template available for resource profile: http://fhir.connectinggta.ca/Profile/encounter
Patient 5993715http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Patient/59937152014-08-08T14:46:16-04:00
Person CHA
IdentifierUHN MRN 7018614
Address100 Dundas street west
Toronto ON Can
Date of birth01 January 1988
Practitioner Practitioner/5738815http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Practitioner/57388152014-08-08T13:53:52.000-04:002009-12-04T13:43:11.000-05:00
No narrative template available for resource profile: http://hl7.org/fhir/profiles/Practitioner
Location Location/5994269http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Location/59942692014-08-08T14:46:16-04:00
No narrative template available for resource profile: http://hl7.org/fhir/profiles/Location
"; + String retVal = "<id>bc59fca7-0a8f-4caf-abef-45c8d53ece6a</id><link rel=\"self\" href=\"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Encounter?identifier=urn%3Aoid%3A1.3.6.1.4.1.12201.2%7C11410000159&_include=Encounter.participant&_include=Encounter.location.location&_include=Encounter.subject\"/><link rel=\"fhir-base\" href=\"http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">1</os:totalResults><author><name>HAPI FHIR Server</name></author><entry><title>Encounter 5994268http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Encounter/59942682014-08-05T12:00:11.000-04:002014-08-05T11:59:21.000-04:00
No narrative template available for resource profile: http://fhir.connectinggta.ca/Profile/encounter
Patient 5993715http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Patient/59937152014-08-08T14:46:16-04:00
Person CHA
IdentifierUHN MRN 7018614
Address100 Dundas street west
Toronto ON Can
Date of birth01 January 1988
Practitioner Practitioner/5738815http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Practitioner/57388152014-08-08T13:53:52.000-04:002009-12-04T13:43:11.000-05:00
No narrative template available for resource profile: http://hl7.org/fhir/profiles/Practitioner
Location Location/5994269http://uhnvesb01d.uhn.on.ca:25180/uhn-fhir-service-1.2/Location/59942692014-08-08T14:46:16-04:00
No narrative template available for resource profile: http://hl7.org/fhir/profiles/Location
"; ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); when(ourHttpClient.execute(capt.capture())).thenReturn(ourHttpResponse); diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/Dstu1BundleFactoryTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/Dstu1BundleFactoryTest.java index 404564532be..d6177356b67 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/Dstu1BundleFactoryTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/Dstu1BundleFactoryTest.java @@ -20,6 +20,7 @@ import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.resource.DiagnosticReport; +import ca.uhn.fhir.model.dstu.resource.Medication; import ca.uhn.fhir.model.dstu.resource.Observation; import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Practitioner; @@ -86,6 +87,23 @@ public class Dstu1BundleFactoryTest { myBundleFactory = new Dstu1BundleFactory(ourCtx); } + @Test + public void whenMedicationHasIngredients_include_shouldIncludeThem() throws Exception { + Medication medication = new Medication(); + medication.setName("Main Medication"); + medication.setId("Medication/1"); + Medication ingredientMedication = new Medication(); + ingredientMedication.setName("Ingredient"); + ingredientMedication.setId("Medication/2"); + Medication.ProductIngredient ingredient = new Medication.ProductIngredient(); + ingredient.setItem(new ResourceReferenceDt(ingredientMedication)); + medication.getProduct().getIngredient().add(ingredient); + + myResourceList = Arrays.asList(new IBaseResource[]{medication}); + Bundle bundle = makeBundle(BundleInclusionRule.BASED_ON_INCLUDES, includes("Medication.product.ingredient.item")); + assertEquals(2, bundle.getEntries().size()); + } + @Test public void whenIncludeIsAsterisk_bundle_shouldContainAllReferencedResources() throws Exception { Bundle bundle = makeBundle(BundleInclusionRule.BASED_ON_INCLUDES, includes("*")); diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategyTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategyTest.java index 9e96a5100f4..6164f1c8b8d 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategyTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/IncomingRequestAddressStrategyTest.java @@ -1,30 +1,236 @@ package ca.uhn.fhir.rest.server; -import static org.junit.Assert.*; -import static org.mockito.Mockito.*; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHolder; +import org.junit.Before; +import org.junit.BeforeClass; import org.junit.Test; +import ca.uhn.fhir.util.RandomServerPortProvider; + public class IncomingRequestAddressStrategyTest { + private static CloseableHttpClient ourClient; + private static String ourLastBase; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(IncomingRequestAddressStrategyTest.class); + private static IncomingRequestAddressStrategy ourStrategy; + + private Server myServer; + + public void after() throws Exception { + if (myServer != null) { + myServer.stop(); + } + } + + @Before + public void before() { + ourLastBase = null; + ourStrategy = new IncomingRequestAddressStrategy(); + } + + private void httpGet(String url) throws IOException, ClientProtocolException { + ourLastBase = null; + + HttpGet httpPost = new HttpGet(url); + HttpResponse status = ourClient.execute(httpPost); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + } + + private void startServer(int port, String contextPath, String servletPath) throws Exception { + myServer = new Server(port); + + org.eclipse.jetty.servlet.ServletContextHandler proxyHandler = new org.eclipse.jetty.servlet.ServletContextHandler(); + proxyHandler.setContextPath(contextPath); + + ServletHolder handler = new ServletHolder(); + handler.setServlet(new MyServlet()); + proxyHandler.addServlet(handler, servletPath); + + myServer.setHandler(proxyHandler); + myServer.start(); + } + /** - * This is an incoming request from an instance of Tomcat on AWS, provided by - * Simon Ling of Systems Made Simple + * This is an incoming request from an instance of Tomcat on AWS, provided by Simon Ling of Systems Made Simple */ @Test public void testAwsUrl() { - + HttpServletRequest req = mock(HttpServletRequest.class); when(req.getRequestURI()).thenReturn("/FhirStorm/fhir/Patient/_search"); when(req.getServletPath()).thenReturn("/fhir"); when(req.getRequestURL()).thenReturn(new StringBuffer().append("http://fhirstorm.dyndns.org:8080/FhirStorm/fhir/Patient/_search")); when(req.getContextPath()).thenReturn("/FhirStorm"); - + IncomingRequestAddressStrategy incomingRequestAddressStrategy = new IncomingRequestAddressStrategy(); - String actual = incomingRequestAddressStrategy.determineServerBase(null,req); + String actual = incomingRequestAddressStrategy.determineServerBase(null, req); assertEquals("http://fhirstorm.dyndns.org:8080/FhirStorm/fhir", actual); } + + @Test + public void testUnderJettyWithContextPathServletRoot() throws Exception { + int port = RandomServerPortProvider.findFreePort(); + + String contextPath = "/ctx"; + String servletPath = "/*"; + + startServer(port, contextPath, servletPath); + + httpGet("http://localhost:" + port + "/ctx"); + assertEquals("http://localhost:" + port + "/ctx/", ourLastBase); + + httpGet("http://localhost:" + port + "/ctx/"); + assertEquals("http://localhost:" + port + "/ctx/", ourLastBase); + + httpGet("http://localhost:" + port + "/ctx/Patient?_pretty=true"); + assertEquals("http://localhost:" + port + "/ctx/", ourLastBase); + + httpGet("http://localhost:" + port + "/ctx/Patient/123/_history/222"); + assertEquals("http://localhost:" + port + "/ctx/", ourLastBase); + + httpGet("http://localhost:" + port); + assertEquals(null, ourLastBase); + + } + + @Test + public void testUnderJettyWithContextPathServletRootContextOnly() throws Exception { + int port = RandomServerPortProvider.findFreePort(); + + String contextPath = "/ctx"; + String servletPath = "/"; + + startServer(port, contextPath, servletPath); + ourStrategy.setServletPath(""); + + httpGet("http://localhost:" + port + "/ctx"); + assertEquals("http://localhost:" + port + "/ctx/", ourLastBase); + + httpGet("http://localhost:" + port + "/ctx/"); + assertEquals("http://localhost:" + port + "/ctx/", ourLastBase); + + httpGet("http://localhost:" + port + "/ctx/Patient?_pretty=true"); + assertEquals("http://localhost:" + port + "/ctx/", ourLastBase); + + httpGet("http://localhost:" + port + "/ctx/Patient/123/_history/222"); + assertEquals("http://localhost:" + port + "/ctx/", ourLastBase); + + httpGet("http://localhost:" + port); + assertEquals(null, ourLastBase); + + } + + @Test + public void testUnderJettyWithContextPathServletRoot2() throws Exception { + int port = RandomServerPortProvider.findFreePort(); + + String contextPath = "/ctx"; + String servletPath = "/foo/bar/*"; // not /* but still this should work + + startServer(port, contextPath, servletPath); + + httpGet("http://localhost:" + port + "/ctx/foo/bar/Patient?_pretty=true"); + assertEquals("http://localhost:" + port + "/ctx/foo/bar", ourLastBase); + + httpGet("http://localhost:" + port + "/ctx/foo/bar"); + assertEquals("http://localhost:" + port + "/ctx/foo/bar", ourLastBase); + + httpGet("http://localhost:" + port + "/ctx/foo/bar/"); + assertEquals("http://localhost:" + port + "/ctx/foo/bar", ourLastBase); + + httpGet("http://localhost:" + port + "/ctx/foo/bar/Patient/123/_history/222"); + assertEquals("http://localhost:" + port + "/ctx/foo/bar", ourLastBase); + + httpGet("http://localhost:" + port); + assertEquals(null, ourLastBase); + + } + + @Test + public void testUnderJettyWithContextPathServletPath() throws Exception { + int port = RandomServerPortProvider.findFreePort(); + + String contextPath = "/ctx"; + String servletPath = "/servlet/*"; + + startServer(port, contextPath, servletPath); + + httpGet("http://localhost:" + port); + assertEquals(null, ourLastBase); + + httpGet("http://localhost:" + port + "/ctx/servlet/"); + assertEquals("http://localhost:" + port + "/ctx/servlet", ourLastBase); + + httpGet("http://localhost:" + port + "/ctx/servlet/Patient?_pretty=true"); + assertEquals("http://localhost:" + port + "/ctx/servlet", ourLastBase); + + httpGet("http://localhost:" + port + "/ctx/servlet/Patient/123/_history/222"); + assertEquals("http://localhost:" + port + "/ctx/servlet", ourLastBase); + } + + @Test + public void testUnderJettyWithContextRootServletRoot() throws Exception { + int port = RandomServerPortProvider.findFreePort(); + + String contextPath = "/"; + String servletPath = "/*"; + + startServer(port, contextPath, servletPath); + + httpGet("http://localhost:" + port); + assertEquals("http://localhost:" + port + contextPath, ourLastBase); + + httpGet("http://localhost:" + port + "/Patient?_pretty=true"); + assertEquals("http://localhost:" + port + contextPath, ourLastBase); + + httpGet("http://localhost:" + port + "/Patient/123/_history/222"); + assertEquals("http://localhost:" + port + contextPath, ourLastBase); + } + + @BeforeClass + public static void beforeClass() { + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setConnectionManager(connectionManager); + ourClient = builder.build(); + } + + private static class MyServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException { + +// ourLog.info("Ctx: {}", theReq.) + + ourLastBase = ourStrategy.determineServerBase(getServletContext(), theReq); + theResp.setContentType("text/plain"); + theResp.getWriter().append("Success"); + theResp.getWriter().close(); + } + + } + } diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/SearchTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/SearchTest.java index 66a0ee07316..631139e28ed 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/SearchTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/SearchTest.java @@ -9,12 +9,14 @@ import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.commons.io.IOUtils; +import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.ByteArrayEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; @@ -38,10 +40,13 @@ import ca.uhn.fhir.model.dstu.resource.Observation; import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; +import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.OptionalParam; import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.StringOrListParam; @@ -233,6 +238,75 @@ public class SearchTest { assertEquals("IDAAA (identifier123)", bundle.getEntries().get(0).getTitle().getValue()); } + /** + * See #164 + */ + @Test + public void testSearchByPostWithParamsInBodyAndUrl() throws Exception { + HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient/_search?name=Central"); + + // add parameters to the post method + List parameters = new ArrayList(); + parameters.add(new BasicNameValuePair("_id", "aaa")); + + UrlEncodedFormEntity sendentity = new UrlEncodedFormEntity(parameters, "UTF-8"); + filePost.setEntity(sendentity); + + HttpResponse status = ourClient.execute(filePost); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(200, status.getStatusLine().getStatusCode()); + + Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent); + assertEquals(1, bundle.getEntries().size()); + + Patient p = bundle.getResources(Patient.class).get(0); + assertEquals("idaaa", p.getName().get(0).getFamilyAsSingleString()); + assertEquals("nameCentral", p.getName().get(1).getFamilyAsSingleString()); + + } + + /** + * See #164 + */ + @Test + public void testSearchByPostWithInvalidPostUrl() throws Exception { + HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient?name=Central"); // should end with _search + + // add parameters to the post method + List parameters = new ArrayList(); + parameters.add(new BasicNameValuePair("_id", "aaa")); + + UrlEncodedFormEntity sendentity = new UrlEncodedFormEntity(parameters, "UTF-8"); + filePost.setEntity(sendentity); + + HttpResponse status = ourClient.execute(filePost); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(400, status.getStatusLine().getStatusCode()); + assertThat(responseContent, containsString("
")); + } + + /** + * See #164 + */ + @Test + public void testSearchByPostWithMissingContentType() throws Exception { + HttpPost filePost = new HttpPost("http://localhost:" + ourPort + "/Patient?name=Central"); // should end with _search + + HttpEntity sendentity = new ByteArrayEntity(new byte[] {1,2,3,4} ); + filePost.setEntity(sendentity); + + HttpResponse status = ourClient.execute(filePost); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + assertEquals(400, status.getStatusLine().getStatusCode()); + assertThat(responseContent, containsString("
")); + } + @Test public void testSearchCompartment() throws Exception { HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/123/fooCompartment"); @@ -354,6 +428,14 @@ public class SearchTest { */ public static class DummyPatientResourceProvider implements IResourceProvider { + /** + * Only needed for #164 + */ + @Create + public MethodOutcome create(@ResourceParam Patient thePatient) { + throw new IllegalArgumentException(); + } + @Search(compartmentName = "fooCompartment") public List compartment(@IdParam IdDt theId) { ArrayList retVal = new ArrayList(); @@ -383,7 +465,7 @@ public class SearchTest { @Search - public List findPatient(@RequiredParam(name = "_id") StringParam theParam) { + public List findPatient(@RequiredParam(name = "_id") StringParam theParam, @OptionalParam(name="name") StringParam theName) { ArrayList retVal = new ArrayList(); Patient patient = new Patient(); @@ -391,6 +473,7 @@ public class SearchTest { patient.addIdentifier("system", "identifier123"); if (theParam != null) { patient.addName().addFamily("id" + theParam.getValue()); + patient.addName().addFamily("name" + theName.getValue()); } retVal.add(patient); return retVal; diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/util/RandomServerPortProvider.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/util/RandomServerPortProvider.java new file mode 100644 index 00000000000..4747b741847 --- /dev/null +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/util/RandomServerPortProvider.java @@ -0,0 +1,36 @@ +package ca.uhn.fhir.util; + +import java.io.IOException; +import java.net.ServerSocket; +import java.util.ArrayList; +import java.util.List; + +/** + * Provides server ports + */ +public class RandomServerPortProvider { + + private static List ourPorts = new ArrayList(); + + public static int findFreePort() { + ServerSocket server; + try { + server = new ServerSocket(0); + int port = server.getLocalPort(); + ourPorts.add(port); + server.close(); + Thread.sleep(500); + return port; + } catch (IOException e) { + throw new Error(e); + } catch (InterruptedException e) { + throw new Error(e); + } + } + + public static List list() { + return ourPorts; + } + +} + \ No newline at end of file diff --git a/hapi-fhir-structures-dstu/src/test/resources/mixed-return-bundle.xml b/hapi-fhir-structures-dstu/src/test/resources/mixed-return-bundle.xml index 9bee3ae5f64..3f51cb117f4 100644 --- a/hapi-fhir-structures-dstu/src/test/resources/mixed-return-bundle.xml +++ b/hapi-fhir-structures-dstu/src/test/resources/mixed-return-bundle.xml @@ -4,7 +4,6 @@ 2 - 2014-06-22T21:37:05-04:00 HAPI FHIR Server diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java index 5480b4a40dd..a4210031bb2 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/model/dstu2/FhirDstu2.java @@ -105,7 +105,7 @@ public class FhirDstu2 implements IFhirVersion { public Class getContainedType() { return ContainedDt.class; } - + @Override public BaseCodingDt newCodingDt() { return new CodingDt(); diff --git a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java index 1034d2319cd..868af554427 100644 --- a/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java +++ b/hapi-fhir-structures-dstu2/src/main/java/ca/uhn/fhir/rest/server/provider/dstu2/Dstu2BundleFactory.java @@ -276,7 +276,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { } @Override - public void initializeBundleFromResourceList(String theAuthor, List theResources, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) { + public void initializeBundleFromResourceList(String theAuthor, List theResources, String theServerBase, String theCompleteUrl, int theTotalResults, BundleTypeEnum theBundleType) { myBundle = new Bundle(); myBundle.setId(UUID.randomUUID().toString()); @@ -312,7 +312,7 @@ public class Dstu2BundleFactory implements IVersionSpecificBundleFactory { myBundle.getTotalElement().setValue(theTotalResults); } - private void addResourcesForSearch(List theResult) { + private void addResourcesForSearch(List theResult) { List includedResources = new ArrayList(); Set addedResourceIds = new HashSet(); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/primitive/BaseDateTimeDtTest.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/primitive/BaseDateTimeDtTest.java index 4ca03c140f9..c129b60fb7b 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/primitive/BaseDateTimeDtTest.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/model/primitive/BaseDateTimeDtTest.java @@ -15,12 +15,21 @@ import ca.uhn.fhir.model.api.TemporalPrecisionEnum; public class BaseDateTimeDtTest { private SimpleDateFormat myDateInstantParser; - + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseDateTimeDtTest.class); + @Before public void before() { myDateInstantParser = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); } + @Test + public void testToHumanDisplay() { + DateTimeDt dt = new DateTimeDt("2012-01-05T12:00:00-08:00"); + String human = dt.toHumanDisplay(); + ourLog.info(human); + assertEquals("Jan 5, 2012 12:00:00 PM", human); + } + /** * See HAPI #101 - https://github.com/jamesagnew/hapi-fhir/issues/101 */ diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java index 504dc280898..fb7f4dbf7a4 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/JsonParserDstu2Test.java @@ -6,6 +6,7 @@ import static org.junit.Assert.*; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.UUID; import net.sf.json.JSON; import net.sf.json.JSONSerializer; @@ -13,12 +14,14 @@ import net.sf.json.JsonConfig; import org.apache.commons.io.IOUtils; import org.hamcrest.Matchers; +import org.hamcrest.core.StringContains; import org.junit.Assert; import org.junit.Test; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.ExtensionDt; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; @@ -28,12 +31,15 @@ import ca.uhn.fhir.model.base.composite.BaseCodingDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.composite.HumanNameDt; import ca.uhn.fhir.model.dstu2.resource.Binary; +import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport; import ca.uhn.fhir.model.dstu2.resource.Medication; import ca.uhn.fhir.model.dstu2.resource.MedicationPrescription; import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.dstu2.resource.QuestionnaireAnswers; +import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum; +import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum; import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum; import ca.uhn.fhir.model.dstu2.valueset.ObservationReliabilityEnum; import ca.uhn.fhir.model.dstu2.valueset.ObservationStatusEnum; @@ -80,6 +86,36 @@ public class JsonParserDstu2Test { } + /** + * See #163 + */ + @Test + public void testParseResourceType() { + IParser jsonParser = ourCtx.newJsonParser().setPrettyPrint(true); + + // Patient + Patient patient = new Patient(); + String patientId = UUID.randomUUID().toString(); + patient.setId(new IdDt("Patient", patientId)); + patient.addName().addGiven("John").addFamily("Smith"); + patient.setGender(AdministrativeGenderEnum.MALE); + patient.setBirthDate(new DateDt("1987-04-16")); + + // Bundle + ca.uhn.fhir.model.dstu2.resource.Bundle bundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + bundle.setType(BundleTypeEnum.COLLECTION); + bundle.addEntry().setResource(patient); + + String bundleText = jsonParser.encodeResourceToString(bundle); + ourLog.info(bundleText); + + ca.uhn.fhir.model.dstu2.resource.Bundle reincarnatedBundle = jsonParser.parseResource (ca.uhn.fhir.model.dstu2.resource.Bundle.class, bundleText); + Patient reincarnatedPatient = reincarnatedBundle.getAllPopulatedChildElementsOfType(Patient.class).get(0); + + assertEquals("Patient", patient.getId().getResourceType()); + assertEquals("Patient", reincarnatedPatient.getId().getResourceType()); + } + /** * See #144 and #146 */ @@ -92,7 +128,7 @@ public class JsonParserDstu2Test { obsv.setStatus(ObservationStatusEnum.FINAL); obsv.setReliability(ObservationReliabilityEnum.OK); obsv.addIdentifier().setSystem("System").setValue("id value"); - + DiagnosticReport report = new DiagnosticReport(); report.getContained().getContainedResources().add(obsv); report.addResult().setResource(obsv); @@ -103,6 +139,46 @@ public class JsonParserDstu2Test { Assert.assertThat(message, containsString("contained")); } + @Test + public void testEncodeBundleOldBundleNoText() { + + Bundle b = new Bundle(); + + BundleEntry e = b.addEntry(); + e.setResource(new Patient()); + b.addCategory("scheme", "term", "label"); + + String val = new FhirContext().newJsonParser().setPrettyPrint(false).encodeBundleToString(b); + ourLog.info(val); + + assertThat(val, not(containsString("text"))); + + b = new FhirContext().newJsonParser().parseBundle(val); + assertEquals(1, b.getEntries().size()); + + } + + @Test + public void testEncodeBundleNewBundleNoText() { + + ca.uhn.fhir.model.dstu2.resource.Bundle b = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + b.getText().setDiv(""); + b.getText().getStatus().setValueAsString(""); + ; + + Entry e = b.addEntry(); + e.setResource(new Patient()); + + String val = new FhirContext().newJsonParser().setPrettyPrint(false).encodeResourceToString(b); + ourLog.info(val); + assertThat(val, not(containsString("text"))); + + val = new FhirContext().newXmlParser().setPrettyPrint(false).encodeResourceToString(b); + ourLog.info(val); + assertThat(val, not(containsString("text"))); + + } + /** * See #144 and #146 */ @@ -115,10 +191,10 @@ public class JsonParserDstu2Test { obsv.setStatus(ObservationStatusEnum.FINAL); obsv.setReliability(ObservationReliabilityEnum.OK); obsv.addIdentifier().setSystem("System").setValue("id value"); - + DiagnosticReport report = new DiagnosticReport(); report.getContained().getContainedResources().add(obsv); - + obsv.setId("#123"); report.addResult().setReference("#123"); @@ -497,10 +573,10 @@ public class JsonParserDstu2Test { String content = IOUtils.toString(JsonParserDstu2Test.class.getResourceAsStream("/bundle-example2.xml")); ca.uhn.fhir.model.dstu2.resource.Bundle parsed = ourCtx.newXmlParser().parseResource(ca.uhn.fhir.model.dstu2.resource.Bundle.class, content); - + MedicationPrescription p = (MedicationPrescription) parsed.getEntry().get(0).getResource(); assertEquals("#med", p.getMedication().getReference().getValue()); - + Medication m = (Medication) p.getMedication().getResource(); assertNotNull(m); assertEquals("#med", m.getId().getValue()); @@ -516,7 +592,6 @@ public class JsonParserDstu2Test { assertThat(reencoded, containsString("contained")); } - @Test public void testParseAndEncodeBundle() throws Exception { String content = IOUtils.toString(JsonParserDstu2Test.class.getResourceAsStream("/bundle-example.json")); @@ -542,7 +617,7 @@ public class JsonParserDstu2Test { Medication m = (Medication) parsed.getEntries().get(1).getResource(); assertEquals("http://example.com/base/Medication/example", m.getId().getValue()); assertSame(p.getMedication().getResource(), m); - + String reencoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeBundleToString(parsed); ourLog.info(reencoded); @@ -566,7 +641,7 @@ public class JsonParserDstu2Test { String content = IOUtils.toString(JsonParserDstu2Test.class.getResourceAsStream("/bundle-example.json")); Bundle parsed = ourCtx.newJsonParser().parseBundle(content); - + assertEquals(new InstantDt("2014-08-18T01:43:30Z"), parsed.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED)); assertEquals("searchset", parsed.getType().getValue()); assertEquals(3, parsed.getTotalResults().getValue().intValue()); @@ -583,7 +658,6 @@ public class JsonParserDstu2Test { assertEquals("Medication/example", p.getMedication().getReference().getValue()); assertSame(p.getMedication().getResource(), m); - String reencoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeBundleToString(parsed); ourLog.info(reencoded); diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2Test.java index d701f863529..98a296fa12f 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/parser/XmlParserDstu2Test.java @@ -53,6 +53,8 @@ import ca.uhn.fhir.model.dstu2.resource.MedicationPrescription; import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.model.dstu2.resource.Organization; import ca.uhn.fhir.model.dstu2.resource.Patient; +import ca.uhn.fhir.model.dstu2.valueset.AdministrativeGenderEnum; +import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum; import ca.uhn.fhir.model.dstu2.valueset.DocumentReferenceStatusEnum; import ca.uhn.fhir.model.dstu2.valueset.IdentifierUseEnum; import ca.uhn.fhir.model.primitive.DateDt; @@ -471,6 +473,38 @@ public class XmlParserDstu2Test { assertEquals(htmlNoNs, p.getText().getDiv().getValueAsString()); } + + /** + * See #163 + */ + @Test + public void testParseResourceType() { + IParser xmlParser = ourCtx.newXmlParser().setPrettyPrint(true); + + // Patient + Patient patient = new Patient(); + String patientId = UUID.randomUUID().toString(); + patient.setId(new IdDt("Patient", patientId)); + patient.addName().addGiven("John").addFamily("Smith"); + patient.setGender(AdministrativeGenderEnum.MALE); + patient.setBirthDate(new DateDt("1987-04-16")); + + // Bundle + ca.uhn.fhir.model.dstu2.resource.Bundle bundle = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + bundle.setType(BundleTypeEnum.COLLECTION); + bundle.addEntry().setResource(patient); + + String bundleText = xmlParser.encodeResourceToString(bundle); + ourLog.info(bundleText); + + ca.uhn.fhir.model.dstu2.resource.Bundle reincarnatedBundle = xmlParser.parseResource (ca.uhn.fhir.model.dstu2.resource.Bundle.class, bundleText); + Patient reincarnatedPatient = reincarnatedBundle.getAllPopulatedChildElementsOfType(Patient.class).get(0); + + assertEquals("Patient", patient.getId().getResourceType()); + assertEquals("Patient", reincarnatedPatient.getId().getResourceType()); + } + + /** * Thanks to Alexander Kley! */ diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu2.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu2.java index f420d038793..954ac564863 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu2.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/ClientServerValidationTestDstu2.java @@ -12,6 +12,7 @@ import java.io.StringReader; import java.nio.charset.Charset; import org.apache.commons.io.input.ReaderInputStream; +import org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.ProtocolVersion; import org.apache.http.client.HttpClient; @@ -31,10 +32,11 @@ import ca.uhn.fhir.model.dstu2.resource.Conformance; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException; +import ca.uhn.fhir.rest.client.interceptor.BasicAuthInterceptor; import ca.uhn.fhir.rest.server.Constants; public class ClientServerValidationTestDstu2 { - + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ClientServerValidationTestDstu2.class); private FhirContext myCtx; private HttpClient myHttpClient; @@ -65,20 +67,21 @@ public class ClientServerValidationTestDstu2 { @Override public InputStream answer(InvocationOnMock theInvocation) throws Throwable { if (myFirstResponse) { - myFirstResponse=false; + myFirstResponse = false; return new ReaderInputStream(new StringReader(confResource), Charset.forName("UTF-8")); } else { return new ReaderInputStream(new StringReader(myCtx.newXmlParser().encodeResourceToString(new Patient())), Charset.forName("UTF-8")); } - }}); + } + }); when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); - myCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.ONCE); + myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); IGenericClient client = myCtx.newRestfulGenericClient("http://foo"); - - // don't load the conformance until the first time the client is actually used - assertTrue(myFirstResponse); + + // don't load the conformance until the first time the client is actually used + assertTrue(myFirstResponse); client.read(new UriDt("http://foo/Patient/123")); assertFalse(myFirstResponse); myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123")); @@ -102,20 +105,21 @@ public class ClientServerValidationTestDstu2 { @Override public InputStream answer(InvocationOnMock theInvocation) throws Throwable { if (myFirstResponse) { - myFirstResponse=false; + myFirstResponse = false; return new ReaderInputStream(new StringReader(confResource), Charset.forName("UTF-8")); } else { return new ReaderInputStream(new StringReader(myCtx.newXmlParser().encodeResourceToString(new Patient())), Charset.forName("UTF-8")); } - }}); + } + }); when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); - myCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.ONCE); + myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); IGenericClient client = myCtx.newRestfulGenericClient("http://foo"); - - // don't load the conformance until the first time the client is actually used - assertTrue(myFirstResponse); + + // don't load the conformance until the first time the client is actually used + assertTrue(myFirstResponse); client.read(new UriDt("http://foo/Patient/123")); assertFalse(myFirstResponse); myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123")); @@ -139,7 +143,7 @@ public class ClientServerValidationTestDstu2 { when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); - myCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.ONCE); + myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); try { myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123")); fail(); @@ -151,4 +155,95 @@ public class ClientServerValidationTestDstu2 { assertThat(out, containsString(want)); } } + + @Test + public void testClientUsesInterceptors() throws Exception { + Conformance conf = new Conformance(); + conf.setFhirVersion("0.5.0"); + final String confResource = myCtx.newXmlParser().encodeResourceToString(conf); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { + @Override + public InputStream answer(InvocationOnMock theInvocation) throws Throwable { + if (myFirstResponse) { + myFirstResponse = false; + return new ReaderInputStream(new StringReader(confResource), Charset.forName("UTF-8")); + } else { + Patient resource = new Patient(); + resource.addName().addFamily().setValue("FAM"); + return new ReaderInputStream(new StringReader(myCtx.newXmlParser().encodeResourceToString(resource)), Charset.forName("UTF-8")); + } + } + }); + + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + + myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); + IGenericClient client = myCtx.newRestfulGenericClient("http://foo"); + client.registerInterceptor(new BasicAuthInterceptor("USER", "PASS")); + Patient pt = (Patient) client.read(new UriDt("http://foo/Patient/123")); + assertEquals("FAM", pt.getNameFirstRep().getFamilyAsSingleString()); + + assertEquals(2, capt.getAllValues().size()); + + Header auth = capt.getAllValues().get(0).getFirstHeader("Authorization"); + assertNotNull(auth); + assertEquals("Basic VVNFUjpQQVNT", auth.getValue()); + auth = capt.getAllValues().get(1).getFirstHeader("Authorization"); + assertNotNull(auth); + assertEquals("Basic VVNFUjpQQVNT", auth.getValue()); + } + + @Test + public void testForceConformanceCheck() throws Exception { + Conformance conf = new Conformance(); + conf.setFhirVersion("0.5.0"); + final String confResource = myCtx.newXmlParser().encodeResourceToString(conf); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { + @Override + public InputStream answer(InvocationOnMock theInvocation) throws Throwable { + if (myFirstResponse) { + myFirstResponse = false; + return new ReaderInputStream(new StringReader(confResource), Charset.forName("UTF-8")); + } else { + Patient resource = new Patient(); + resource.addName().addFamily().setValue("FAM"); + return new ReaderInputStream(new StringReader(myCtx.newXmlParser().encodeResourceToString(resource)), Charset.forName("UTF-8")); + } + } + }); + + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + + myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE); + + IGenericClient client = myCtx.newRestfulGenericClient("http://foo"); + client.registerInterceptor(new BasicAuthInterceptor("USER", "PASS")); + + client.forceConformanceCheck(); + + assertEquals(1, capt.getAllValues().size()); + + Patient pt = (Patient) client.read(new UriDt("http://foo/Patient/123")); + assertEquals("FAM", pt.getNameFirstRep().getFamilyAsSingleString()); + + assertEquals(2, capt.getAllValues().size()); + + Header auth = capt.getAllValues().get(0).getFirstHeader("Authorization"); + assertNotNull(auth); + assertEquals("Basic VVNFUjpQQVNT", auth.getValue()); + auth = capt.getAllValues().get(1).getFirstHeader("Authorization"); + assertNotNull(auth); + assertEquals("Basic VVNFUjpQQVNT", auth.getValue()); + } + } diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java index 94f51f5d11c..63ead91b095 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/client/GenericClientDstu2Test.java @@ -8,6 +8,7 @@ import static org.mockito.Mockito.when; import java.io.IOException; import java.io.InputStream; +import java.io.Reader; import java.io.StringReader; import java.nio.charset.Charset; import java.util.ArrayList; @@ -36,6 +37,7 @@ 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.api.Include; +import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.model.dstu2.resource.Parameters; import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.model.primitive.IdDt; @@ -616,6 +618,64 @@ public class GenericClientDstu2Test { // assertEquals("PATIENT2", p2.getName().get(0).getFamily().get(0).getValue()); } + + @Test + public void testTransactionWithString() throws Exception { + + ca.uhn.fhir.model.dstu2.resource.Bundle req = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + req.addEntry().setResource(new Patient()); + req.addEntry().setResource(new Observation()); + String reqStringJson = ourCtx.newJsonParser().encodeResourceToString(req); + String reqStringXml = ourCtx.newXmlParser().encodeResourceToString(req); + + ca.uhn.fhir.model.dstu2.resource.Bundle resp = new ca.uhn.fhir.model.dstu2.resource.Bundle(); + resp.addEntry().getTransactionResponse().setLocation("Patient/1/_history/1"); + resp.addEntry().getTransactionResponse().setLocation("Patient/2/_history/2"); + final String respStringJson = ourCtx.newJsonParser().encodeResourceToString(resp); + + ArgumentCaptor capt = ArgumentCaptor.forClass(HttpUriRequest.class); + when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse); + when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK")); + when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8")); + when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer() { + @Override + public InputStream answer(InvocationOnMock theInvocation) throws Throwable { + return new ReaderInputStream(new StringReader(respStringJson), Charset.forName("UTF-8")); + }}); + + IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir"); + + //@formatter:off + String response = client.transaction() + .withBundle(reqStringJson) + .execute(); + //@formatter:on + + assertEquals("http://example.com/fhir/", capt.getValue().getURI().toString()); + assertEquals(respStringJson, response); + String requestString = IOUtils.toString(((HttpEntityEnclosingRequest) capt.getValue()).getEntity().getContent()); + IOUtils.closeQuietly(((HttpEntityEnclosingRequest) capt.getValue()).getEntity().getContent()); + assertEquals(reqStringJson, requestString); + assertEquals("application/json+fhir; charset=UTF-8", capt.getValue().getFirstHeader("Content-Type").getValue()); + + //@formatter:off + response = client.transaction() + .withBundle(reqStringJson) + .encodedXml() + .execute(); + //@formatter:on + + assertEquals("http://example.com/fhir/?_format=xml", capt.getValue().getURI().toString()); + assertEquals(respStringJson, response); + requestString = IOUtils.toString(((HttpEntityEnclosingRequest) capt.getValue()).getEntity().getContent()); + IOUtils.closeQuietly(((HttpEntityEnclosingRequest) capt.getValue()).getEntity().getContent()); + assertEquals(reqStringXml, requestString); + assertEquals("application/xml+fhir; charset=UTF-8", capt.getValue().getFirstHeader("Content-Type").getValue()); + + } + + + @Test public void testTransactionWithTransactionResource() throws Exception { diff --git a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java index 3976340b623..6344f6fbdb4 100644 --- a/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java +++ b/hapi-fhir-structures-dstu2/src/test/java/ca/uhn/fhir/rest/server/SearchDstu2Test.java @@ -1,6 +1,8 @@ package ca.uhn.fhir.rest.server; -import static org.junit.Assert.assertEquals; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.*; import java.util.concurrent.TimeUnit; @@ -44,13 +46,32 @@ public class SearchDstu2Test { String responseContent = IOUtils.toString(status.getEntity().getContent()); IOUtils.closeQuietly(status.getEntity().getContent()); ourLog.info(responseContent); + + assertThat(responseContent, not(containsString("text"))); assertEquals(200, status.getStatusLine().getStatusCode()); Patient patient = (Patient) ourCtx.newXmlParser().parseResource(Bundle.class, responseContent).getEntry().get(0).getResource(); String ref = patient.getManagingOrganization().getReference().getValue(); assertEquals("Organization/555", ref); + assertNull(status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION)); } + @Test + public void testEncodeConvertsReferencesToRelativeJson() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef&_format=json"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + ourLog.info(responseContent); + + assertThat(responseContent, not(containsString("text"))); + + assertEquals(200, status.getStatusLine().getStatusCode()); + Patient patient = (Patient) ourCtx.newJsonParser().parseResource(Bundle.class, responseContent).getEntry().get(0).getResource(); + String ref = patient.getManagingOrganization().getReference().getValue(); + assertEquals("Organization/555", ref); + assertNull(status.getFirstHeader(Constants.HEADER_CONTENT_LOCATION)); + } @AfterClass public static void afterClass() throws Exception { @@ -66,7 +87,6 @@ public class SearchDstu2Test { ServletHandler proxyHandler = new ServletHandler(); RestfulServer servlet = new RestfulServer(); - servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); servlet.setResourceProviders(patientProvider); ServletHolder servletHolder = new ServletHolder(servlet); diff --git a/hapi-fhir-testpage-interceptor/pom.xml b/hapi-fhir-testpage-interceptor/pom.xml index f0e9df7d4da..5c66362e457 100644 --- a/hapi-fhir-testpage-interceptor/pom.xml +++ b/hapi-fhir-testpage-interceptor/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 0.9-SNAPSHOT + 1.0-SNAPSHOT ../pom.xml @@ -27,7 +27,7 @@ ca.uhn.hapi.fhir hapi-fhir-base - 0.9-SNAPSHOT + 1.0-SNAPSHOT org.thymeleaf @@ -38,7 +38,7 @@ javax.servlet javax.servlet-api - 3.1.0 + ${servlet_api_version} provided diff --git a/hapi-fhir-testpage-overlay/.gitignore b/hapi-fhir-testpage-overlay/.gitignore index 84c048a73cc..0c06d203331 100644 --- a/hapi-fhir-testpage-overlay/.gitignore +++ b/hapi-fhir-testpage-overlay/.gitignore @@ -1 +1,128 @@ +/target/ + +# Created by https://www.gitignore.io + +### Java ### +*.class + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties + + +### Vim ### +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ + + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm + +*.iml + +## Directory-based project format: +.idea/ +# if you remove the above rule, at least ignore the following: + +# User-specific stuff: +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/dictionaries + +# Sensitive or high-churn files: +# .idea/dataSources.ids +# .idea/dataSources.xml +# .idea/sqlDataSources.xml +# .idea/dynamic.xml +# .idea/uiDesigner.xml + +# Gradle: +# .idea/gradle.xml +# .idea/libraries + +# Mongo Explorer plugin: +# .idea/mongoSettings.xml + +## File-based project format: +*.ipr +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties + + + +### Eclipse ### +*.pydevproject +.metadata +.gradle +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath + +# Eclipse Core +.project + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# PDT-specific +.buildpath + +# sbteclipse plugin +.target + +# TeXlipse plugin +.texlipse + /build/ diff --git a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java index e4ee3857861..5ac3dfbcd18 100644 --- a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java +++ b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/Controller.java @@ -1203,7 +1203,7 @@ public class Controller { private String parseNarrative(HomeRequest theRequest, EncodingEnum theCtEnum, String theResultBody) { try { - IResource resource = theCtEnum.newParser(getContext(theRequest)).parseResource(theResultBody); + IResource resource = (IResource) theCtEnum.newParser(getContext(theRequest)).parseResource(theResultBody); String retVal = resource.getText().getDiv().getValueAsString(); return StringUtils.defaultString(retVal); } catch (Exception e) { diff --git a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/TesterConfig.java b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/TesterConfig.java index 21dc44238df..f500a4e0f78 100644 --- a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/TesterConfig.java +++ b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/TesterConfig.java @@ -9,15 +9,21 @@ import org.apache.commons.lang3.Validate; import org.springframework.beans.factory.annotation.Required; import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.util.ITestingUiClientFactory; public class TesterConfig { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TesterConfig.class); public static final String SYSPROP_FORCE_SERVERS = "ca.uhn.fhir.to.TesterConfig_SYSPROP_FORCE_SERVERS"; - private LinkedHashMap myIdToServerName = new LinkedHashMap(); + private ITestingUiClientFactory myClientFactory; private LinkedHashMap myIdToFhirVersion = new LinkedHashMap(); private LinkedHashMap myIdToServerBase = new LinkedHashMap(); + private LinkedHashMap myIdToServerName = new LinkedHashMap(); + + public ITestingUiClientFactory getClientFactory() { + return myClientFactory; + } public boolean getDebugTemplatesMode() { return true; @@ -35,6 +41,10 @@ public class TesterConfig { return myIdToServerName; } + public void setClientFactory(ITestingUiClientFactory theClientFactory) { + myClientFactory = theClientFactory; + } + @Required public void setServers(List theServers) { List servers = theServers; diff --git a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/model/HomeRequest.java b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/model/HomeRequest.java index 1bfe9011c9c..de8d125de98 100644 --- a/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/model/HomeRequest.java +++ b/hapi-fhir-testpage-overlay/src/main/java/ca/uhn/fhir/to/model/HomeRequest.java @@ -13,6 +13,7 @@ import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.rest.client.GenericClient; import ca.uhn.fhir.rest.client.IClientInterceptor; +import ca.uhn.fhir.rest.client.ServerValidationModeEnum; import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.IncomingRequestAddressStrategy; import ca.uhn.fhir.to.Controller; @@ -49,7 +50,9 @@ public class HomeRequest { } if (retVal.contains("${serverBase}")) { - String base = new IncomingRequestAddressStrategy().determineServerBase(theRequest.getServletContext(), theRequest); + IncomingRequestAddressStrategy strategy = new IncomingRequestAddressStrategy(); + strategy.setServletPath(""); + String base = strategy.determineServerBase(theRequest.getServletContext(), theRequest); if (base.endsWith("/")) { base = base.substring(0, base.length() - 1); } @@ -108,6 +111,8 @@ public class HomeRequest { } public GenericClient newClient(HttpServletRequest theRequest, FhirContext theContext, TesterConfig theConfig, Controller.CaptureInterceptor theInterceptor) { + theContext.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.NEVER); + GenericClient retVal = (GenericClient) theContext.newRestfulGenericClient(getServerBase(theRequest, theConfig)); retVal.setKeepResponses(true); diff --git a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/home.html b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/home.html index 5c6809b8f1b..13794d68e9c 100644 --- a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/home.html +++ b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/home.html @@ -127,7 +127,8 @@ $('#server-history-datetime').datetimepicker({ sideBySide: true, use24hours: true, - showToday: true + showToday: true, + keepInvalid: true }); }); $('#server-history-btn').click( diff --git a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/resource.html b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/resource.html index 9e12f8d0b7d..d47273e4474 100644 --- a/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/resource.html +++ b/hapi-fhir-testpage-overlay/src/main/webapp/WEB-INF/templates/resource.html @@ -326,7 +326,8 @@ $('#resource-history-datetime').datetimepicker({ sideBySide: true, use24hours: true, - showToday: true + showToday: true, + keepInvalid: true }); }); $('#resource-history-btn').click( @@ -432,7 +433,7 @@ if (id != null) btn.append($('', { type: 'hidden', name: 'resource-create-id', value: id })); var body = $('#resource-create-body').val(); btn.append($('', { type: 'hidden', name: 'resource-create-body', value: body })); - $("#outerForm").attr("action", "create").submit(); + $("#outerForm").attr("action", "create").attr("method", "POST").submit(); }); $( document ).ready(function() { if ($('#resource-create-id').val() != "") { @@ -498,7 +499,7 @@ if (id != null) btn.append($('', { type: 'hidden', name: 'resource-create-id', value: id })); var body = $('#resource-update-body').val(); btn.append($('', { type: 'hidden', name: 'resource-create-body', value: body })); - $("#outerForm").attr("action", "update").submit(); + $("#outerForm").attr("action", "update").attr("method", "POST").submit(); }); $( document ).ready(function() { if ($('#resource-update-id').val() != "") { @@ -549,7 +550,7 @@ btn.button('loading'); var body = $('#resource-validate-body').val(); btn.append($('', { type: 'hidden', name: 'resource-validate-body', value: body })); - $("#outerForm").attr("action", "validate").submit(); + $("#outerForm").attr("action", "validate").attr("method", "POST").submit(); }); diff --git a/hapi-fhir-testpage-overlay/src/main/webapp/css/tester.css b/hapi-fhir-testpage-overlay/src/main/webapp/css/tester.css index 697eac63c2e..ab1245eeb49 100644 --- a/hapi-fhir-testpage-overlay/src/main/webapp/css/tester.css +++ b/hapi-fhir-testpage-overlay/src/main/webapp/css/tester.css @@ -11,8 +11,8 @@ fieldset[disabled].btn { /* Move down content because we have a fixed navbar that is 50px tall */ body { - padding-top: 50px; - overflow-x: hidden; + padding-top: 50px; + overflow-x: hidden; } .clientCodeBox @@ -145,6 +145,7 @@ PRE.resultBodyPre { background-color: transparent; overflow: visible; /*white-space: normal;*/ + white-space: pre-wrap; } /* @@ -152,16 +153,16 @@ PRE.resultBodyPre { */ .sub-header { - padding-bottom: 10px; - border-bottom: 1px solid #eee; + padding-bottom: 10px; + border-bottom: 1px solid #eee; } body .syntaxhighlighter .line { - white-space: pre-wrap !important; /* make code wrap */ + white-space: pre-wrap !important; /* make code wrap */ } .syntaxhighlight { - white-space: pre-wrap; + white-space: pre-wrap; } /* @@ -217,14 +218,14 @@ body .syntaxhighlighter .line { padding: 20px; } @media (min-width: 768px) { - .main { - padding-top: 10px; - padding-right: 10px; - padding-left: 10px; - } + .main { + padding-top: 10px; + padding-right: 10px; + padding-left: 10px; + } } .main .page-header { - margin-top: 0; + margin-top: 0; } .navBarButtonLabel { @@ -236,18 +237,18 @@ body .syntaxhighlighter .line { */ .placeholders { - margin-bottom: 30px; - text-align: center; + margin-bottom: 30px; + text-align: center; } .placeholders h4 { - margin-bottom: 0; + margin-bottom: 0; } .placeholder { - margin-bottom: 20px; + margin-bottom: 20px; } .placeholder img { - display: inline-block; - border-radius: 50%; + display: inline-block; + border-radius: 50%; } DIV.queryParameter { diff --git a/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/OverlayTestApp.java b/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/OverlayTestApp.java index f7a5833fbc4..04af9b1be2c 100644 --- a/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/OverlayTestApp.java +++ b/hapi-fhir-testpage-overlay/src/test/java/ca/uhn/fhir/jpa/test/OverlayTestApp.java @@ -129,7 +129,7 @@ public class OverlayTestApp { client.create(p1); List resources = restServerDstu1.getFhirContext().newJsonParser().parseBundle(IOUtils.toString(OverlayTestApp.class.getResourceAsStream("/test-server-seed-bundle.json"))).toListOfResources(); - client.transaction(resources); + client.transaction().withResources(resources).execute(); client.create(p1); client.create(p1); diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/.gitignore b/hapi-fhir-tutorial/jpaserver-example-with-custom/.gitignore new file mode 100644 index 00000000000..b834fa249bd --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/.gitignore @@ -0,0 +1 @@ +/jpaserver_derby_files diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/pom.xml b/hapi-fhir-tutorial/jpaserver-example-with-custom/pom.xml new file mode 100644 index 00000000000..93bdd026358 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/pom.xml @@ -0,0 +1,231 @@ + + 4.0.0 + ca.uhn.hapi.fhir + hapi-fhir-jpaserver-example-with-custom + 0.0.1-SNAPSHOT + + HAPI FHIR JPA Server - Example with Custom Resources + + + + oss-snapshots + + true + + https://oss.sonatype.org/content/repositories/snapshots/ + + + + + + + + ca.uhn.hapi.fhir + hapi-fhir-base + 1.0-SNAPSHOT + + + + + ca.uhn.hapi.fhir + hapi-fhir-structures-dstu2 + 1.0-SNAPSHOT + + + + + ca.uhn.hapi.fhir + hapi-fhir-jpaserver-base + 1.0-SNAPSHOT + + + + + ca.uhn.hapi.fhir + hapi-fhir-testpage-overlay + 1.0-SNAPSHOT + war + provided + + + + + ch.qos.logback + logback-classic + 1.1.2 + + + + + javax.servlet + javax.servlet-api + 3.0.1 + provided + + + + + org.thymeleaf + thymeleaf + 2.1.4.RELEASE + + + + + org.ebaysf.web + cors-filter + 1.0.1 + + + servlet-api + javax.servlet + + + + + + + org.springframework + spring-web + 4.1.6.RELEASE + provided + + + org.springframework + spring-webmvc + 4.1.6.RELEASE + + + + + org.apache.commons + commons-dbcp2 + 2.0.1 + + + + + + com.fasterxml.jackson.core + jackson-core + 2.5.2 + + + com.fasterxml.jackson.core + jackson-annotations + 2.5.2 + + + com.fasterxml.jackson.core + jackson-databind + 2.5.2 + + + + + org.apache.derby + derby + 10.11.1.1 + + + org.apache.derby + derbynet + 10.11.1.1 + + + org.apache.derby + derbyclient + 10.11.1.1 + + + + + junit + junit + 4.12 + test + + + org.hamcrest + java-hamcrest + 2.0.0.0 + test + + + + + + + + hapi-fhir-jpaserver-example + + + + + + org.eclipse.jetty + jetty-maven-plugin + 9.1.1.v20140108 + + + /hapi-fhir-jpaserver-example-advanced + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.2 + + 1.7 + 1.7 + + + + + + org.apache.maven.plugins + maven-war-plugin + + + + ca.uhn.hapi.fhir + hapi-fhir-testpage-overlay + + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + + + + + diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/DerbyInit.java b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/DerbyInit.java new file mode 100644 index 00000000000..3326fb1d274 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/DerbyInit.java @@ -0,0 +1,9 @@ +package ca.uhn.fhir.jpa.demo; + +public class DerbyInit { + + public DerbyInit() throws ClassNotFoundException { + Class.forName("org.apache.derby.jdbc.ClientDriver"); + } + +} diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java new file mode 100644 index 00000000000..e64b6d8ed27 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -0,0 +1,117 @@ +package ca.uhn.fhir.jpa.demo; + +import java.util.List; + +import javax.servlet.ServletException; + +import ca.uhn.fhir.context.FhirVersionEnum; + +import org.springframework.web.context.ContextLoaderListener; +import org.springframework.web.context.WebApplicationContext; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.dao.IFhirSystemDao; +import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu1; +import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; +import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu1; +import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.dstu2.resource.Bundle; +import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; +import ca.uhn.fhir.rest.server.ETagSupportEnum; +import ca.uhn.fhir.rest.server.EncodingEnum; +import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider; +import ca.uhn.fhir.rest.server.IResourceProvider; +import ca.uhn.fhir.rest.server.RestfulServer; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; +import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor; + +public class JpaServerDemo extends RestfulServer { + + private static final long serialVersionUID = 1L; + + private WebApplicationContext myAppCtx; + + @SuppressWarnings("unchecked") + @Override + protected void initialize() throws ServletException { + super.initialize(); + + /* + * We want to support FHIR DSTU2 format. This means that the server + * will use the DSTU2 bundle format and other DSTU2 encoding changes. + * + * If you want to use DSTU1 instead, change the following line, and change the 2 occurrences of dstu2 in web.xml to dstu1 + */ + FhirVersionEnum fhirVersion = FhirVersionEnum.DSTU2; + setFhirContext(new FhirContext(fhirVersion)); + + // Get the spring context from the web container (it's declared in web.xml) + myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext(); + + /* + * The hapi-fhir-server-resourceproviders-dev.xml file is a spring configuration + * file which is automatically generated as a part of hapi-fhir-jpaserver-base and + * contains bean definitions for a resource provider for each resource type + */ + List beans = myAppCtx.getBean("myResourceProvidersDstu2", List.class); + setResourceProviders(beans); + + /* + * The system provider implements non-resource-type methods, such as + * transaction, and global history. + */ + Object systemProvider = myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class); + setPlainProviders(systemProvider); + + /* + * The conformance provider exports the supported resources, search parameters, etc for + * this server. The JPA version adds resource counts to the exported statement, so it + * is a nice addition. + */ + IFhirSystemDao systemDao = myAppCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class); + JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao); + confProvider.setImplementationDescription("Example Server"); + setServerConformanceProvider(confProvider); + + /* + * Enable ETag Support (this is already the default) + */ + setETagSupport(ETagSupportEnum.ENABLED); + + /* + * This tells the server to use "browser friendly" MIME types if it + * detects that the request is coming from a browser, in the hopes that the + * browser won't just treat the content as a binary payload and try + * to download it (which is what generally happens if you load a + * FHIR URL in a browser). + * + * This means that the server isn't technically complying with the + * FHIR specification for direct browser requests, but this mode + * is very helpful for testing and troubleshooting since it means + * you can look at FHIR URLs directly in a browser. + */ + setUseBrowserFriendlyContentTypes(true); + + /* + * Default to XML and pretty printing + */ + setDefaultPrettyPrint(true); + setDefaultResponseEncoding(EncodingEnum.JSON); + + /* + * This is a simple paging strategy that keeps the last 10 searches in memory + */ + setPagingProvider(new FifoMemoryPagingProvider(10)); + + /* + * Load interceptors for the server from Spring (these are defined in hapi-fhir-server-config.xml + */ + List interceptorBeans = myAppCtx.getBean("myServerInterceptors", List.class); + for (IServerInterceptor interceptor : interceptorBeans) { + this.registerInterceptor(interceptor); + } + + } + +} diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/entity/User.java b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/entity/User.java new file mode 100644 index 00000000000..9fbbefc3e9d --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/entity/User.java @@ -0,0 +1,75 @@ +package ca.uhn.fhir.jpa.demo.entity; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2015 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import java.io.Serializable; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; + +//@formatter:off +@Entity() +@Table(name = "CUST_USER") +//@formatter:on +public class User implements Serializable { + + private static final long serialVersionUID = 1L; + + @GeneratedValue(strategy = GenerationType.AUTO) + @Id + @Column(name = "PID") + private Long myId; + + @Column(name = "PASSWORD", length = 200) + private String myPassword; + + @Column(name = "USERNAME", length = 200) + private String myUsername; + + public Long getId() { + return myId; + } + + public String getPassword() { + return myPassword; + } + + public String getUsername() { + return myUsername; + } + + public void setId(Long theId) { + myId = theId; + } + + public void setPassword(String thePassword) { + myPassword = thePassword; + } + + public void setUsername(String theUsername) { + myUsername = theUsername; + } + +} diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/interceptor/RequestAuthorizationInterceptor.java b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/interceptor/RequestAuthorizationInterceptor.java new file mode 100644 index 00000000000..d3690744bae --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/interceptor/RequestAuthorizationInterceptor.java @@ -0,0 +1,47 @@ +package ca.uhn.fhir.jpa.demo.interceptor; + +import javax.persistence.EntityManager; +import javax.persistence.NoResultException; +import javax.persistence.PersistenceContext; +import javax.persistence.TypedQuery; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +import ca.uhn.fhir.jpa.demo.entity.User; +import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter; + +public class RequestAuthorizationInterceptor extends InterceptorAdapter { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RequestAuthorizationInterceptor.class); + + @PersistenceContext() + private EntityManager myEntityManager; + + @Autowired + private PlatformTransactionManager myPlatformTransactionManager; + + @Transactional(propagation = Propagation.REQUIRED) + @Override + public boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse) { + + String authorization = theResponse.getHeader("Authorization"); + + TypedQuery q = myEntityManager.createQuery("SELECT u FROM User u WHERE u.myUsername = :username", User.class); + String username = authorization; + + q.setParameter("username", username); + try { + User user = q.getSingleResult(); + ourLog.info("Found user [{}]: {}", username, user); + } catch (NoResultException e) { + ourLog.info("No user found in user table with username [{}]", username); + } + + return true; + } + +} diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/svc/LoginResponse.java b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/svc/LoginResponse.java new file mode 100644 index 00000000000..bb45bb13be3 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/svc/LoginResponse.java @@ -0,0 +1,18 @@ +package ca.uhn.fhir.jpa.demo.svc; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class LoginResponse { + + @JsonProperty(value = "success") + private boolean mySuccess; + + public boolean isSuccess() { + return mySuccess; + } + + public void setSuccess(boolean theSuccess) { + mySuccess = theSuccess; + } + +} diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/svc/ServiceController.java b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/svc/ServiceController.java new file mode 100644 index 00000000000..300419a0193 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/java/ca/uhn/fhir/jpa/demo/svc/ServiceController.java @@ -0,0 +1,28 @@ +package ca.uhn.fhir.jpa.demo.svc; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class ServiceController { + + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServiceController.class); + + public ServiceController() { + ourLog.info("Starting service controller"); + } + + @RequestMapping(value = "/login", method = {RequestMethod.GET, RequestMethod.POST} ) + public @ResponseBody LoginResponse handleFormUpload(@RequestParam("name") String theUsername) { + ourLog.info("Login request for user[{}]", theUsername); + + LoginResponse retVal = new LoginResponse(); + retVal.setSuccess(true); + + return retVal; + } + +} diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/resources/logback.xml b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/resources/logback.xml new file mode 100644 index 00000000000..ffec8d30c06 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + + INFO + + + %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] %msg%n + + + + + + + + \ No newline at end of file diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/hapi-fhir-server-config.xml b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/hapi-fhir-server-config.xml new file mode 100644 index 00000000000..054e108e2e9 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/hapi-fhir-server-config.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/hapi-fhir-server-database-config.xml b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/hapi-fhir-server-database-config.xml new file mode 100644 index 00000000000..7f2d4baee76 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/hapi-fhir-server-database-config.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + ca.uhn.fhir.jpa.entity + ca.uhn.fhir.jpa.demo.entity + + + + + + + + + + + + diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml new file mode 100644 index 00000000000..cac5c724ca6 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml @@ -0,0 +1,23 @@ + + + + + + home , DSTU2 , Local Server , ${serverBase}/fhir + + + + + + + + diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/non-fhir-services-config.xml b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/non-fhir-services-config.xml new file mode 100644 index 00000000000..a94a56f55ac --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/non-fhir-services-config.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/templates/about.html b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/templates/about.html new file mode 100644 index 00000000000..d552027e956 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/templates/about.html @@ -0,0 +1,67 @@ + + + + About This Server + + + +
+
+ +
+
+
+ +
+ +
+ +
+
+

About This Server

+
+
+
+ +
+

+ This server provides a nearly complete implementation of the FHIR Specification + using a 100% open source software stack. It is hosted by University Health Network. +

+

+ The architecture in use here is shown in the image on the right. This server is built + from a number of modules of the + HAPI FHIR + project, which is a 100% open-source (Apache 2.0 Licensed) Java based + implementation of the FHIR specification. +

+

+ +

+
+
+
+
+

Data On This Server

+
+
+

+ This server is regularly loaded with a standard set of test data sourced + from UHN's own testing environment. Do not use this server to store any data + that you will need later, as we will be regularly resetting it. +

+

+ This is not a production server and it provides no privacy. Do not store any + confidential data here. +

+
+
+ +
+
+
+ +
+
+ + diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/templates/tmpl-footer.html b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/templates/tmpl-footer.html new file mode 100644 index 00000000000..bf18c498a78 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/templates/tmpl-footer.html @@ -0,0 +1,16 @@ + + +
+ +
+ diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/templates/tmpl-home-welcome.html b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/templates/tmpl-home-welcome.html new file mode 100644 index 00000000000..dfc4769d6e4 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/templates/tmpl-home-welcome.html @@ -0,0 +1,52 @@ + + +
+ +

+ This is the home for the FHIR test server operated by + University Health Network. This server + (and the testing application you are currently using to access it) + is entirely built using + HAPI-FHIR, + a 100% open-source Java implementation of the + FHIR specification. +

+

+ Here are some things you might wish to try: +

+
    +
  • + View a + list of patients + on this server. +
  • +
  • + Construct a + search query + on this server. +
  • +
  • + Access a + different server + (use the Server menu at the top of the page to see a list of public FHIR servers) +
  • +
+
+ +

+ You are accessing the public FHIR server + . This server is hosted elsewhere on the internet + but is being accessed using the HAPI client implementation. +

+
+

+ + + This is not a production server! + + Do not store any information here that contains personal health information + or any other confidential information. This server will be regularly purged + and reloaded with fixed test data. +

+
+ diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/web.xml b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000000..72689c0e498 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,104 @@ + + + + org.springframework.web.context.ContextLoaderListener + + + + contextConfigLocation + + classpath:hapi-fhir-server-resourceproviders-dstu2.xml + /WEB-INF/hapi-fhir-server-database-config.xml + /WEB-INF/hapi-fhir-server-config.xml + /WEB-INF/hapi-fhir-tester-application-context.xml + /WEB-INF/hapi-fhir-tester-config.xml + /WEB-INF/non-fhir-services-config.xml + + + + + + + spring + org.springframework.web.servlet.DispatcherServlet + + contextConfigLocation + + /WEB-INF/hapi-fhir-tester-application-context.xml + /WEB-INF/hapi-fhir-tester-config.xml + + + 2 + + + + fhirServlet + ca.uhn.fhir.jpa.demo.JpaServerDemo + + ImplementationDescription + FHIR JPA Server + + 1 + + + + fhirServlet + /fhir/* + + + + spring + / + + + + + + + CORS Filter + org.ebaysf.web.cors.CORSFilter + + A comma separated list of allowed origins. Note: An '*' cannot be used for an allowed origin when using credentials. + cors.allowed.origins + * + + + A comma separated list of HTTP verbs, using which a CORS request can be made. + cors.allowed.methods + GET,POST,PUT,DELETE,OPTIONS + + + A comma separated list of allowed headers when making a non simple CORS request. + cors.allowed.headers + X-FHIR-Starter,Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers + + + A comma separated list non-standard response headers that will be exposed to XHR2 object. + cors.exposed.headers + Location,Content-Location + + + A flag that suggests if CORS is supported with cookies + cors.support.credentials + true + + + A flag to control logging + cors.logging.enabled + true + + + Indicates how long (in seconds) the results of a preflight request can be cached in a preflight result cache. + cors.preflight.maxage + 300 + + + + CORS Filter + /* + + + + diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/javaee_6.xsd b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/javaee_6.xsd new file mode 100644 index 00000000000..9fb587749ce --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/javaee_6.xsd @@ -0,0 +1,2419 @@ + + + + + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2003-2009 Sun Microsystems, Inc. All rights reserved. + + The contents of this file are subject to the terms of either the + GNU General Public License Version 2 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with + the License. You can obtain a copy of the License at + https://glassfish.dev.java.net/public/CDDL+GPL.html or + glassfish/bootstrap/legal/LICENSE.txt. See the License for the + specific language governing permissions and limitations under the + License. + + When distributing the software, include this License Header + Notice in each file and include the License file at + glassfish/bootstrap/legal/LICENSE.txt. Sun designates this + particular file as subject to the "Classpath" exception as + provided by Sun in the GPL Version 2 section of the License file + that accompanied this code. If applicable, add the following + below the License Header, with the fields enclosed by brackets [] + replaced by your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + Contributor(s): + + If you wish your version of this file to be governed by only the + CDDL or only the GPL Version 2, indicate your decision by adding + "[Contributor] elects to include this software in this + distribution under the [CDDL or GPL Version 2] license." If you + don't indicate a single choice of license, a recipient has the + option to distribute your version of this file under either the + CDDL, the GPL Version 2 or to extend the choice of license to its + licensees as provided above. However, if you add GPL Version 2 + code and therefore, elected the GPL Version 2 license, then the + option applies only if the new code is made subject to such + option by the copyright holder. + + + + + + + + The following definitions that appear in the common + shareable schema(s) of Java EE deployment descriptors should be + interpreted with respect to the context they are included: + + Deployment Component may indicate one of the following: + java ee application; + application client; + web application; + enterprise bean; + resource adapter; + + Deployment File may indicate one of the following: + ear file; + war file; + jar file; + rar file; + + + + + + + + + + + This group keeps the usage of the contained description related + elements consistent across Java EE deployment descriptors. + + All elements may occur multiple times with different languages, + to support localization of the content. + + + + + + + + + + + + + + + This group keeps the usage of the contained JNDI environment + reference elements consistent across Java EE deployment descriptors. + + + + + + + + + + + + + + + + + + + + + + + This group collects elements that are common to most + JNDI resource elements. + + + + + + + + + + The JNDI name to be looked up to resolve a resource reference. + + + + + + + + + + + + This group collects elements that are common to all the + JNDI resource elements. It does not include the lookup-name + element, that is only applicable to some resource elements. + + + + + + + + + A product specific name that this resource should be + mapped to. The name of this resource, as defined by the + resource's name element or defaulted, is a name that is + local to the application component using the resource. + (It's a name in the JNDI java:comp/env namespace.) Many + application servers provide a way to map these local + names to names of resources known to the application + server. This mapped name is often a global JNDI name, + but may be a name of any form. + + Application servers are not required to support any + particular form or type of mapped name, nor the ability + to use mapped names. The mapped name is + product-dependent and often installation-dependent. No + use of a mapped name is portable. + + + + + + + + + + + + + + + + Configuration of a DataSource. + + + + + + + + + Description of this DataSource. + + + + + + + + + The name element specifies the JNDI name of the + data source being defined. + + + + + + + + + DataSource, XADataSource or ConnectionPoolDataSource + implementation class. + + + + + + + + + Database server name. + + + + + + + + + Port number where a server is listening for requests. + + + + + + + + + Name of a database on a server. + + + + + + + + url
property is specified + along with other standard DataSource properties + such as serverName, databaseName + and portNumber, the more specific properties will + take precedence and url will be ignored. + + ]]> + + + + + + + + User name to use for connection authentication. + + + + + + + + + Password to use for connection authentication. + + + + + + + + + JDBC DataSource property. This may be a vendor-specific + property or a less commonly used DataSource property. + + + + + + + + + Sets the maximum time in seconds that this data source + will wait while attempting to connect to a database. + + + + + + + + + Set to false if connections should not participate in + transactions. + + + + + + + + + Isolation level for connections. + + + + + + + + + Number of connections that should be created when a + connection pool is initialized. + + + + + + + + + Maximum number of connections that should be concurrently + allocated for a connection pool. + + + + + + + + + Minimum number of connections that should be concurrently + allocated for a connection pool. + + + + + + + + + The number of seconds that a physical connection should + remain unused in the pool before the connection is + closed for a connection pool. + + + + + + + + + The total number of statements that a connection pool + should keep open. + + + + + + + + + + + + + + + + The description type is used by a description element to + provide text describing the parent element. The elements + that use this type should include any information that the + Deployment Component's Deployment File file producer wants + to provide to the consumer of the Deployment Component's + Deployment File (i.e., to the Deployer). Typically, the + tools used by such a Deployment File consumer will display + the description when processing the parent element that + contains the description. + + The lang attribute defines the language that the + description is provided in. The default value is "en" (English). + + + + + + + + + + + + + + + This type defines a dewey decimal that is used + to describe versions of documents. + + + + + + + + + + + + + + + + Employee Self Service + + + The value of the xml:lang attribute is "en" (English) by default. + + ]]> + + + + + + + + + + + + + + + + EmployeeRecord + + ../products/product.jar#ProductEJB + + ]]> + + + + + + + + + + + + + + + The ejb-local-refType is used by ejb-local-ref elements for + the declaration of a reference to an enterprise bean's local + home or to the local business interface of a 3.0 bean. + The declaration consists of: + + - an optional description + - the EJB reference name used in the code of the Deployment + Component that's referencing the enterprise bean. + - the optional expected type of the referenced enterprise bean + - the optional expected local interface of the referenced + enterprise bean or the local business interface of the + referenced enterprise bean. + - the optional expected local home interface of the referenced + enterprise bean. Not applicable if this ejb-local-ref refers + to the local business interface of a 3.0 bean. + - optional ejb-link information, used to specify the + referenced enterprise bean + - optional elements to define injection of the named enterprise + bean into a component field or property. + + + + + + + + + + + + + + + + + + + + + + ejb/Payroll + + ]]> + + + + + + + + + + + + + + + The ejb-refType is used by ejb-ref elements for the + declaration of a reference to an enterprise bean's home or + to the remote business interface of a 3.0 bean. + The declaration consists of: + + - an optional description + - the EJB reference name used in the code of + the Deployment Component that's referencing the enterprise + bean. + - the optional expected type of the referenced enterprise bean + - the optional remote interface of the referenced enterprise bean + or the remote business interface of the referenced enterprise + bean + - the optional expected home interface of the referenced + enterprise bean. Not applicable if this ejb-ref + refers to the remote business interface of a 3.0 bean. + - optional ejb-link information, used to specify the + referenced enterprise bean + - optional elements to define injection of the named enterprise + bean into a component field or property + + + + + + + + + + + + + + + + + + + + + + + The ejb-ref-typeType contains the expected type of the + referenced enterprise bean. + + The ejb-ref-type designates a value + that must be one of the following: + + Entity + Session + + + + + + + + + + + + + + + + + + + This type is used to designate an empty + element when used. + + + + + + + + + + + + + + The env-entryType is used to declare an application's + environment entry. The declaration consists of an optional + description, the name of the environment entry, a type + (optional if the value is injected, otherwise required), and + an optional value. + + It also includes optional elements to define injection of + the named resource into fields or JavaBeans properties. + + If a value is not specified and injection is requested, + no injection will occur and no entry of the specified name + will be created. This allows an initial value to be + specified in the source code without being incorrectly + changed when no override has been specified. + + If a value is not specified and no injection is requested, + a value must be supplied during deployment. + + This type is used by env-entry elements. + + + + + + + + + minAmount + + ]]> + + + + + + + java.lang.Integer + + ]]> + + + + + + + 100.00 + + ]]> + + + + + + + + + + + + + + + java.lang.Boolean + java.lang.Class + com.example.Color + + ]]> + + + + + + + + + + + + + + + The elements that use this type designate the name of a + Java class or interface. The name is in the form of a + "binary name", as defined in the JLS. This is the form + of name used in Class.forName(). Tools that need the + canonical name (the name used in source code) will need + to convert this binary name to the canonical name. + + + + + + + + + + + + + + + + This type defines four different values which can designate + boolean values. This includes values yes and no which are + not designated by xsd:boolean + + + + + + + + + + + + + + + + + + + + + The icon type contains small-icon and large-icon elements + that specify the file names for small and large GIF, JPEG, + or PNG icon images used to represent the parent element in a + GUI tool. + + The xml:lang attribute defines the language that the + icon file names are provided in. Its value is "en" (English) + by default. + + + + + + + + employee-service-icon16x16.jpg + + ]]> + + + + + + + employee-service-icon32x32.jpg + + ]]> + + + + + + + + + + + + + + + + An injection target specifies a class and a name within + that class into which a resource should be injected. + + The injection target class specifies the fully qualified + class name that is the target of the injection. The + Java EE specifications describe which classes can be an + injection target. + + The injection target name specifies the target within + the specified class. The target is first looked for as a + JavaBeans property name. If not found, the target is + looked for as a field name. + + The specified resource will be injected into the target + during initialization of the class by either calling the + set method for the target property or by setting a value + into the named field. + + + + + + + + + + + + + + The following transaction isolation levels are allowed + (see documentation for the java.sql.Connection interface): + TRANSACTION_READ_UNCOMMITTED + TRANSACTION_READ_COMMITTED + TRANSACTION_REPEATABLE_READ + TRANSACTION_SERIALIZABLE + + + + + + + + + + + + + + + + + + + The java-identifierType defines a Java identifier. + The users of this type should further verify that + the content does not contain Java reserved keywords. + + + + + + + + + + + + + + + + + + This is a generic type that designates a Java primitive + type or a fully qualified name of a Java interface/type, + or an array of such types. + + + + + + + + + + + + + + + + + : + + Example: + + jdbc:mysql://localhost:3307/testdb + + ]]> + + + + + + + + + + + + + + + + + The jndi-nameType type designates a JNDI name in the + Deployment Component's environment and is relative to the + java:comp/env context. A JNDI name must be unique within the + Deployment Component. + + + + + + + + + + + + + + + com.aardvark.payroll.PayrollHome + + ]]> + + + + + + + + + + + + + + + The lifecycle-callback type specifies a method on a + class to be called when a lifecycle event occurs. + Note that each class may have only one lifecycle callback + method for any given event and that the method may not + be overloaded. + + If the lifefycle-callback-class element is missing then + the class defining the callback is assumed to be the + component class in scope at the place in the descriptor + in which the callback definition appears. + + + + + + + + + + + + + + + + + The listenerType indicates the deployment properties for a web + application listener bean. + + + + + + + + + + The listener-class element declares a class in the + application must be registered as a web + application listener bean. The value is the fully + qualified classname of the listener class. + + + + + + + + + + + + + + + + The localType defines the fully-qualified name of an + enterprise bean's local interface. + + + + + + + + + + + + + + + + The local-homeType defines the fully-qualified + name of an enterprise bean's local home interface. + + + + + + + + + + + + + + + + This type is a general type that can be used to declare + parameter/value lists. + + + + + + + + + + The param-name element contains the name of a + parameter. + + + + + + + + + The param-value element contains the value of a + parameter. + + + + + + + + + + + + + + + + The elements that use this type designate either a relative + path or an absolute path starting with a "/". + + In elements that specify a pathname to a file within the + same Deployment File, relative filenames (i.e., those not + starting with "/") are considered relative to the root of + the Deployment File's namespace. Absolute filenames (i.e., + those starting with "/") also specify names in the root of + the Deployment File's namespace. In general, relative names + are preferred. The exception is .war files where absolute + names are preferred for consistency with the Servlet API. + + + + + + + + + + + + + + + + myPersistenceContext + + + + + myPersistenceContext + + PersistenceUnit1 + + Extended + + + ]]> + + + + + + + + + The persistence-context-ref-name element specifies + the name of a persistence context reference; its + value is the environment entry name used in + Deployment Component code. The name is a JNDI name + relative to the java:comp/env context. + + + + + + + + + The Application Assembler(or BeanProvider) may use the + following syntax to avoid the need to rename persistence + units to have unique names within a Java EE application. + + The Application Assembler specifies the pathname of the + root of the persistence.xml file for the referenced + persistence unit and appends the name of the persistence + unit separated from the pathname by #. The pathname is + relative to the referencing application component jar file. + In this manner, multiple persistence units with the same + persistence unit name may be uniquely identified when the + Application Assembler cannot change persistence unit names. + + + + + + + + + + Used to specify properties for the container or persistence + provider. Vendor-specific properties may be included in + the set of properties. Properties that are not recognized + by a vendor must be ignored. Entries that make use of the + namespace javax.persistence and its subnamespaces must not + be used for vendor-specific properties. The namespace + javax.persistence is reserved for use by the specification. + + + + + + + + + + + + + + + + + The persistence-context-typeType specifies the transactional + nature of a persistence context reference. + + The value of the persistence-context-type element must be + one of the following: + Transaction + Extended + + + + + + + + + + + + + + + + + + + Specifies a name/value pair. + + + + + + + + + + + + + + + + + + + + myPersistenceUnit + + + + + myPersistenceUnit + + PersistenceUnit1 + + + + ]]> + + + + + + + + + The persistence-unit-ref-name element specifies + the name of a persistence unit reference; its + value is the environment entry name used in + Deployment Component code. The name is a JNDI name + relative to the java:comp/env context. + + + + + + + + + The Application Assembler(or BeanProvider) may use the + following syntax to avoid the need to rename persistence + units to have unique names within a Java EE application. + + The Application Assembler specifies the pathname of the + root of the persistence.xml file for the referenced + persistence unit and appends the name of the persistence + unit separated from the pathname by #. The pathname is + relative to the referencing application component jar file. + In this manner, multiple persistence units with the same + persistence unit name may be uniquely identified when the + Application Assembler cannot change persistence unit names. + + + + + + + + + + + + + + + + com.wombat.empl.EmployeeService + + ]]> + + + + + + + + + + + + + + + jms/StockQueue + + javax.jms.Queue + + + + ]]> + + + + + + + + + The resource-env-ref-name element specifies the name + of a resource environment reference; its value is + the environment entry name used in + the Deployment Component code. The name is a JNDI + name relative to the java:comp/env context and must + be unique within a Deployment Component. + + + + + + + + + The resource-env-ref-type element specifies the type + of a resource environment reference. It is the + fully qualified name of a Java language class or + interface. + + + + + + + + + + + + + + + + + jdbc/EmployeeAppDB + javax.sql.DataSource + Container + Shareable + + + ]]> + + + + + + + + + The res-ref-name element specifies the name of a + resource manager connection factory reference. + The name is a JNDI name relative to the + java:comp/env context. + The name must be unique within a Deployment File. + + + + + + + + + The res-type element specifies the type of the data + source. The type is specified by the fully qualified + Java language class or interface + expected to be implemented by the data source. + + + + + + + + + + + + + + + + + + + The res-authType specifies whether the Deployment Component + code signs on programmatically to the resource manager, or + whether the Container will sign on to the resource manager + on behalf of the Deployment Component. In the latter case, + the Container uses information that is supplied by the + Deployer. + + The value must be one of the two following: + + Application + Container + + + + + + + + + + + + + + + + + + + The res-sharing-scope type specifies whether connections + obtained through the given resource manager connection + factory reference can be shared. The value, if specified, + must be one of the two following: + + Shareable + Unshareable + + The default value is Shareable. + + + + + + + + + + + + + + + + + + + The run-asType specifies the run-as identity to be + used for the execution of a component. It contains an + optional description, and the name of a security role. + + + + + + + + + + + + + + + + + + The role-nameType designates the name of a security role. + + The name must conform to the lexical rules for a token. + + + + + + + + + + + + + + + + + This role includes all employees who are authorized + to access the employee service application. + + employee + + + ]]> + + + + + + + + + + + + + + + + + The security-role-refType contains the declaration of a + security role reference in a component's or a + Deployment Component's code. The declaration consists of an + optional description, the security role name used in the + code, and an optional link to a security role. If the + security role is not specified, the Deployer must choose an + appropriate security role. + + + + + + + + + + The value of the role-name element must be the String used + as the parameter to the + EJBContext.isCallerInRole(String roleName) method or the + HttpServletRequest.isUserInRole(String role) method. + + + + + + + + + The role-link element is a reference to a defined + security role. The role-link element must contain + the name of one of the security roles defined in the + security-role elements. + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:QName. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:boolean. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:NMTOKEN. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:anyURI. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:integer. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:positiveInteger. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:nonNegativeInteger. + + + + + + + + + + + + + + + + + + This type adds an "id" attribute to xsd:string. + + + + + + + + + + + + + + + + + + This is a special string datatype that is defined by Java EE as + a base type for defining collapsed strings. When schemas + require trailing/leading space elimination as well as + collapsing the existing whitespace, this base type may be + used. + + + + + + + + + + + + + + + + + + This simple type designates a boolean with only two + permissible values + + - true + - false + + + + + + + + + + + + + + + + + + The url-patternType contains the url pattern of the mapping. + It must follow the rules specified in Section 11.2 of the + Servlet API Specification. This pattern is assumed to be in + URL-decoded form and must not contain CR(#xD) or LF(#xA). + If it contains those characters, the container must inform + the developer with a descriptive error message. + The container must preserve all characters including whitespaces. + + + + + + + + + + + + + + + + CorporateStocks + + + + ]]> + + + + + + + + + The message-destination-name element specifies a + name for a message destination. This name must be + unique among the names of message destinations + within the Deployment File. + + + + + + + + + A product specific name that this message destination + should be mapped to. Each message-destination-ref + element that references this message destination will + define a name in the namespace of the referencing + component or in one of the other predefined namespaces. + Many application servers provide a way to map these + local names to names of resources known to the + application server. This mapped name is often a global + JNDI name, but may be a name of any form. Each of the + local names should be mapped to this same global name. + + Application servers are not required to support any + particular form or type of mapped name, nor the ability + to use mapped names. The mapped name is + product-dependent and often installation-dependent. No + use of a mapped name is portable. + + + + + + + + + The JNDI name to be looked up to resolve the message destination. + + + + + + + + + + + + + + + + jms/StockQueue + + javax.jms.Queue + + Consumes + + CorporateStocks + + + + ]]> + + + + + + + + + The message-destination-ref-name element specifies + the name of a message destination reference; its + value is the environment entry name used in + Deployment Component code. + + + + + + + + + + + + + + + + + + + + The message-destination-usageType specifies the use of the + message destination indicated by the reference. The value + indicates whether messages are consumed from the message + destination, produced for the destination, or both. The + Assembler makes use of this information in linking producers + of a destination with its consumers. + + The value of the message-destination-usage element must be + one of the following: + Consumes + Produces + ConsumesProduces + + + + + + + + + + + + + + + + + + + javax.jms.Queue + + + ]]> + + + + + + + + + + + + + + + The message-destination-linkType is used to link a message + destination reference or message-driven bean to a message + destination. + + The Assembler sets the value to reflect the flow of messages + between producers and consumers in the application. + + The value must be the message-destination-name of a message + destination in the same Deployment File or in another + Deployment File in the same Java EE application unit. + + Alternatively, the value may be composed of a path name + specifying a Deployment File containing the referenced + message destination with the message-destination-name of the + destination appended and separated from the path name by + "#". The path name is relative to the Deployment File + containing Deployment Component that is referencing the + message destination. This allows multiple message + destinations with the same name to be uniquely identified. + + + + + + + + + diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/jsp_2_2.xsd b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/jsp_2_2.xsd new file mode 100644 index 00000000000..fa41e4266f1 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/jsp_2_2.xsd @@ -0,0 +1,389 @@ + + + + + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2003-2009 Sun Microsystems, Inc. All rights reserved. + + The contents of this file are subject to the terms of either the + GNU General Public License Version 2 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with + the License. You can obtain a copy of the License at + https://glassfish.dev.java.net/public/CDDL+GPL.html or + glassfish/bootstrap/legal/LICENSE.txt. See the License for the + specific language governing permissions and limitations under the + License. + + When distributing the software, include this License Header + Notice in each file and include the License file at + glassfish/bootstrap/legal/LICENSE.txt. Sun designates this + particular file as subject to the "Classpath" exception as + provided by Sun in the GPL Version 2 section of the License file + that accompanied this code. If applicable, add the following + below the License Header, with the fields enclosed by brackets [] + replaced by your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + Contributor(s): + + If you wish your version of this file to be governed by only the + CDDL or only the GPL Version 2, indicate your decision by adding + "[Contributor] elects to include this software in this + distribution under the [CDDL or GPL Version 2] license." If you + don't indicate a single choice of license, a recipient has the + option to distribute your version of this file under either the + CDDL, the GPL Version 2 or to extend the choice of license to its + licensees as provided above. However, if you add GPL Version 2 + code and therefore, elected the GPL Version 2 license, then the + option applies only if the new code is made subject to such + option by the copyright holder. + + + + + + + + This is the XML Schema for the JSP 2.2 deployment descriptor + types. The JSP 2.2 schema contains all the special + structures and datatypes that are necessary to use JSP files + from a web application. + + The contents of this schema is used by the web-common_3_0.xsd + file to define JSP specific content. + + + + + + + + The following conventions apply to all Java EE + deployment descriptor elements unless indicated otherwise. + + - In elements that specify a pathname to a file within the + same JAR file, relative filenames (i.e., those not + starting with "/") are considered relative to the root of + the JAR file's namespace. Absolute filenames (i.e., those + starting with "/") also specify names in the root of the + JAR file's namespace. In general, relative names are + preferred. The exception is .war files where absolute + names are preferred for consistency with the Servlet API. + + + + + + + + + + + + + + The jsp-configType is used to provide global configuration + information for the JSP files in a web application. It has + two subelements, taglib and jsp-property-group. + + + + + + + + + + + + + + + + + + The jsp-file element contains the full path to a JSP file + within the web application beginning with a `/'. + + + + + + + + + + + + + + + + The jsp-property-groupType is used to group a number of + files so they can be given global property information. + All files so described are deemed to be JSP files. The + following additional properties can be described: + + - Control whether EL is ignored. + - Control whether scripting elements are invalid. + - Indicate pageEncoding information. + - Indicate that a resource is a JSP document (XML). + - Prelude and Coda automatic includes. + - Control whether the character sequence #{ is allowed + when used as a String literal. + - Control whether template text containing only + whitespaces must be removed from the response output. + - Indicate the default contentType information. + - Indicate the default buffering model for JspWriter + - Control whether error should be raised for the use of + undeclared namespaces in a JSP page. + + + + + + + + + + + Can be used to easily set the isELIgnored + property of a group of JSP pages. By default, the + EL evaluation is enabled for Web Applications using + a Servlet 2.4 or greater web.xml, and disabled + otherwise. + + + + + + + + + The valid values of page-encoding are those of the + pageEncoding page directive. It is a + translation-time error to name different encodings + in the pageEncoding attribute of the page directive + of a JSP page and in a JSP configuration element + matching the page. It is also a translation-time + error to name different encodings in the prolog + or text declaration of a document in XML syntax and + in a JSP configuration element matching the document. + It is legal to name the same encoding through + mulitple mechanisms. + + + + + + + + + Can be used to easily disable scripting in a + group of JSP pages. By default, scripting is + enabled. + + + + + + + + + If true, denotes that the group of resources + that match the URL pattern are JSP documents, + and thus must be interpreted as XML documents. + If false, the resources are assumed to not + be JSP documents, unless there is another + property group that indicates otherwise. + + + + + + + + + The include-prelude element is a context-relative + path that must correspond to an element in the + Web Application. When the element is present, + the given path will be automatically included (as + in an include directive) at the beginning of each + JSP page in this jsp-property-group. + + + + + + + + + The include-coda element is a context-relative + path that must correspond to an element in the + Web Application. When the element is present, + the given path will be automatically included (as + in an include directive) at the end of each + JSP page in this jsp-property-group. + + + + + + + + + The character sequence #{ is reserved for EL expressions. + Consequently, a translation error occurs if the #{ + character sequence is used as a String literal, unless + this element is enabled (true). Disabled (false) by + default. + + + + + + + + + Indicates that template text containing only whitespaces + must be removed from the response output. It has no + effect on JSP documents (XML syntax). Disabled (false) + by default. + + + + + + + + + The valid values of default-content-type are those of the + contentType page directive. It specifies the default + response contentType if the page directive does not include + a contentType attribute. + + + + + + + + + The valid values of buffer are those of the + buffer page directive. It specifies if buffering should be + used for the output to response, and if so, the size of the + buffer to use. + + + + + + + + + The default behavior when a tag with unknown namespace is used + in a JSP page (regular syntax) is to silently ignore it. If + set to true, then an error must be raised during the translation + time when an undeclared tag is used in a JSP page. Disabled + (false) by default. + + + + + + + + + + + + + + + + The taglibType defines the syntax for declaring in + the deployment descriptor that a tag library is + available to the application. This can be done + to override implicit map entries from TLD files and + from the container. + + + + + + + + + A taglib-uri element describes a URI identifying a + tag library used in the web application. The body + of the taglib-uri element may be either an + absolute URI specification, or a relative URI. + There should be no entries in web.xml with the + same taglib-uri value. + + + + + + + + + the taglib-location element contains the location + (as a resource relative to the root of the web + application) where to find the Tag Library + Description file for the tag library. + + + + + + + + + diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/web-app_3_0.xsd b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/web-app_3_0.xsd new file mode 100644 index 00000000000..bbcdf43cd3a --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/web-app_3_0.xsd @@ -0,0 +1,272 @@ + + + + + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2003-2009 Sun Microsystems, Inc. All rights reserved. + + The contents of this file are subject to the terms of either the + GNU General Public License Version 2 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with + the License. You can obtain a copy of the License at + https://glassfish.dev.java.net/public/CDDL+GPL.html or + glassfish/bootstrap/legal/LICENSE.txt. See the License for the + specific language governing permissions and limitations under the + License. + + When distributing the software, include this License Header + Notice in each file and include the License file at + glassfish/bootstrap/legal/LICENSE.txt. Sun designates this + particular file as subject to the "Classpath" exception as + provided by Sun in the GPL Version 2 section of the License file + that accompanied this code. If applicable, add the following + below the License Header, with the fields enclosed by brackets [] + replaced by your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + Contributor(s): + + If you wish your version of this file to be governed by only the + CDDL or only the GPL Version 2, indicate your decision by adding + "[Contributor] elects to include this software in this + distribution under the [CDDL or GPL Version 2] license." If you + don't indicate a single choice of license, a recipient has the + option to distribute your version of this file under either the + CDDL, the GPL Version 2 or to extend the choice of license to its + licensees as provided above. However, if you add GPL Version 2 + code and therefore, elected the GPL Version 2 license, then the + option applies only if the new code is made subject to such + option by the copyright holder. + + + + + + + + ... + + + The instance documents may indicate the published version of + the schema using the xsi:schemaLocation attribute for Java EE + namespace with the following location: + + http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd + + ]]> + + + + + + + The following conventions apply to all Java EE + deployment descriptor elements unless indicated otherwise. + + - In elements that specify a pathname to a file within the + same JAR file, relative filenames (i.e., those not + starting with "/") are considered relative to the root of + the JAR file's namespace. Absolute filenames (i.e., those + starting with "/") also specify names in the root of the + JAR file's namespace. In general, relative names are + preferred. The exception is .war files where absolute + names are preferred for consistency with the Servlet API. + + + + + + + + + + + + + + The web-app element is the root of the deployment + descriptor for a web application. Note that the sub-elements + of this element can be in the arbitrary order. Because of + that, the multiplicity of the elements of distributable, + session-config, welcome-file-list, jsp-config, login-config, + and locale-encoding-mapping-list was changed from "?" to "*" + in this schema. However, the deployment descriptor instance + file must not contain multiple elements of session-config, + jsp-config, and login-config. When there are multiple elements of + welcome-file-list or locale-encoding-mapping-list, the container + must concatenate the element contents. The multiple occurence + of the element distributable is redundant and the container + treats that case exactly in the same way when there is only + one distributable. + + + + + + + + The servlet element contains the name of a servlet. + The name must be unique within the web application. + + + + + + + + + + + The filter element contains the name of a filter. + The name must be unique within the web application. + + + + + + + + + + + The ejb-local-ref-name element contains the name of an EJB + reference. The EJB reference is an entry in the web + application's environment and is relative to the + java:comp/env context. The name must be unique within + the web application. + + It is recommended that name is prefixed with "ejb/". + + + + + + + + + + + The ejb-ref-name element contains the name of an EJB + reference. The EJB reference is an entry in the web + application's environment and is relative to the + java:comp/env context. The name must be unique within + the web application. + + It is recommended that name is prefixed with "ejb/". + + + + + + + + + + + The resource-env-ref-name element specifies the name of + a resource environment reference; its value is the + environment entry name used in the web application code. + The name is a JNDI name relative to the java:comp/env + context and must be unique within a web application. + + + + + + + + + + + The message-destination-ref-name element specifies the name of + a message destination reference; its value is the + environment entry name used in the web application code. + The name is a JNDI name relative to the java:comp/env + context and must be unique within a web application. + + + + + + + + + + + The res-ref-name element specifies the name of a + resource manager connection factory reference. The name + is a JNDI name relative to the java:comp/env context. + The name must be unique within a web application. + + + + + + + + + + + The env-entry-name element contains the name of a web + application's environment entry. The name is a JNDI + name relative to the java:comp/env context. The name + must be unique within a web application. + + + + + + + + + + + A role-name-key is specified to allow the references + from the security-role-refs. + + + + + + + + + + + The keyref indicates the references from + security-role-ref to a specified role-name. + + + + + + + + + diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/web-common_3_0.xsd b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/web-common_3_0.xsd new file mode 100644 index 00000000000..f994bc2c651 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/web-common_3_0.xsd @@ -0,0 +1,1575 @@ + + + + + + DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + + Copyright 2003-2009 Sun Microsystems, Inc. All rights reserved. + + The contents of this file are subject to the terms of either the + GNU General Public License Version 2 only ("GPL") or the Common + Development and Distribution License("CDDL") (collectively, the + "License"). You may not use this file except in compliance with + the License. You can obtain a copy of the License at + https://glassfish.dev.java.net/public/CDDL+GPL.html or + glassfish/bootstrap/legal/LICENSE.txt. See the License for the + specific language governing permissions and limitations under the + License. + + When distributing the software, include this License Header + Notice in each file and include the License file at + glassfish/bootstrap/legal/LICENSE.txt. Sun designates this + particular file as subject to the "Classpath" exception as + provided by Sun in the GPL Version 2 section of the License file + that accompanied this code. If applicable, add the following + below the License Header, with the fields enclosed by brackets [] + replaced by your own identifying information: + "Portions Copyrighted [year] [name of copyright owner]" + + Contributor(s): + + If you wish your version of this file to be governed by only the + CDDL or only the GPL Version 2, indicate your decision by adding + "[Contributor] elects to include this software in this + distribution under the [CDDL or GPL Version 2] license." If you + don't indicate a single choice of license, a recipient has the + option to distribute your version of this file under either the + CDDL, the GPL Version 2 or to extend the choice of license to its + licensees as provided above. However, if you add GPL Version 2 + code and therefore, elected the GPL Version 2 license, then the + option applies only if the new code is made subject to such + option by the copyright holder. + + + + + + + + ... + + + The instance documents may indicate the published version of + the schema using the xsi:schemaLocation attribute for Java EE + namespace with the following location: + + http://java.sun.com/xml/ns/javaee/web-common_3_0.xsd + + ]]> + + + + + + + The following conventions apply to all Java EE + deployment descriptor elements unless indicated otherwise. + + - In elements that specify a pathname to a file within the + same JAR file, relative filenames (i.e., those not + starting with "/") are considered relative to the root of + the JAR file's namespace. Absolute filenames (i.e., those + starting with "/") also specify names in the root of the + JAR file's namespace. In general, relative names are + preferred. The exception is .war files where absolute + names are preferred for consistency with the Servlet API. + + + + + + + + + + + + + + + + + The context-param element contains the declaration + of a web application's servlet context + initialization parameters. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The metadata-complete attribute defines whether this + deployment descriptor and other related deployment + descriptors for this module (e.g., web service + descriptors) are complete, or whether the class + files available to this module and packaged with + this application should be examined for annotations + that specify deployment information. + + If metadata-complete is set to "true", the deployment + tool must ignore any annotations that specify deployment + information, which might be present in the class files + of the application. + + If metadata-complete is not specified or is set to + "false", the deployment tool must examine the class + files of the application for annotations, as + specified by the specifications. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The auth-constraintType indicates the user roles that + should be permitted access to this resource + collection. The role-name used here must either correspond + to the role-name of one of the security-role elements + defined for this web application, or be the specially + reserved role-name "*" that is a compact syntax for + indicating all roles in the web application. If both "*" + and rolenames appear, the container interprets this as all + roles. If no roles are defined, no user is allowed access + to the portion of the web application described by the + containing security-constraint. The container matches + role names case sensitively when determining access. + + + + + + + + + + + + + + + + + + The auth-methodType is used to configure the authentication + mechanism for the web application. As a prerequisite to + gaining access to any web resources which are protected by + an authorization constraint, a user must have authenticated + using the configured mechanism. Legal values are "BASIC", + "DIGEST", "FORM", "CLIENT-CERT", or a vendor-specific + authentication scheme. + + Used in: login-config + + + + + + + + + + + + + + + + The dispatcher has five legal values: FORWARD, REQUEST, + INCLUDE, ASYNC, and ERROR. + + A value of FORWARD means the Filter will be applied under + RequestDispatcher.forward() calls. + A value of REQUEST means the Filter will be applied under + ordinary client calls to the path or servlet. + A value of INCLUDE means the Filter will be applied under + RequestDispatcher.include() calls. + A value of ASYNC means the Filter will be applied under + calls dispatched from an AsyncContext. + A value of ERROR means the Filter will be applied under the + error page mechanism. + + The absence of any dispatcher elements in a filter-mapping + indicates a default of applying filters only under ordinary + client calls to the path or servlet. + + + + + + + + + + + + + + + + + + + + + + The error-code contains an HTTP error code, ex: 404 + + Used in: error-page + + + + + + + + + + + + + + + + + + + The error-pageType contains a mapping between an error code + or exception type to the path of a resource in the web + application. + + Error-page declarations using the exception-type element in + the deployment descriptor must be unique up to the class name of + the exception-type. Similarly, error-page declarations using the + status-code element must be unique in the deployment descriptor + up to the status code. + + Used in: web-app + + + + + + + + + + + The exception-type contains a fully qualified class + name of a Java exception type. + + + + + + + + + + The location element contains the location of the + resource in the web application relative to the root of + the web application. The value of the location must have + a leading `/'. + + + + + + + + + + + + + + + + The filterType is used to declare a filter in the web + application. The filter is mapped to either a servlet or a + URL pattern in the filter-mapping element, using the + filter-name value to reference. Filters can access the + initialization parameters declared in the deployment + descriptor at runtime via the FilterConfig interface. + + Used in: web-app + + + + + + + + + + + The fully qualified classname of the filter. + + + + + + + + + + The init-param element contains a name/value pair as + an initialization param of a servlet filter + + + + + + + + + + + + + + + + Declaration of the filter mappings in this web + application is done by using filter-mappingType. + The container uses the filter-mapping + declarations to decide which filters to apply to a request, + and in what order. The container matches the request URI to + a Servlet in the normal way. To determine which filters to + apply it matches filter-mapping declarations either on + servlet-name, or on url-pattern for each filter-mapping + element, depending on which style is used. The order in + which filters are invoked is the order in which + filter-mapping declarations that match a request URI for a + servlet appear in the list of filter-mapping elements.The + filter-name value must be the value of the filter-name + sub-elements of one of the filter declarations in the + deployment descriptor. + + + + + + + + + + + + + + + + + + + + + + This type defines a string which contains at least one + character. + + + + + + + + + + + + + + + + + + The logical name of the filter is declare + by using filter-nameType. This name is used to map the + filter. Each filter name is unique within the web + application. + + Used in: filter, filter-mapping + + + + + + + + + + + + + + + + The form-login-configType specifies the login and error + pages that should be used in form based login. If form based + authentication is not used, these elements are ignored. + + Used in: login-config + + + + + + + + + The form-login-page element defines the location in the web + app where the page that can be used for login can be + found. The path begins with a leading / and is interpreted + relative to the root of the WAR. + + + + + + + + + The form-error-page element defines the location in + the web app where the error page that is displayed + when login is not successful can be found. + The path begins with a leading / and is interpreted + relative to the root of the WAR. + + + + + + + + + + + + + A HTTP method type as defined in HTTP 1.1 section 2.2. + + + + + + + + + + + + + + + + + + + + + + + + + + The login-configType is used to configure the authentication + method that should be used, the realm name that should be + used for this application, and the attributes that are + needed by the form login mechanism. + + Used in: web-app + + + + + + + + + + The realm name element specifies the realm name to + use in HTTP Basic authorization. + + + + + + + + + + + + + + + + + The mime-mappingType defines a mapping between an extension + and a mime type. + + Used in: web-app + + + + + + + + The extension element contains a string describing an + extension. example: "txt" + + + + + + + + + + + + + + + + + The mime-typeType is used to indicate a defined mime type. + + Example: + "text/plain" + + Used in: mime-mapping + + + + + + + + + + + + + + + + + + The security-constraintType is used to associate + security constraints with one or more web resource + collections + + Used in: web-app + + + + + + + + + + + + + + + + + + + + The servletType is used to declare a servlet. + It contains the declarative data of a + servlet. If a jsp-file is specified and the load-on-startup + element is present, then the JSP should be precompiled and + loaded. + + Used in: web-app + + + + + + + + + + + + The servlet-class element contains the fully + qualified class name of the servlet. + + + + + + + + + + + + The load-on-startup element indicates that this + servlet should be loaded (instantiated and have + its init() called) on the startup of the web + application. The optional contents of these + element must be an integer indicating the order in + which the servlet should be loaded. If the value + is a negative integer, or the element is not + present, the container is free to load the servlet + whenever it chooses. If the value is a positive + integer or 0, the container must load and + initialize the servlet as the application is + deployed. The container must guarantee that + servlets marked with lower integers are loaded + before servlets marked with higher integers. The + container may choose the order of loading of + servlets with the same load-on-start-up value. + + + + + + + + + + + + + + + + + + + + + The servlet-mappingType defines a mapping between a + servlet and a url pattern. + + Used in: web-app + + + + + + + + + + + + + + + + + + The servlet-name element contains the canonical name of the + servlet. Each servlet name is unique within the web + application. + + + + + + + + + + + + + + + + The session-configType defines the session parameters + for this web application. + + Used in: web-app + + + + + + + + + The session-timeout element defines the default + session timeout interval for all sessions created + in this web application. The specified timeout + must be expressed in a whole number of minutes. + If the timeout is 0 or less, the container ensures + the default behaviour of sessions is never to time + out. If this element is not specified, the container + must set its default timeout period. + + + + + + + + + The cookie-config element defines the configuration of the + session tracking cookies created by this web application. + + + + + + + + + The tracking-mode element defines the tracking modes + for sessions created by this web application + + + + + + + + + + + + + + + + The cookie-configType defines the configuration for the + session tracking cookies of this web application. + + Used in: session-config + + + + + + + + + The name that will be assigned to any session tracking + cookies created by this web application. + The default is JSESSIONID + + + + + + + + + The domain name that will be assigned to any session tracking + cookies created by this web application. + + + + + + + + + The path that will be assigned to any session tracking + cookies created by this web application. + + + + + + + + + The comment that will be assigned to any session tracking + cookies created by this web application. + + + + + + + + + Specifies whether any session tracking cookies created + by this web application will be marked as HttpOnly + + + + + + + + + Specifies whether any session tracking cookies created + by this web application will be marked as secure + even if the request that initiated the corresponding session + is using plain HTTP instead of HTTPS + + + + + + + + + The lifetime (in seconds) that will be assigned to any + session tracking cookies created by this web application. + Default is -1 + + + + + + + + + + + + + + + + The name that will be assigned to any session tracking + cookies created by this web application. + The default is JSESSIONID + + Used in: cookie-config + + + + + + + + + + + + + + + + The domain name that will be assigned to any session tracking + cookies created by this web application. + + Used in: cookie-config + + + + + + + + + + + + + + + + The path that will be assigned to any session tracking + cookies created by this web application. + + Used in: cookie-config + + + + + + + + + + + + + + + + The comment that will be assigned to any session tracking + cookies created by this web application. + + Used in: cookie-config + + + + + + + + + + + + + + + + The tracking modes for sessions created by this web + application + + Used in: session-config + + + + + + + + + + + + + + + + + + + + The transport-guaranteeType specifies that the communication + between client and server should be NONE, INTEGRAL, or + CONFIDENTIAL. NONE means that the application does not + require any transport guarantees. A value of INTEGRAL means + that the application requires that the data sent between the + client and server be sent in such a way that it can't be + changed in transit. CONFIDENTIAL means that the application + requires that the data be transmitted in a fashion that + prevents other entities from observing the contents of the + transmission. In most cases, the presence of the INTEGRAL or + CONFIDENTIAL flag will indicate that the use of SSL is + required. + + Used in: user-data-constraint + + + + + + + + + + + + + + + + + + + + The user-data-constraintType is used to indicate how + data communicated between the client and container should be + protected. + + Used in: security-constraint + + + + + + + + + + + + + + + + + + The elements that use this type designate a path starting + with a "/" and interpreted relative to the root of a WAR + file. + + + + + + + + + + + + + + + This type contains the recognized versions of + web-application supported. It is used to designate the + version of the web application. + + + + + + + + + + + + + + + + The web-resource-collectionType is used to identify the + resources and HTTP methods on those resources to which a + security constraint applies. If no HTTP methods are specified, + then the security constraint applies to all HTTP methods. + If HTTP methods are specified by http-method-omission + elements, the security constraint applies to all methods + except those identified in the collection. + http-method-omission and http-method elements are never + mixed in the same collection. + + Used in: security-constraint + + + + + + + + + The web-resource-name contains the name of this web + resource collection. + + + + + + + + + + + + Each http-method names an HTTP method to which the + constraint applies. + + + + + + + + + Each http-method-omission names an HTTP method to + which the constraint does not apply. + + + + + + + + + + + + + + + + + The welcome-file-list contains an ordered list of welcome + files elements. + + Used in: web-app + + + + + + + + + The welcome-file element contains file name to use + as a default welcome file, such as index.html + + + + + + + + + + + + + The localeType defines valid locale defined by ISO-639-1 + and ISO-3166. + + + + + + + + + + + + + The encodingType defines IANA character sets. + + + + + + + + + + + + + + + + The locale-encoding-mapping-list contains one or more + locale-encoding-mapping(s). + + + + + + + + + + + + + + + + + The locale-encoding-mapping contains locale name and + encoding name. The locale name must be either "Language-code", + such as "ja", defined by ISO-639 or "Language-code_Country-code", + such as "ja_JP". "Country code" is defined by ISO-3166. + + + + + + + + + + + + + + + + + + This element indicates that the ordering sub-element in which + it was placed should take special action regarding the ordering + of this application resource relative to other application + configuration resources. + See section 8.2.2 of the specification for details. + + + + + + + + + + + + + + Please see section 8.2.2 of the specification for details. + + + + + + + + + + + + + + + + + Please see section 8.2.2 of the specification for details. + + + + + + + + + + + + + + + + + This element contains a sequence of "name" elements, each of + which + refers to an application configuration resource by the "name" + declared on its web.xml fragment. This element can also contain + a single "others" element which specifies that this document + comes + before or after other documents within the application. + See section 8.2.2 of the specification for details. + + + + + + + + + + + + + + + + + This element specifies configuration information related to the + handling of multipart/form-data requests. + + + + + + + + + The directory location where uploaded files will be stored + + + + + + + + + The maximum size limit of uploaded files + + + + + + + + + The maximum size limit of multipart/form-data requests + + + + + + + + + The size threshold after which an uploaded file will be + written to disk + + + + + + + + diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/xml.xsd b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/xml.xsd new file mode 100644 index 00000000000..aea7d0db0a4 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/main/webapp/WEB-INF/xsd/xml.xsd @@ -0,0 +1,287 @@ + + + + + + +
+

About the XML namespace

+ +
+

+ This schema document describes the XML namespace, in a form + suitable for import by other schema documents. +

+

+ See + http://www.w3.org/XML/1998/namespace.html and + + http://www.w3.org/TR/REC-xml for information + about this namespace. +

+

+ Note that local names in this namespace are intended to be + defined only by the World Wide Web Consortium or its subgroups. + The names currently defined in this namespace are listed below. + They should not be used with conflicting semantics by any Working + Group, specification, or document instance. +

+

+ See further below in this document for more information about how to refer to this schema document from your own + XSD schema documents and about the + namespace-versioning policy governing this schema document. +

+
+
+
+
+ + + + +
+ +

lang (as an attribute name)

+

+ denotes an attribute whose value + is a language code for the natural language of the content of + any element; its value is inherited. This name is reserved + by virtue of its definition in the XML specification.

+ +
+
+

Notes

+

+ Attempting to install the relevant ISO 2- and 3-letter + codes as the enumerated possible values is probably never + going to be a realistic possibility. +

+

+ See BCP 47 at + http://www.rfc-editor.org/rfc/bcp/bcp47.txt + and the IANA language subtag registry at + + http://www.iana.org/assignments/language-subtag-registry + for further information. +

+

+ The union allows for the 'un-declaration' of xml:lang with + the empty string. +

+
+
+
+ + + + + + + + + +
+ + + + +
+ +

space (as an attribute name)

+

+ denotes an attribute whose + value is a keyword indicating what whitespace processing + discipline is intended for the content of the element; its + value is inherited. This name is reserved by virtue of its + definition in the XML specification.

+ +
+
+
+ + + + + + +
+ + + +
+ +

base (as an attribute name)

+

+ denotes an attribute whose value + provides a URI to be used as the base for interpreting any + relative URIs in the scope of the element on which it + appears; its value is inherited. This name is reserved + by virtue of its definition in the XML Base specification.

+ +

+ See http://www.w3.org/TR/xmlbase/ + for information about this attribute. +

+
+
+
+
+ + + + +
+ +

id (as an attribute name)

+

+ denotes an attribute whose value + should be interpreted as if declared to be of type ID. + This name is reserved by virtue of its definition in the + xml:id specification.

+ +

+ See http://www.w3.org/TR/xml-id/ + for information about this attribute. +

+
+
+
+
+ + + + + + + + + + +
+ +

Father (in any context at all)

+ +
+

+ denotes Jon Bosak, the chair of + the original XML Working Group. This name is reserved by + the following decision of the W3C XML Plenary and + XML Coordination groups: +

+
+

+ In appreciation for his vision, leadership and + dedication the W3C XML Plenary on this 10th day of + February, 2000, reserves for Jon Bosak in perpetuity + the XML name "xml:Father". +

+
+
+
+
+
+ + + +
+

About this schema document

+ +
+

+ This schema defines attributes and an attribute group suitable + for use by schemas wishing to allow xml:base, + xml:lang, xml:space or + xml:id attributes on elements they define. +

+

+ To enable this, such a schema must import this schema for + the XML namespace, e.g. as follows: +

+
+          <schema . . .>
+           . . .
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2001/xml.xsd"/>
+     
+

+ or +

+
+           <import namespace="http://www.w3.org/XML/1998/namespace"
+                      schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
+     
+

+ Subsequently, qualified reference to any of the attributes or the + group defined below will have the desired effect, e.g. +

+
+          <type . . .>
+           . . .
+           <attributeGroup ref="xml:specialAttrs"/>
+     
+

+ will define a type which will schema-validate an instance element + with any of those attributes. +

+
+
+
+
+ + + +
+

Versioning policy for this schema document

+
+

+ In keeping with the XML Schema WG's standard versioning + policy, this schema document will persist at + + http://www.w3.org/2009/01/xml.xsd. +

+

+ At the date of issue it can also be found at + + http://www.w3.org/2001/xml.xsd. +

+

+ The schema document at that URI may however change in the future, + in order to remain compatible with the latest version of XML + Schema itself, or with the XML namespace itself. In other words, + if the XML Schema or XML namespaces change, the version of this + document at + http://www.w3.org/2001/xml.xsd + + will change accordingly; the version at + + http://www.w3.org/2009/01/xml.xsd + + will not change. +

+

+ Previous dated (and unchanging) versions of this schema + document are at: +

+ +
+
+
+
+ +
+ diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/test/java/ca/uhn/fhir/jpa/demo/ServiceTest.java b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/test/java/ca/uhn/fhir/jpa/demo/ServiceTest.java new file mode 100644 index 00000000000..2b663ce1192 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/test/java/ca/uhn/fhir/jpa/demo/ServiceTest.java @@ -0,0 +1,32 @@ +package ca.uhn.fhir.jpa.demo; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.context.support.FileSystemXmlApplicationContext; + +public class ServiceTest { + + private static FileSystemXmlApplicationContext ourAppCtx; + + @BeforeClass + public static void beforeClass() { + ourAppCtx = new FileSystemXmlApplicationContext( + "src/test/resources/test-hapi-fhir-server-database-config.xml", + "src/main/webapp/WEB-INF/hapi-fhir-server-config.xml", + "src/main/webapp/WEB-INF/non-fhir-services-config.xml" + ); + ourAppCtx.start(); + } + + @Test + public void testSomething() { + + } + + @AfterClass + public static void afterClass() { + ourAppCtx.stop(); + } +} + diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/test/resources/test-hapi-fhir-server-config.xml b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/test/resources/test-hapi-fhir-server-config.xml new file mode 100644 index 00000000000..054e108e2e9 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/test/resources/test-hapi-fhir-server-config.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/test/resources/test-hapi-fhir-server-database-config.xml b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/test/resources/test-hapi-fhir-server-database-config.xml new file mode 100644 index 00000000000..7f2d4baee76 --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/test/resources/test-hapi-fhir-server-database-config.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + ca.uhn.fhir.jpa.entity + ca.uhn.fhir.jpa.demo.entity + + + + + + + + + + + + diff --git a/hapi-fhir-tutorial/jpaserver-example-with-custom/src/test/resources/test-non-fhir-services-config.xml b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/test/resources/test-non-fhir-services-config.xml new file mode 100644 index 00000000000..a94a56f55ac --- /dev/null +++ b/hapi-fhir-tutorial/jpaserver-example-with-custom/src/test/resources/test-non-fhir-services-config.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index c35ab4ecb0e..e8127dcf0c9 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ pom 1.0-SNAPSHOT HAPI-FHIR - http://hl7api.sourceforge.net/hapi-fhir/ + http://jamesagnew.github.io/hapi-fhir/ University Health Network diff --git a/restful-server-example/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml b/restful-server-example/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml index 3afad987817..2272b7bafa7 100644 --- a/restful-server-example/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml +++ b/restful-server-example/src/main/webapp/WEB-INF/hapi-fhir-tester-config.xml @@ -21,6 +21,11 @@ example , DSTU1 , Restful Server Example , http://localhost:8080/fhir + + diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 3d051bb05fc..9762e809472 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -130,6 +130,63 @@ JPA server failed to index resources containing ContactPointDt elements with populated values (e.g. Patient.telecom). Thanks to Mohammad Jafari for reporting! + + Terser's IModelVisitor now supplies to the path to the element. This is + an API change, but I don't think there are many users of the IModelVisitor yet. + Please let us know if this is a big hardship and we can find an alternate way + of making this change. + + + Prevent server from returning a Content-Location header for search + response when using the DSTU2 bundle format + + + JPA server (uhnfhirtest.uhn.ca) sometimes included an empty + "text" element in Bundles being returned. + + + Add a framework for the Web Tester UI to allow its internal FHIR client to + be configured (e.g. to add an authorization interceptor so that it adds + credentials to client requests it makes). Thanks to Harsha Kumara for + the suggestion! + + + Fix regression in early 1.0 builds where resource type sometimes does not get + populated in a resource ID when the resource is parsed. Thanks to + Nick Peterson for reporting, and for providing a test case! + + + Allow fluent/generic client users to execute a transaction using a raw string (containing a bundle resource) + as input instead of a Bundle resource class instance. + + + Disable date validation in the web tester UI, so that it is possible to + enter partial dates, or dates without times, or even test out invalid date + options. + + + Make BaseElement#getUndeclaredExtensions() and BaseElement#getUndeclaredExtensions() return + a mutable list so that it is possible to delete extensions from a resource instance. + + + Server conformance statement check in clients (this is the check + where the first time a given FhirContext is used to access a given server + base URL, it will first check the server's Conformance statement to ensure + that it supports the correct version of FHIR) now uses any + registered client interceptors. In addition, IGenericClient now has a method + "forceConformanceCheck()" which manually triggers this check. Thanks to + Doug Martin for reporting and suggesting! + + + Rename the Spring Bean definition for the JPA server EntityManager from + "myEntityManagerFactory" to just "entityManagerFactory" as this is the + default bean name expected in other parts of the Spring framework. + Thanks to Mohammad Jafari for the suggestion! + + + Improve error message when a user tries to perform a create/update with an invalid + or missing Content-Type header. Thanks to wanghaisheng for reporting! + Add a new configuration method on the parsers, setStripVersionsFromReferences(boolean)
]]> which diff --git a/src/site/xdoc/doc_rest_client.xml b/src/site/xdoc/doc_rest_client.xml index 8128e6a589f..40e2cb8dc7b 100644 --- a/src/site/xdoc/doc_rest_client.xml +++ b/src/site/xdoc/doc_rest_client.xml @@ -165,8 +165,10 @@

Search - Using HTTP POST

- The FHIR specification allows the use of an HTTP POST to transmit a search to a server instead of using - an HTTP GET. With this style of search, the search parameters are included in the request body instead + The FHIR specification allows the use of an HTTP POST to transmit a search to a server instead of + using + an HTTP GET. With this style of search, the search parameters are included in the request body + instead of the request URL, which can be useful if you need to transmit a search with a large number of parameters.

@@ -176,7 +178,7 @@ case the client automatically switches to POST.

- An alternate form of the search URL (using a URL ending with _search) was also + An alternate form of the search URL (using a URL ending with_search) was also supported in FHIR DSTU1. This form is no longer valid in FHIR DSTU2, but HAPI retains support for using this form in order to interoperate with servers which use it.

@@ -211,18 +213,18 @@ - +

Conditional Creates

FHIR also specifies a type of update called "conditional create", where a set of search parameters are provided and a new resource is only - created if no existing resource matches those parameters. See the - FHIR specification for more information on conditional creation. + created if no existing resource matches those parameters. See the + FHIR specification for more information on conditional creation.

- + + value="examples/src/main/java/example/GenericClientExample.java"/> @@ -276,7 +278,7 @@

Conditional Deletes

- Conditional deletions are also possible, which is a form where + Conditional deletions are also possible, which is a form where instead of deleting a resource using its logical ID, you specify a set of search criteria and a single resource is deleted if it matches that criteria. Note that this is not a mechanism @@ -284,9 +286,9 @@ on conditional deletes and how they are used.

- + + value="examples/src/main/java/example/GenericClientExample.java"/> @@ -305,19 +307,19 @@ - +

Conditional Updates

FHIR also specifies a type of update called "conditional updates", where insetad of using the logical ID of a resource to update, a set of search parameters is provided. If a single resource matches that set of - parameters, that resource is updated. See the FHIR specification for + parameters, that resource is updated. See the FHIR specification for information on how conditional updates work.

- + + value="examples/src/main/java/example/GenericClientExample.java"/>

ETags and Resource Contention

diff --git a/src/site/xdoc/doc_rest_client_http_config.xml b/src/site/xdoc/doc_rest_client_http_config.xml index 9e384fc664c..c9079dfe197 100644 --- a/src/site/xdoc/doc_rest_client_http_config.xml +++ b/src/site/xdoc/doc_rest_client_http_config.xml @@ -10,46 +10,66 @@
- +

- RESTful clients (both Generic and Annotation-Driven) use + RESTful clients (both Generic and Annotation-Driven) use Apache HTTP Client - as a provider. The Apache HTTP Client is very powerful and extremely flexible, - but can be confusing at first to configure, because of the low-level approach that - the library uses. + as a provider. The Apache HTTP Client is very powerful and extremely + flexible, + but can be confusing at first to configure, because of the low-level + approach that + the library uses.

- In many cases, the default configuration should suffice. However, if you require anything - more sophisticated (username/password, HTTP proxy settings, etc.) you will need - to configure the underlying client. + In many cases, the default configuration should suffice. However, + if you require anything + more sophisticated (username/password, HTTP + proxy settings, etc.) you will need + to configure the underlying + client.

- +

The underlying client configuration is provided by accessing the IRestfulClientFactory class from the FhirContext.

- +

Note that individual requests and responses - can be tweaked using Client Interceptors. + can be tweaked using + Client Interceptors + .

- + + +

+ The following example shows how to configure low level + socket timeouts for client operations. +

+ + + + +
+ - +

The following example shows how to configure the use of an HTTP proxy in the client.

- + - - + + - +
- +
diff --git a/src/site/xdoc/doc_server_tester.xml.vm b/src/site/xdoc/doc_server_tester.xml.vm index d57a893ee5e..f09ae400a98 100644 --- a/src/site/xdoc/doc_server_tester.xml.vm +++ b/src/site/xdoc/doc_server_tester.xml.vm @@ -170,6 +170,43 @@ + +
+ +

+ The testing UI uses its own client to talk to your FHIR server. In other words, there are no + special "hooks" which the tested uses to retrieve data from your server, it acts as an HTTP client + just like any other client. +

+ +

+ This does mean that if your server has any authorization requirements, you will need to configure the + tester UI to meet those requirements. For example, if your server has been configured to require + a HTTP Basic Auth header (e.g. Authorization: Basic VVNFUjpQQVNT) you need to + configure the tester UI to send those credentials across when it is acting as + a FHIR client. +

+ +

+ This is done by providing your own implementation of the ITestingUiClientFactory + interface. This interface takes in some details about the incoming request and produces + a client. +

+ +

+ The following example shows an implementation of the client factory which registers + an authorization interceptor with hardcoded credentials. +

+ + + + +

+ This client factory is then registered with the TesterConfig in the hapi-fhir-tester-config.xml + file, as shown above. +

+ +
diff --git a/src/site/xdoc/download.xml.vm b/src/site/xdoc/download.xml.vm index cf54ee9ca1c..a9c1945ba14 100644 --- a/src/site/xdoc/download.xml.vm +++ b/src/site/xdoc/download.xml.vm @@ -9,15 +9,34 @@
+

+ If you are developing applications in Java, the easiest way to use HAPI is + to use a build system which handles dependency management automatically. The + two most common such systems are + Apache Maven and + Gradle. These systems will automatically + download "dependency" libraries and add them to your classpath. + If you are not using one of these systems, you can still manually download + the latest release of HAPI by looking in the + GitHub Release Section. +

+

To use HAPI in your application, at a minimum you need to include the HAPI-FHIR core JAR hapi-fhir-base-[version].jar, as well as at least one "structures" JAR. The structures JAR contains classes with the resource and datatype definitions for a given - version of FHIR. At this time, the only official release of FHIR is the "DSTU1" release, - which is available by including the hapi-fhir-structures-dstu-[version].jar. + version of FHIR.

- If you use Maven, you can include these JARs with the following dependency tags: + At this time, the only official release of FHIR is the "DSTU1" release, + which is available by including the hapi-fhir-structures-dstu-[version].jar. + Note that the ballot process for the next release (DSTU2) has not yet been + completed, but many users are already targeting it for new development as it + is likely to be approved without significant changes. +

+

+ If you use Maven, you can include these JARs with the following dependency tags + to enable DSTU1 support:

ca.uhn.hapi.fhir @@ -42,7 +61,9 @@

- If you are using Maven, add the following dependency to include DSTU2 resources: + If you are using Maven, add the following dependency to include DSTU2 resources. + Note that if you do not need to support DSTU1 resources, you do not need to + include the "hapi-fhir-structures-dstu" artifact.

ca.uhn.hapi.fhir @@ -52,13 +73,22 @@ - - +

- Non-maven users can grab the binary releases from the - GitHub Release Section. + If you are using Gradle, you may use the following dependencies. Note that if + you are doing Android development, you may want to use our + Android build instead.

- +

+ DSTU1: +

+ +

+ DSTU2: +

+
@@ -69,21 +99,34 @@ Snapshot builds of HAPI are pre-release builds which can contain fixes and new features not yet released in a formal release. To use snapshot builds of HAPI you may need to add a reference to the OSS snapshot - repository to your project pom.xml: + repository to your project build file. +

+

+ Using Maven:

- - oss-snapshots - - true - - https://oss.sonatype.org/content/repositories/snapshots/ - + + oss-snapshots + + true + + https://oss.sonatype.org/content/repositories/snapshots/ + ]]>

- Non Maven users can often also find snapshot builds in the Maven repository + Using Gradle: +

+ + +

+ Non Maven users can often also find snapshot builds in the Maven repository manually by searching here.