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 e6e52bf0522..900a54d19d3 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
@@ -694,12 +694,15 @@ public class FhirContext {
* The profile string, e.g. "http://example.com/some_patient_profile"
. Must not be
* null
or empty.
* @param theClass
- * The resource type. Must not be null
or empty.
+ * The resource type, or null
to clear any existing type
*/
public void setDefaultTypeForProfile(String theProfile, Class extends IBaseResource> theClass) {
Validate.notBlank(theProfile, "theProfile must not be null or empty");
- Validate.notNull(theClass, "theProfile must not be null");
- myDefaultTypeForProfile.put(theProfile, theClass);
+ if (theClass == null) {
+ myDefaultTypeForProfile.remove(theProfile);
+ } else {
+ myDefaultTypeForProfile.put(theProfile, theClass);
+ }
}
/**
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
index 3ee157f83f3..044c76737fd 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java
@@ -733,6 +733,17 @@ public abstract class BaseHapiFhirDao implements IDao {
extractTagsRi((IAnyResource) theResource, theEntity, allDefs);
}
+ RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
+ if (def.isStandardType() == false) {
+ String profile = def.getResourceProfile("");
+ if (isNotBlank(profile)) {
+ TagDefinition tag = getTag(TagTypeEnum.PROFILE, NS_JPA_PROFILE, profile, null);
+ allDefs.add(tag);
+ theEntity.addTag(tag);
+ theEntity.setHasTags(true);
+ }
+ }
+
ArrayList existingTags = new ArrayList();
if (theEntity.isHasTags()) {
existingTags.addAll(theEntity.getTags());
@@ -1003,9 +1014,11 @@ public abstract class BaseHapiFhirDao implements IDao {
@Override
public IBaseResource toResource(BaseHasResource theEntity, boolean theForHistoryOperation) {
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
- return toResource(type.getImplementingClass(), theEntity, theForHistoryOperation);
+ Class extends IBaseResource> resourceType = type.getImplementingClass();
+ return toResource(resourceType, theEntity, theForHistoryOperation);
}
+ @SuppressWarnings("unchecked")
@Override
public R toResource(Class theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation) {
String resourceText = null;
@@ -1022,14 +1035,34 @@ public abstract class BaseHapiFhirDao implements IDao {
break;
}
+ /*
+ * Use the appropriate custom type if one is specified in the context
+ */
+ Class resourceType = theResourceType;
+ if (myContext.hasDefaultTypeForProfile()) {
+ for (BaseTag nextTag : theEntity.getTags()) {
+ if (nextTag.getTag().getTagType() == TagTypeEnum.PROFILE) {
+ String profile = nextTag.getTag().getCode();
+ if (isNotBlank(profile)) {
+ Class extends IBaseResource> newType = myContext.getDefaultTypeForProfile(profile);
+ if (newType != null && theResourceType.isAssignableFrom(newType)) {
+ ourLog.debug("Using custom type {} for profile: {}", newType.getName(), profile);
+ resourceType = (Class) newType;
+ break;
+ }
+ }
+ }
+ }
+ }
+
IParser parser = theEntity.getEncoding().newParser(getContext(theEntity.getFhirVersion()));
R retVal;
try {
- retVal = parser.parseResource(theResourceType, resourceText);
+ retVal = parser.parseResource(resourceType, resourceText);
} catch (Exception e) {
StringBuilder b = new StringBuilder();
b.append("Failed to parse database resource[");
- b.append(theResourceType);
+ b.append(resourceType);
b.append("/");
b.append(theEntity.getIdDt().getIdPart());
b.append(" (pid ");
@@ -1045,10 +1078,10 @@ public abstract class BaseHapiFhirDao implements IDao {
if (retVal instanceof IResource) {
IResource res = (IResource) retVal;
- retVal = populateResourceMetadataHapi(theResourceType, theEntity, theForHistoryOperation, res);
+ retVal = populateResourceMetadataHapi(resourceType, theEntity, theForHistoryOperation, res);
} else {
IAnyResource res = (IAnyResource) retVal;
- retVal = populateResourceMetadataRi(theResourceType, theEntity, theForHistoryOperation, res);
+ retVal = populateResourceMetadataRi(resourceType, theEntity, theForHistoryOperation, res);
}
return retVal;
}
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/CustomObservationDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/CustomObservationDstu3.java
new file mode 100644
index 00000000000..d934433296a
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/CustomObservationDstu3.java
@@ -0,0 +1,29 @@
+package ca.uhn.fhir.jpa.dao.dstu3;
+
+import org.hl7.fhir.dstu3.model.Observation;
+import org.hl7.fhir.dstu3.model.StringType;
+
+import ca.uhn.fhir.model.api.annotation.Child;
+import ca.uhn.fhir.model.api.annotation.Extension;
+import ca.uhn.fhir.model.api.annotation.ResourceDef;
+
+@ResourceDef(name = "Observation", profile = CustomObservationDstu3.PROFILE)
+public class CustomObservationDstu3 extends Observation {
+
+ public static final String PROFILE = "http://custom_ObservationDstu3";
+
+ private static final long serialVersionUID = 1L;
+
+ @Extension(definedLocally = false, isModifier = false, url = "http://eyeColour")
+ @Child(name = "eyeColour")
+ private StringType myEyeColour;
+
+ public StringType getEyeColour() {
+ return myEyeColour;
+ }
+
+ public void setEyeColour(StringType theEyeColour) {
+ myEyeColour = theEyeColour;
+ }
+
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCustomTypeDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCustomTypeDstu3Test.java
new file mode 100644
index 00000000000..d53fca64e2d
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCustomTypeDstu3Test.java
@@ -0,0 +1,45 @@
+package ca.uhn.fhir.jpa.dao.dstu3;
+
+import static org.junit.Assert.assertEquals;
+
+import org.hl7.fhir.dstu3.model.StringType;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import ca.uhn.fhir.jpa.dao.SearchParameterMap;
+import ca.uhn.fhir.rest.server.IBundleProvider;
+
+@SuppressWarnings({ })
+public class FhirResourceDaoCustomTypeDstu3Test extends BaseJpaDstu3Test {
+
+ private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCustomTypeDstu3Test.class);
+
+ @Before
+ public void before() {
+ myFhirCtx.setDefaultTypeForProfile(CustomObservationDstu3.PROFILE, CustomObservationDstu3.class);
+ }
+
+ @Test
+ public void testSaveAndRestore() {
+ CustomObservationDstu3 obs = new CustomObservationDstu3();
+ obs.setEyeColour(new StringType("blue"));
+
+ IIdType id = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
+
+ CustomObservationDstu3 read = (CustomObservationDstu3) myObservationDao.read(id);
+ assertEquals("blue", read.getEyeColour().getValue());
+
+ IBundleProvider found = myObservationDao.search(new SearchParameterMap());
+ assertEquals(1, found.size());
+ CustomObservationDstu3 search = (CustomObservationDstu3) found.getResources(0, 1).get(0);
+ assertEquals("blue", search.getEyeColour().getValue());
+
+ }
+
+ @After
+ public void after() {
+ myFhirCtx.setDefaultTypeForProfile(CustomObservationDstu3.PROFILE, null);
+ }
+}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDocumentDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDocumentDstu3Test.java
index 0b1684c19a9..a9370b6e83a 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDocumentDstu3Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDocumentDstu3Test.java
@@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.dao.dstu3;
+import java.nio.charset.StandardCharsets;
+
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.Bundle;
import org.junit.AfterClass;
@@ -8,7 +10,6 @@ import org.junit.Test;
import ca.uhn.fhir.jpa.dao.DaoMethodOutcome;
import ca.uhn.fhir.util.TestUtil;
-@SuppressWarnings("unchecked")
public class FhirResourceDaoDocumentDstu3Test extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDocumentDstu3Test.class);
@@ -21,7 +22,7 @@ public class FhirResourceDaoDocumentDstu3Test extends BaseJpaDstu3Test {
@Test
public void testPostDocument() throws Exception {
- String input = IOUtils.toString(getClass().getResourceAsStream("/sample-document.xml"));
+ String input = IOUtils.toString(getClass().getResourceAsStream("/sample-document.xml"), StandardCharsets.UTF_8);
Bundle inputBundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, input);
DaoMethodOutcome responseBundle = myBundleDao.create(inputBundle, mySrd);
}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java
index 6be0bd893a7..2a007253dc3 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3SearchNoFtTest.java
@@ -13,11 +13,14 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
+import java.io.IOException;
import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.ContactPoint.ContactPointSystem;
@@ -81,6 +84,18 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
assertEquals(Patient.class, foundResources.get(0).getClass());
assertEquals(Condition.class, foundResources.get(1).getClass());
}
+
+ /**
+ * #454
+ */
+ @Test
+ public void testIndexWithUtf8Chars() throws IOException {
+ String input = IOUtils.toString(getClass().getResourceAsStream("/bug454_utf8.json"), StandardCharsets.UTF_8);
+
+ CodeSystem cs = (CodeSystem) myFhirCtx.newJsonParser().parseResource(input);
+ myCodeSystemDao.create(cs);
+ }
+
@Test
public void testCodeSearch() {
diff --git a/hapi-fhir-jpaserver-base/src/test/resources/bug454_utf8.json b/hapi-fhir-jpaserver-base/src/test/resources/bug454_utf8.json
new file mode 100644
index 00000000000..ff362522f48
--- /dev/null
+++ b/hapi-fhir-jpaserver-base/src/test/resources/bug454_utf8.json
@@ -0,0 +1,16 @@
+{
+ "resourceType": "CodeSystem",
+ "url": "http://nestvision.com/fhir/myexample-yh",
+ "name": "NestVision Restful Interactions",
+ "status": "active",
+ "publisher": "杨浩",
+ "description": "this is a codesystem example.",
+ "caseSensitive": true,
+ "concept": [
+ {
+ "code": "mygod",
+ "display": "wodetian",
+ "definition": "this is a translate."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt b/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt
index 7864fd0dd8e..626a2adeab3 100644
--- a/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt
+++ b/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt
@@ -1,3 +1,5 @@
call SYSCS_UTIL.SYSCS_COMPRESS_TABLE('SA', 'HFJ_SEARCH_RESULT', 1);
CALL SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE( 'SA', 'HFJ_SEARCH_RESULT', 0, 0, 1 );
+
+dd if=/dev/urandom of=/opt/glassfish/tmp.tmp bs=1M count=500
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 28da318b75a..eee67402d6c 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -102,6 +102,11 @@
JAX-RS server was not able to handle the new mime types defined
in STU3
+
+ JPA server did not handle custom types when being called
+ programatically (I.e. not through HTTP interface). Thanks to
+ Anthony Mei for pointing this out!
+