diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index d0f6a743795..ebef43721bc 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -223,6 +223,13 @@ ANDROID + + + ca.uhn.hapi.fhir + hapi-fhir-structures-dstu + 0.8-SNAPSHOT + + @@ -238,6 +245,18 @@ false true + + + + ca.uhn.hapi.fhir:hapi-fhir-structures-dstu + org.glassfish:javax.json + org.codehaus.woodstox:woodstox-core-asl + javax.xml.stream:stax-api + org.codehaus.woodstox:stax2-api + + javax.xml.stream diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java index 01f82665ec5..7828dd97084 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeElementDefinition.java @@ -137,7 +137,7 @@ public abstract class BaseRuntimeElementDefinition { for (RuntimeChildDeclaredExtensionDefinition next : myExtensions) { String extUrl = next.getExtensionUrl(); if (myUrlToExtension.containsKey(extUrl)) { - throw new ConfigurationException("Duplicate extension URL: " + extUrl); + throw new ConfigurationException("Duplicate extension URL[" + extUrl + "] in Element[" + getName() + "]"); } else { myUrlToExtension.put(extUrl, next); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java index 8a0f0ba3ced..3c04c523a70 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/ModelScanner.java @@ -294,7 +294,7 @@ class ModelScanner { } if (blockDefinition == null && datatypeDefinition == null && resourceDefinition == null) { - throw new ConfigurationException("Resource type does not contain any valid HAPI-FHIR annotations: " + theClass.getCanonicalName()); + throw new ConfigurationException("Resource class[" + theClass.getName() + "] does not contain any valid HAPI-FHIR annotations"); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseHumanNameDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseHumanNameDt.java index 41fd7dad8a6..210fa93e0d7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseHumanNameDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/base/composite/BaseHumanNameDt.java @@ -102,28 +102,24 @@ public abstract class BaseHumanNameDt extends BaseIdentifiableElement { public String getSuffixAsSingleString() { return ca.uhn.fhir.util.DatatypeUtil.joinStringsSpaceSeparated(getSuffix()); } - + /** - * Gets the value(s) for text (Text representation of the full name). - * creating it if it does - * not exist. Will not return null. + * Gets the value(s) for text (Text representation of the full name). creating it if it does not exist. Will not return null. * - *

- * Definition: - * A full text representation of the name - *

+ *

+ * Definition: A full text representation of the name + *

*/ - public abstract StringDt getText() ; + public abstract StringDt getTextElement(); /** * Sets the value(s) for text (Text representation of the full name) * - *

- * Definition: - * A full text representation of the name - *

+ *

+ * Definition: A full text representation of the name + *

*/ - public abstract BaseHumanNameDt setText(StringDt theValue); + public abstract BaseHumanNameDt setText(StringDt theValue); @Override public String toString() { @@ -132,15 +128,26 @@ public abstract class BaseHumanNameDt extends BaseIdentifiableElement { b.append("given", getGivenAsSingleString()); return b.toString(); } - - public String getNameAsSingleString(){ + + /** + * Returns all of the components of the name (prefix, given, family, suffix) as a + * single string with a single spaced string separating each part. + *

+ * If none of the parts are populated, returns the {@link #getTextElement() text} + * element value instead. + *

+ */ + public String getNameAsSingleString() { List nameParts = new ArrayList(); nameParts.addAll(getPrefix()); nameParts.addAll(getGiven()); nameParts.addAll(getFamily()); nameParts.addAll(getSuffix()); - if(nameParts.size() > 0) return ca.uhn.fhir.util.DatatypeUtil.joinStringsSpaceSeparated(nameParts); - else return getText().getValue(); + if (nameParts.size() > 0) { + return ca.uhn.fhir.util.DatatypeUtil.joinStringsSpaceSeparated(nameParts); + } else { + return getTextElement().getValue(); + } } - + } 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 8cf5fb588e2..c8c448bd7e6 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 @@ -41,6 +41,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.util.VersionUtil; + import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.exception.ExceptionUtils; @@ -50,6 +51,7 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + import java.io.IOException; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; @@ -723,16 +725,19 @@ public class RestfulServer extends HttpServlet { Collection resourceProvider = getResourceProviders(); if (resourceProvider != null) { - Map, IResourceProvider> typeToProvider = new HashMap, IResourceProvider>(); + Map typeToProvider = new HashMap(); for (IResourceProvider nextProvider : resourceProvider) { + Class resourceType = nextProvider.getResourceType(); if (resourceType == null) { throw new NullPointerException("getResourceType() on class '" + nextProvider.getClass().getCanonicalName() + "' returned null"); } - if (typeToProvider.containsKey(resourceType)) { - throw new ServletException("Multiple providers for type: " + resourceType.getCanonicalName()); + + String resourceName = myFhirContext.getResourceDefinition(resourceType).getName(); + if (typeToProvider.containsKey(resourceName)) { + throw new ServletException("Multiple resource providers return resource type[" + resourceName + "]: First[" + typeToProvider.get(resourceName).getClass().getCanonicalName() + "] and Second[" + nextProvider.getClass().getCanonicalName() + "]"); } - typeToProvider.put(resourceType, nextProvider); + typeToProvider.put(resourceName, nextProvider); providedResourceScanner.scanForProvidedResources(nextProvider); } ourLog.info("Got {} resource providers", typeToProvider.size()); diff --git a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java index 07765b9beaa..81158f84644 100644 --- a/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java +++ b/hapi-fhir-structures-dstu/src/main/java/ca/uhn/fhir/model/dstu/FhirDstu1.java @@ -33,6 +33,7 @@ import java.util.Map; import ca.uhn.fhir.context.*; import ca.uhn.fhir.model.api.ICompositeDatatype; + import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.text.WordUtils; @@ -57,7 +58,7 @@ import ca.uhn.fhir.rest.server.provider.ServerProfileProvider; public class FhirDstu1 implements IFhirVersion { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirDstu1.class); - private Map myExtensionDefToCode = new HashMap(); +// private Map myExtensionDefToCode = new HashMap(); private String myId; @Override @@ -158,7 +159,7 @@ public class FhirDstu1 implements IFhirVersion { String expectedPath = StringUtils.join(path, '.'); - ourLog.info("Filling profile for: {} - Path: {}", expectedPath); + ourLog.debug("Filling profile for: {} - Path: {}", expectedPath); String name = def.getName(); if (!expectedPath.equals(name)) { path.pollLast(); @@ -267,11 +268,12 @@ public class FhirDstu1 implements IFhirVersion { return retVal; } - private void scanForExtensions(Profile theProfile, BaseRuntimeElementDefinition def) { + private Map scanForExtensions(Profile theProfile, BaseRuntimeElementDefinition def) { BaseRuntimeElementCompositeDefinition cdef = ((BaseRuntimeElementCompositeDefinition) def); + Map extensionDefToCode = new HashMap(); for (RuntimeChildDeclaredExtensionDefinition nextChild : cdef.getExtensions()) { - if (myExtensionDefToCode.containsKey(nextChild)) { + if (extensionDefToCode.containsKey(nextChild)) { continue; } @@ -288,10 +290,10 @@ public class FhirDstu1 implements IFhirVersion { } defn.setCode(code); - if (myExtensionDefToCode.values().contains(code)) { + if (extensionDefToCode.values().contains(code)) { throw new IllegalStateException("Duplicate extension code: " + code); } - myExtensionDefToCode.put(nextChild, code); + extensionDefToCode.put(nextChild, code); if (nextChild.getChildType() != null && IPrimitiveDatatype.class.isAssignableFrom(nextChild.getChildType())) { RuntimePrimitiveDatatypeDefinition pdef = (RuntimePrimitiveDatatypeDefinition) nextChild.getSingleChildOrThrow(); @@ -306,11 +308,13 @@ public class FhirDstu1 implements IFhirVersion { for (RuntimeChildDeclaredExtensionDefinition nextChildExt : pdef.getExtensions()) { StructureElementDefinitionType type = defn.getDefinition().addType(); type.setCode(DataTypeEnum.EXTENSION); - type.setProfile("#" + myExtensionDefToCode.get(nextChildExt)); + type.setProfile("#" + extensionDefToCode.get(nextChildExt)); } } } + + return extensionDefToCode; } @Override diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/context/DuplicateExtensionTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/context/DuplicateExtensionTest.java new file mode 100644 index 00000000000..a391d4214c0 --- /dev/null +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/context/DuplicateExtensionTest.java @@ -0,0 +1,82 @@ +package ca.uhn.fhir.context; + +import junit.framework.TestCase; + +import org.junit.Test; + +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.api.annotation.Child; +import ca.uhn.fhir.model.api.annotation.Description; +import ca.uhn.fhir.model.api.annotation.Extension; +import ca.uhn.fhir.model.api.annotation.ProvidesResources; +import ca.uhn.fhir.model.api.annotation.ResourceDef; +import ca.uhn.fhir.model.dstu.resource.Observation; +import ca.uhn.fhir.model.dstu.resource.Patient; +import ca.uhn.fhir.model.dstu.resource.Profile; +import ca.uhn.fhir.model.primitive.StringDt; + +/** + * Created by Bill de Beaubien on 12/10/2014. + */ +public class DuplicateExtensionTest extends TestCase { + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DuplicateExtensionTest.class); + + @Test + public void testScannerShouldAddProvidedResources() { + FhirContext ctx = new FhirContext(); + RuntimeResourceDefinition patientDef = ctx.getResourceDefinition(CustomPatient.class); + Profile profile = (Profile) patientDef.toProfile(); + + String res = ctx.newJsonParser().setPrettyPrint(true).encodeResourceToString(profile); + ourLog.info(res); + + RuntimeResourceDefinition observationDef = ctx.getResourceDefinition(CustomObservation.class); + profile = (Profile) observationDef.toProfile(); + } + + @ResourceDef(name = "Observation", id = "CustomObservation") + class CustomObservation extends Observation { + @Child(name = "idExt", order = 0) + @Extension(url = "http://foo.org#id", definedLocally = true, isModifier = false) + @Description(shortDefinition = "Contains the id of the resource") + private StringDt myIdExt; + + public StringDt getIdExt() { + if (myIdExt == null) { + myIdExt = new StringDt(); + } + return myIdExt; + } + + public void setIdExt(StringDt theIdExt) { + myIdExt = theIdExt; + } + } + + @ProvidesResources(resources = CustomObservation.class) + class CustomObservationProvider { + } + + @ResourceDef(name = "Patient", id = "CustomPatient") + class CustomPatient extends Patient { + @Child(name = "idExt", order = 0) + @Extension(url = "http://foo.org#id", definedLocally = true, isModifier = false) + @Description(shortDefinition = "Contains the id of the resource") + private StringDt myIdExt; + + public StringDt getIdExt() { + if (myIdExt == null) { + myIdExt = new StringDt(); + } + return myIdExt; + } + + public void setIdExt(StringDt theIdExt) { + myIdExt = theIdExt; + } + } + + @ProvidesResources(resources = CustomPatient.class) + class CustomPatientProvider { + } +} diff --git a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionTest.java b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionTest.java index fa53d6d58fe..594d55ba10d 100644 --- a/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionTest.java +++ b/hapi-fhir-structures-dstu/src/test/java/ca/uhn/fhir/rest/server/ServerInvalidDefinitionTest.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.rest.server; import static org.junit.Assert.*; import java.util.List; +import java.util.Map; import javax.servlet.ServletException; @@ -10,8 +11,13 @@ import org.hamcrest.core.StringContains; import org.junit.Test; import ca.uhn.fhir.model.api.BaseResource; +import ca.uhn.fhir.model.api.IElement; import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; +import ca.uhn.fhir.model.dstu.composite.ContainedDt; +import ca.uhn.fhir.model.dstu.composite.NarrativeDt; import ca.uhn.fhir.model.dstu.resource.Patient; +import ca.uhn.fhir.model.primitive.CodeDt; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.Read; @@ -62,6 +68,21 @@ public class ServerInvalidDefinitionTest { } } + @Test + public void testMultipleResourceProviderForSameType() { + RestfulServer srv = new RestfulServer(); + srv.setResourceProviders(new PatientResourceProvider1(), new PatientResourceProvider2()); + + try { + srv.init(); + fail(); + } catch (ServletException e) { + assertThat(e.getCause().toString(), StringContains.containsString("[Patient]")); + assertThat(e.getCause().toString(), StringContains.containsString("PatientResourceProvider1]")); + assertThat(e.getCause().toString(), StringContains.containsString("PatientResourceProvider2]")); + } + } + @Test public void testSearchWithId() { RestfulServer srv = new RestfulServer(); @@ -76,6 +97,19 @@ public class ServerInvalidDefinitionTest { } } + @Test + public void testProviderWithNonResourceType() { + RestfulServer srv = new RestfulServer(); + srv.setResourceProviders(new ProviderWithNonResourceType()); + + try { + srv.init(); + fail(); + } catch (ServletException e) { + assertThat(e.getCause().toString(), StringContains.containsString("ConfigurationException")); + assertThat(e.getCause().toString(), StringContains.containsString("does not contain any valid HAPI-FHIR annotations")); + } + } /** * Normal, should initialize properly */ @@ -127,6 +161,74 @@ public class ServerInvalidDefinitionTest { } } + + + public static class ProviderWithNonResourceType implements IResourceProvider { + + @Override + public Class getResourceType() { + return new IResource() { + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public List getAllPopulatedChildElementsOfType(Class theType) { + return null; + } + + @Override + public void setResourceMetadata(Map, Object> theMap) { + } + + @Override + public void setLanguage(CodeDt theLanguage) { + } + + @Override + public void setId(IdDt theId) { + } + + @Override + public NarrativeDt getText() { + return null; + } + + @Override + public String getResourceName() { + return null; + } + + @Override + public Map, Object> getResourceMetadata() { + return null; + } + + @Override + public CodeDt getLanguage() { + return null; + } + + @Override + public IdDt getId() { + return null; + } + + @Override + public ContainedDt getContained() { + return null; + } + }.getClass(); + } + + @Search + public List read(@IdParam IdDt theId, @RequiredParam(name = "aaa") StringParam theParam) { + return null; + } + + } public static class InvalidSpecialParameterNameResourceProvider implements IResourceProvider { @@ -156,4 +258,32 @@ public class ServerInvalidDefinitionTest { } + public static class PatientResourceProvider1 implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Read + public Patient read(@IdParam IdDt theId) { + return null; + } + + } + + public static class PatientResourceProvider2 implements IResourceProvider { + + @Override + public Class getResourceType() { + return Patient.class; + } + + @Read + public Patient read(@IdParam IdDt theId) { + return null; + } + + } + } diff --git a/hapi-fhir-structures-hl7org-dev/bin/.gitignore b/hapi-fhir-structures-hl7org-dev/bin/.gitignore new file mode 100644 index 00000000000..ae3c1726048 --- /dev/null +++ b/hapi-fhir-structures-hl7org-dev/bin/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/hapi-fhir-structures-hl7org-dev/bin/.project b/hapi-fhir-structures-hl7org-dev/bin/.project new file mode 100644 index 00000000000..5ed54fe4349 --- /dev/null +++ b/hapi-fhir-structures-hl7org-dev/bin/.project @@ -0,0 +1,23 @@ + + + hapi-fhir-structures-hl7org-dev + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + diff --git a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu/composite/HumanNameDt.java b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu/composite/HumanNameDt.java index c1785cbd862..3f68acc3b16 100644 --- a/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu/composite/HumanNameDt.java +++ b/hapi-tinder-plugin/src/main/java/ca/uhn/fhir/model/dstu/composite/HumanNameDt.java @@ -566,6 +566,11 @@ public class HumanNameDt return this; } + @Override + public StringDt getTextElement() { + return getText(); + } + diff --git a/pom.xml b/pom.xml index c58ee41af83..d2e95572564 100644 --- a/pom.xml +++ b/pom.xml @@ -114,6 +114,11 @@ Tahura Chaudhry University Health Network + + b.debeaubien + Bill de Beaubien + Systems Made Simple + diff --git a/restful-server-example/.settings/org.eclipse.wst.common.component b/restful-server-example/.settings/org.eclipse.wst.common.component index 576cd601a1b..38e539885da 100644 --- a/restful-server-example/.settings/org.eclipse.wst.common.component +++ b/restful-server-example/.settings/org.eclipse.wst.common.component @@ -9,7 +9,7 @@ uses - + consumes diff --git a/restful-server-example/src/main/java/ca/uhn/example/model/MyOrganization.java b/restful-server-example/src/main/java/ca/uhn/example/model/MyOrganization.java index e1b8a677aa0..b3d44892bac 100644 --- a/restful-server-example/src/main/java/ca/uhn/example/model/MyOrganization.java +++ b/restful-server-example/src/main/java/ca/uhn/example/model/MyOrganization.java @@ -43,7 +43,7 @@ public class MyOrganization extends Organization { * of this file. */ @Description(shortDefinition="Contains emergency contact details") - @Extension(url = "http://foo#billingCode", isModifier = false, definedLocally = true) + @Extension(url = "http://foo#emergencyContact", isModifier = false, definedLocally = true) @Child(name = "emergencyContact", min=0, max=Child.MAX_UNLIMITED) private List myEmergencyContact; diff --git a/src/changes/changes.xml b/src/changes/changes.xml index b5f0a0bdf53..bde50cc75dd 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -169,7 +169,11 @@ Encoding a Binary resource without a content type set should not result in a NullPointerException. Thanks to Alexander Kley for reporting! - + + Server gives a more helpful error message if multiple IResourceProvider implementations + are provided for the same resource type. Thanks to wanghaisheng for the idea! + + API CHANGE:]]> The TagList class previously implemented ArrayList semantics,