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

This commit is contained in:
James 2017-06-07 21:20:45 -04:00
commit 993e99770d
10 changed files with 434 additions and 277 deletions

View File

@ -323,13 +323,24 @@ public abstract class BaseClient implements IRestfulClient {
} }
} catch (DataFormatException e) { } catch (DataFormatException e) {
//FIXME potential null access on httpResquest String msg;
String msg = getFhirContext().getLocalizer().getMessage(BaseClient.class, "failedToParseResponse", httpRequest.getHttpVerbName(), httpRequest.getUri(), e.toString()); if ( httpRequest != null ) {
msg = getFhirContext().getLocalizer().getMessage(BaseClient.class, "failedToParseResponse", httpRequest.getHttpVerbName(), httpRequest.getUri(), e.toString());
}
else {
msg = getFhirContext().getLocalizer().getMessage(BaseClient.class, "failedToParseResponse", "UNKNOWN", "UNKNOWN", e.toString());
}
throw new FhirClientConnectionException(msg, e); throw new FhirClientConnectionException(msg, e);
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
throw new FhirClientConnectionException(e); throw new FhirClientConnectionException(e);
} catch (IOException e) { } catch (IOException e) {
String msg = getFhirContext().getLocalizer().getMessage(BaseClient.class, "ioExceptionDuringOperation", httpRequest.getHttpVerbName(), httpRequest.getUri(), e.toString()); String msg;
if ( httpRequest != null ) {
msg = getFhirContext().getLocalizer().getMessage(BaseClient.class, "failedToParseResponse", httpRequest.getHttpVerbName(), httpRequest.getUri(), e.toString());
}
else {
msg = getFhirContext().getLocalizer().getMessage(BaseClient.class, "failedToParseResponse", "UNKNOWN", "UNKNOWN", e.toString());
}
throw new FhirClientConnectionException(msg, e); throw new FhirClientConnectionException(msg, e);
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw e; throw e;

View File

@ -479,30 +479,36 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(theResource); TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(theResource);
if (tagList != null) { if (tagList != null) {
for (Tag next : tagList) { for (Tag next : tagList) {
TagDefinition tag = getTag(TagTypeEnum.TAG, next.getScheme(), next.getTerm(), next.getLabel()); TagDefinition tag = getTagOrNull(TagTypeEnum.TAG, next.getScheme(), next.getTerm(), next.getLabel());
allDefs.add(tag); if (tag != null) {
theEntity.addTag(tag); allDefs.add(tag);
theEntity.setHasTags(true); theEntity.addTag(tag);
theEntity.setHasTags(true);
}
} }
} }
List<BaseCodingDt> securityLabels = ResourceMetadataKeyEnum.SECURITY_LABELS.get(theResource); List<BaseCodingDt> securityLabels = ResourceMetadataKeyEnum.SECURITY_LABELS.get(theResource);
if (securityLabels != null) { if (securityLabels != null) {
for (BaseCodingDt next : securityLabels) { for (BaseCodingDt next : securityLabels) {
TagDefinition tag = getTag(TagTypeEnum.SECURITY_LABEL, next.getSystemElement().getValue(), next.getCodeElement().getValue(), next.getDisplayElement().getValue()); TagDefinition tag = getTagOrNull(TagTypeEnum.SECURITY_LABEL, next.getSystemElement().getValue(), next.getCodeElement().getValue(), next.getDisplayElement().getValue());
allDefs.add(tag); if (tag != null) {
theEntity.addTag(tag); allDefs.add(tag);
theEntity.setHasTags(true); theEntity.addTag(tag);
theEntity.setHasTags(true);
}
} }
} }
List<IdDt> profiles = ResourceMetadataKeyEnum.PROFILES.get(theResource); List<IdDt> profiles = ResourceMetadataKeyEnum.PROFILES.get(theResource);
if (profiles != null) { if (profiles != null) {
for (IIdType next : profiles) { for (IIdType next : profiles) {
TagDefinition tag = getTag(TagTypeEnum.PROFILE, NS_JPA_PROFILE, next.getValue(), null); TagDefinition tag = getTagOrNull(TagTypeEnum.PROFILE, NS_JPA_PROFILE, next.getValue(), null);
allDefs.add(tag); if (tag != null) {
theEntity.addTag(tag); allDefs.add(tag);
theEntity.setHasTags(true); theEntity.addTag(tag);
theEntity.setHasTags(true);
}
} }
} }
} }
@ -511,30 +517,36 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
List<? extends IBaseCoding> tagList = theResource.getMeta().getTag(); List<? extends IBaseCoding> tagList = theResource.getMeta().getTag();
if (tagList != null) { if (tagList != null) {
for (IBaseCoding next : tagList) { for (IBaseCoding next : tagList) {
TagDefinition tag = getTag(TagTypeEnum.TAG, next.getSystem(), next.getCode(), next.getDisplay()); TagDefinition tag = getTagOrNull(TagTypeEnum.TAG, next.getSystem(), next.getCode(), next.getDisplay());
allDefs.add(tag); if (tag != null) {
theEntity.addTag(tag); allDefs.add(tag);
theEntity.setHasTags(true); theEntity.addTag(tag);
theEntity.setHasTags(true);
}
} }
} }
List<? extends IBaseCoding> securityLabels = theResource.getMeta().getSecurity(); List<? extends IBaseCoding> securityLabels = theResource.getMeta().getSecurity();
if (securityLabels != null) { if (securityLabels != null) {
for (IBaseCoding next : securityLabels) { for (IBaseCoding next : securityLabels) {
TagDefinition tag = getTag(TagTypeEnum.SECURITY_LABEL, next.getSystem(), next.getCode(), next.getDisplay()); TagDefinition tag = getTagOrNull(TagTypeEnum.SECURITY_LABEL, next.getSystem(), next.getCode(), next.getDisplay());
allDefs.add(tag); if (tag != null) {
theEntity.addTag(tag); allDefs.add(tag);
theEntity.setHasTags(true); theEntity.addTag(tag);
theEntity.setHasTags(true);
}
} }
} }
List<? extends IPrimitiveType<String>> profiles = theResource.getMeta().getProfile(); List<? extends IPrimitiveType<String>> profiles = theResource.getMeta().getProfile();
if (profiles != null) { if (profiles != null) {
for (IPrimitiveType<String> next : profiles) { for (IPrimitiveType<String> next : profiles) {
TagDefinition tag = getTag(TagTypeEnum.PROFILE, NS_JPA_PROFILE, next.getValue(), null); TagDefinition tag = getTagOrNull(TagTypeEnum.PROFILE, NS_JPA_PROFILE, next.getValue(), null);
allDefs.add(tag); if (tag != null) {
theEntity.addTag(tag); allDefs.add(tag);
theEntity.setHasTags(true); theEntity.addTag(tag);
theEntity.setHasTags(true);
}
} }
} }
} }
@ -662,28 +674,28 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return mySearchParamRegistry.getActiveSearchParams(theResourceDef.getName()).values(); return mySearchParamRegistry.getActiveSearchParams(theResourceDef.getName()).values();
} }
protected TagDefinition getTag(TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) { protected TagDefinition getTagOrNull(TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
if (isBlank(theScheme) && isBlank(theTerm) && isBlank(theLabel)) {
return null;
}
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<TagDefinition> cq = builder.createQuery(TagDefinition.class); CriteriaQuery<TagDefinition> cq = builder.createQuery(TagDefinition.class);
Root<TagDefinition> from = cq.from(TagDefinition.class); Root<TagDefinition> from = cq.from(TagDefinition.class);
//@formatter:off
if (isNotBlank(theScheme)) { if (isNotBlank(theScheme)) {
cq.where( cq.where(
builder.and( builder.and(
builder.equal(from.get("myTagType"), theTagType), builder.equal(from.get("myTagType"), theTagType),
builder.equal(from.get("mySystem"), theScheme), builder.equal(from.get("mySystem"), theScheme),
builder.equal(from.get("myCode"), theTerm)) builder.equal(from.get("myCode"), theTerm)));
);
} else { } else {
cq.where( cq.where(
builder.and( builder.and(
builder.equal(from.get("myTagType"), theTagType), builder.equal(from.get("myTagType"), theTagType),
builder.isNull(from.get("mySystem")), builder.isNull(from.get("mySystem")),
builder.equal(from.get("myCode"), theTerm)) builder.equal(from.get("myCode"), theTerm)));
);
} }
//@formatter:on
TypedQuery<TagDefinition> q = myEntityManager.createQuery(cq); TypedQuery<TagDefinition> q = myEntityManager.createQuery(cq);
try { try {
@ -938,10 +950,12 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
if (def.isStandardType() == false) { if (def.isStandardType() == false) {
String profile = def.getResourceProfile(""); String profile = def.getResourceProfile("");
if (isNotBlank(profile)) { if (isNotBlank(profile)) {
TagDefinition tag = getTag(TagTypeEnum.PROFILE, NS_JPA_PROFILE, profile, null); TagDefinition tag = getTagOrNull(TagTypeEnum.PROFILE, NS_JPA_PROFILE, profile, null);
allDefs.add(tag); if (tag != null) {
theEntity.addTag(tag); allDefs.add(tag);
theEntity.setHasTags(true); theEntity.addTag(tag);
theEntity.setHasTags(true);
}
} }
} }
@ -1151,7 +1165,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
if (dao == null) { if (dao == null) {
throw new InternalErrorException("No DAO for resource type: " + theResourceType.getName()); throw new InternalErrorException("No DAO for resource type: " + theResourceType.getName());
} }
Set<Long> ids = dao.searchForIds(paramMap); Set<Long> ids = dao.searchForIds(paramMap);
return ids; return ids;

View File

@ -114,12 +114,14 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
entity.setHasTags(true); entity.setHasTags(true);
TagDefinition def = getTag(TagTypeEnum.TAG, theScheme, theTerm, theLabel); TagDefinition def = getTagOrNull(TagTypeEnum.TAG, theScheme, theTerm, theLabel);
BaseTag newEntity = entity.addTag(def); if (def != null) {
BaseTag newEntity = entity.addTag(def);
myEntityManager.persist(newEntity);
myEntityManager.merge(entity); myEntityManager.persist(newEntity);
myEntityManager.merge(entity);
}
ourLog.info("Processed addTag {}/{} on {} in {}ms", new Object[] { theScheme, theTerm, theId, w.getMillisAndRestart() }); ourLog.info("Processed addTag {}/{} on {} in {}ms", new Object[] { theScheme, theTerm, theId, w.getMillisAndRestart() });
} }
@ -439,9 +441,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
if (!hasTag) { if (!hasTag) {
entity.setHasTags(true); entity.setHasTags(true);
TagDefinition def = getTag(nextDef.getTagType(), nextDef.getSystem(), nextDef.getCode(), nextDef.getDisplay()); TagDefinition def = getTagOrNull(nextDef.getTagType(), nextDef.getSystem(), nextDef.getCode(), nextDef.getDisplay());
BaseTag newEntity = entity.addTag(def); if (def != null) {
myEntityManager.persist(newEntity); BaseTag newEntity = entity.addTag(def);
myEntityManager.persist(newEntity);
}
} }
} }
//@formatter:on //@formatter:on

View File

@ -0,0 +1,29 @@
package ca.uhn.fhir.jpa.dao.data;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.springframework.data.jpa.repository.JpaRepository;
import ca.uhn.fhir.jpa.entity.ResourceTag;
public interface IResourceTagDao extends JpaRepository<ResourceTag, Long> {
// nothing
}

View File

@ -0,0 +1,29 @@
package ca.uhn.fhir.jpa.dao.data;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.springframework.data.jpa.repository.JpaRepository;
import ca.uhn.fhir.jpa.entity.TagDefinition;
public interface ITagDefinitionDao extends JpaRepository<TagDefinition, Long> {
// nothing
}

View File

@ -77,8 +77,7 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.ISearchParamRegistry; import ca.uhn.fhir.jpa.dao.ISearchParamRegistry;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao; import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest; import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
@ -105,6 +104,10 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
private static JpaValidationSupportChainDstu3 ourJpaValidationSupportChainDstu3; private static JpaValidationSupportChainDstu3 ourJpaValidationSupportChainDstu3;
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao; private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
@Autowired
protected ITagDefinitionDao myTagDefinitionDao;
@Autowired
protected IResourceTagDao myResourceTagDao;
@Autowired @Autowired
protected ISearchDao mySearchEntityDao; protected ISearchDao mySearchEntityDao;
@Autowired @Autowired

View File

@ -8,6 +8,7 @@ import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.matchesPattern; import static org.hamcrest.Matchers.matchesPattern;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -154,6 +155,56 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
} }
} }
@Test
public void testCreateDuplicateTagsDoesNotCauseDuplicates() {
Patient p = new Patient();
p.setActive(true);
p.getMeta().addTag().setSystem("FOO").setCode("BAR");
p.getMeta().addTag().setSystem("FOO").setCode("BAR");
p.getMeta().addTag().setSystem("FOO").setCode("BAR");
p.getMeta().addTag().setSystem("FOO").setCode("BAR");
p.getMeta().addTag().setSystem("FOO").setCode("BAR");
p.getMeta().addTag().setSystem("FOO").setCode("BAR");
p.getMeta().addTag().setSystem("FOO").setCode("BAR");
myPatientDao.create(p);
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
assertThat(myResourceTagDao.findAll(), hasSize(1));
assertThat(myTagDefinitionDao.findAll(), hasSize(1));
}
});
}
@Test
public void testCreateEmptyTagsIsIgnored() {
Patient p = new Patient();
p.setActive(true);
// Add an empty tag
p.getMeta().addTag();
// Add another empty tag
p.getMeta().addTag().setSystem("");
p.getMeta().addTag().setCode("");
p.getMeta().addTag().setDisplay("");
myPatientDao.create(p);
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
assertThat(myResourceTagDao.findAll(), empty());
assertThat(myTagDefinitionDao.findAll(), empty());
}
});
}
/** /**
* This gets called from assertGone too! Careful about exceptions... * This gets called from assertGone too! Careful about exceptions...
*/ */

View File

@ -22,243 +22,245 @@ import ca.uhn.fhir.context.FhirContext;
public class DefaultProfileValidationSupport implements IValidationSupport { public class DefaultProfileValidationSupport implements IValidationSupport {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultProfileValidationSupport.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(DefaultProfileValidationSupport.class);
private Map<String, CodeSystem> myCodeSystems; private Map<String, CodeSystem> myCodeSystems;
private Map<String, StructureDefinition> myStructureDefinitions; private Map<String, StructureDefinition> myStructureDefinitions;
private Map<String, ValueSet> myValueSets; private Map<String, ValueSet> myValueSets;
@Override @Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
ValueSetExpansionComponent retVal = new ValueSetExpansionComponent(); ValueSetExpansionComponent retVal = new ValueSetExpansionComponent();
Set<String> wantCodes = new HashSet<String>(); Set<String> wantCodes = new HashSet<String>();
for (ConceptReferenceComponent next : theInclude.getConcept()) { for (ConceptReferenceComponent next : theInclude.getConcept()) {
wantCodes.add(next.getCode()); wantCodes.add(next.getCode());
} }
CodeSystem system = fetchCodeSystem(theContext, theInclude.getSystem()); CodeSystem system = fetchCodeSystem(theContext, theInclude.getSystem());
for (ConceptDefinitionComponent next : system.getConcept()) { for (ConceptDefinitionComponent next : system.getConcept()) {
if (wantCodes.isEmpty() || wantCodes.contains(next.getCode())) { if (wantCodes.isEmpty() || wantCodes.contains(next.getCode())) {
retVal.addContains().setSystem(theInclude.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay()); retVal.addContains().setSystem(theInclude.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay());
} }
} }
return retVal; return retVal;
} }
@Override @Override
public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) { public List<StructureDefinition> fetchAllStructureDefinitions(FhirContext theContext) {
return new ArrayList<StructureDefinition>(provideStructureDefinitionMap(theContext).values()); return new ArrayList<StructureDefinition>(provideStructureDefinitionMap(theContext).values());
} }
@Override @Override
public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) { public CodeSystem fetchCodeSystem(FhirContext theContext, String theSystem) {
return (CodeSystem) fetchCodeSystemOrValueSet(theContext, theSystem, true); return (CodeSystem) fetchCodeSystemOrValueSet(theContext, theSystem, true);
} }
private DomainResource fetchCodeSystemOrValueSet(FhirContext theContext, String theSystem, boolean codeSystem) { private DomainResource fetchCodeSystemOrValueSet(FhirContext theContext, String theSystem, boolean codeSystem) {
Map<String, CodeSystem> codeSystems = myCodeSystems; synchronized (this) {
Map<String, ValueSet> valueSets = myValueSets; Map<String, CodeSystem> codeSystems = myCodeSystems;
if (codeSystems == null) { Map<String, ValueSet> valueSets = myValueSets;
codeSystems = new HashMap<String, CodeSystem>(); if (codeSystems == null || valueSets == null) {
valueSets = new HashMap<String, ValueSet>(); codeSystems = new HashMap<String, CodeSystem>();
valueSets = new HashMap<String, ValueSet>();
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/instance/model/dstu3/valueset/valuesets.xml"); loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/instance/model/dstu3/valueset/valuesets.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/instance/model/dstu3/valueset/v2-tables.xml"); loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/instance/model/dstu3/valueset/v2-tables.xml");
loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/instance/model/dstu3/valueset/v3-codesystems.xml"); loadCodeSystems(theContext, codeSystems, valueSets, "/org/hl7/fhir/instance/model/dstu3/valueset/v3-codesystems.xml");
myCodeSystems = codeSystems; myCodeSystems = codeSystems;
myValueSets = valueSets; myValueSets = valueSets;
} }
if (codeSystem) { if (codeSystem) {
return codeSystems.get(theSystem); return codeSystems.get(theSystem);
} else { } else {
return valueSets.get(theSystem); return valueSets.get(theSystem);
} }
} }
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) { public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
Validate.notBlank(theUri, "theUri must not be null or blank"); Validate.notBlank(theUri, "theUri must not be null or blank");
if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
return (T) fetchStructureDefinition(theContext, theUri);
}
if (theUri.startsWith("http://hl7.org/fhir/ValueSet/")) {
return (T) fetchValueSet(theContext, theUri);
}
// if (theUri.startsWith("http://hl7.org/fhir/ValueSet/")) {
// Map<String, ValueSet> defaultValueSets = myDefaultValueSets;
// if (defaultValueSets == null) {
// String path = theContext.getVersion().getPathToSchemaDefinitions().replace("/schema", "/valueset") + "/valuesets.xml";
// InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(path);
// if (valuesetText == null) {
// return null;
// }
// InputStreamReader reader;
// try {
// reader = new InputStreamReader(valuesetText, "UTF-8");
// } catch (UnsupportedEncodingException e) {
// // Shouldn't happen!
// throw new InternalErrorException("UTF-8 encoding not supported on this platform", e);
// }
//
// defaultValueSets = new HashMap<String, ValueSet>();
//
// Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
// for (BundleEntryComponent next : bundle.getEntry()) {
// IdType nextId = new IdType(next.getFullUrl());
// if (nextId.isEmpty() || !nextId.getValue().startsWith("http://hl7.org/fhir/ValueSet/")) {
// continue;
// }
// defaultValueSets.put(nextId.toVersionless().getValue(), (ValueSet) next.getResource());
// }
//
// myDefaultValueSets = defaultValueSets;
// }
//
// return (T) defaultValueSets.get(theUri);
// }
return null; if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
} return (T) fetchStructureDefinition(theContext, theUri);
}
if (theUri.startsWith("http://hl7.org/fhir/ValueSet/")) {
return (T) fetchValueSet(theContext, theUri);
}
// if (theUri.startsWith("http://hl7.org/fhir/ValueSet/")) {
// Map<String, ValueSet> defaultValueSets = myDefaultValueSets;
// if (defaultValueSets == null) {
// String path = theContext.getVersion().getPathToSchemaDefinitions().replace("/schema", "/valueset") + "/valuesets.xml";
// InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(path);
// if (valuesetText == null) {
// return null;
// }
// InputStreamReader reader;
// try {
// reader = new InputStreamReader(valuesetText, "UTF-8");
// } catch (UnsupportedEncodingException e) {
// // Shouldn't happen!
// throw new InternalErrorException("UTF-8 encoding not supported on this platform", e);
// }
//
// defaultValueSets = new HashMap<String, ValueSet>();
//
// Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
// for (BundleEntryComponent next : bundle.getEntry()) {
// IdType nextId = new IdType(next.getFullUrl());
// if (nextId.isEmpty() || !nextId.getValue().startsWith("http://hl7.org/fhir/ValueSet/")) {
// continue;
// }
// defaultValueSets.put(nextId.toVersionless().getValue(), (ValueSet) next.getResource());
// }
//
// myDefaultValueSets = defaultValueSets;
// }
//
// return (T) defaultValueSets.get(theUri);
// }
@Override return null;
public StructureDefinition fetchStructureDefinition(FhirContext theContext, String theUrl) { }
return provideStructureDefinitionMap(theContext).get(theUrl);
}
ValueSet fetchValueSet(FhirContext theContext, String theSystem) { @Override
return (ValueSet) fetchCodeSystemOrValueSet(theContext, theSystem, false); public StructureDefinition fetchStructureDefinition(FhirContext theContext, String theUrl) {
} return provideStructureDefinitionMap(theContext).get(theUrl);
}
public void flush() { ValueSet fetchValueSet(FhirContext theContext, String theSystem) {
myCodeSystems = null; return (ValueSet) fetchCodeSystemOrValueSet(theContext, theSystem, false);
myStructureDefinitions = null; }
}
@Override public void flush() {
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) { myCodeSystems = null;
CodeSystem cs = fetchCodeSystem(theContext, theSystem); myStructureDefinitions = null;
return cs != null && cs.getContent() != CodeSystemContentMode.NOTPRESENT; }
}
private void loadCodeSystems(FhirContext theContext, Map<String, CodeSystem> theCodeSystems, Map<String, ValueSet> theValueSets, String theClasspath) { @Override
ourLog.info("Loading CodeSystem/ValueSet from classpath: {}", theClasspath); public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath); CodeSystem cs = fetchCodeSystem(theContext, theSystem);
if (valuesetText != null) { return cs != null && cs.getContent() != CodeSystemContentMode.NOTPRESENT;
InputStreamReader reader = new InputStreamReader(valuesetText, Charsets.UTF_8); }
Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader); private void loadCodeSystems(FhirContext theContext, Map<String, CodeSystem> theCodeSystems, Map<String, ValueSet> theValueSets, String theClasspath) {
for (BundleEntryComponent next : bundle.getEntry()) { ourLog.info("Loading CodeSystem/ValueSet from classpath: {}", theClasspath);
if (next.getResource() instanceof CodeSystem) { InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath);
CodeSystem nextValueSet = (CodeSystem) next.getResource(); if (valuesetText != null) {
nextValueSet.getText().setDivAsString(""); InputStreamReader reader = new InputStreamReader(valuesetText, Charsets.UTF_8);
String system = nextValueSet.getUrl();
if (isNotBlank(system)) {
theCodeSystems.put(system, nextValueSet);
}
} else if (next.getResource() instanceof ValueSet) {
ValueSet nextValueSet = (ValueSet) next.getResource();
nextValueSet.getText().setDivAsString("");
String system = nextValueSet.getUrl();
if (isNotBlank(system)) {
theValueSets.put(system, nextValueSet);
}
}
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
private void loadStructureDefinitions(FhirContext theContext, Map<String, StructureDefinition> theCodeSystems, String theClasspath) { Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
ourLog.info("Loading structure definitions from classpath: {}", theClasspath); for (BundleEntryComponent next : bundle.getEntry()) {
InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath); if (next.getResource() instanceof CodeSystem) {
if (valuesetText != null) { CodeSystem nextValueSet = (CodeSystem) next.getResource();
InputStreamReader reader = new InputStreamReader(valuesetText, Charsets.UTF_8); nextValueSet.getText().setDivAsString("");
String system = nextValueSet.getUrl();
if (isNotBlank(system)) {
theCodeSystems.put(system, nextValueSet);
}
} else if (next.getResource() instanceof ValueSet) {
ValueSet nextValueSet = (ValueSet) next.getResource();
nextValueSet.getText().setDivAsString("");
String system = nextValueSet.getUrl();
if (isNotBlank(system)) {
theValueSets.put(system, nextValueSet);
}
}
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader); private void loadStructureDefinitions(FhirContext theContext, Map<String, StructureDefinition> theCodeSystems, String theClasspath) {
for (BundleEntryComponent next : bundle.getEntry()) { ourLog.info("Loading structure definitions from classpath: {}", theClasspath);
if (next.getResource() instanceof StructureDefinition) { InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(theClasspath);
StructureDefinition nextSd = (StructureDefinition) next.getResource(); if (valuesetText != null) {
nextSd.getText().setDivAsString(""); InputStreamReader reader = new InputStreamReader(valuesetText, Charsets.UTF_8);
String system = nextSd.getUrl();
if (isNotBlank(system)) {
theCodeSystems.put(system, nextSd);
}
}
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
private Map<String, StructureDefinition> provideStructureDefinitionMap(FhirContext theContext) { Bundle bundle = theContext.newXmlParser().parseResource(Bundle.class, reader);
Map<String, StructureDefinition> structureDefinitions = myStructureDefinitions; for (BundleEntryComponent next : bundle.getEntry()) {
if (structureDefinitions == null) { if (next.getResource() instanceof StructureDefinition) {
structureDefinitions = new HashMap<String, StructureDefinition>(); StructureDefinition nextSd = (StructureDefinition) next.getResource();
nextSd.getText().setDivAsString("");
String system = nextSd.getUrl();
if (isNotBlank(system)) {
theCodeSystems.put(system, nextSd);
}
}
}
} else {
ourLog.warn("Unable to load resource: {}", theClasspath);
}
}
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/instance/model/dstu3/profile/profiles-resources.xml"); private Map<String, StructureDefinition> provideStructureDefinitionMap(FhirContext theContext) {
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/instance/model/dstu3/profile/profiles-types.xml"); Map<String, StructureDefinition> structureDefinitions = myStructureDefinitions;
loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/instance/model/dstu3/profile/profiles-others.xml"); if (structureDefinitions == null) {
structureDefinitions = new HashMap<String, StructureDefinition>();
myStructureDefinitions = structureDefinitions; loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/instance/model/dstu3/profile/profiles-resources.xml");
} loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/instance/model/dstu3/profile/profiles-types.xml");
return structureDefinitions; loadStructureDefinitions(theContext, structureDefinitions, "/org/hl7/fhir/instance/model/dstu3/profile/profiles-others.xml");
}
@Override myStructureDefinitions = structureDefinitions;
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { }
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem); return structureDefinitions;
if (cs != null) { }
boolean caseSensitive = true;
if (cs.hasCaseSensitive()) {
caseSensitive = cs.getCaseSensitive();
}
CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive);
if (retVal != null) {
return retVal;
}
}
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode); @Override
} public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
CodeSystem cs = fetchCodeSystem(theContext, theCodeSystem);
if (cs != null) {
boolean caseSensitive = true;
if (cs.hasCaseSensitive()) {
caseSensitive = cs.getCaseSensitive();
}
private CodeValidationResult testIfConceptIsInList(String theCode, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive) { CodeValidationResult retVal = testIfConceptIsInList(theCode, cs.getConcept(), caseSensitive);
String code = theCode;
if (theCaseSensitive == false) {
code = code.toUpperCase();
}
return testIfConceptIsInListInner(conceptList, theCaseSensitive, code);
}
private CodeValidationResult testIfConceptIsInListInner(List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive, String code) { if (retVal != null) {
CodeValidationResult retVal = null; return retVal;
for (ConceptDefinitionComponent next : conceptList) { }
String nextCandidate = next.getCode(); }
if (theCaseSensitive == false) {
nextCandidate = nextCandidate.toUpperCase();
}
if (nextCandidate.equals(code)) {
retVal = new CodeValidationResult(next);
break;
}
// recurse return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
retVal = testIfConceptIsInList(code, next.getConcept(), theCaseSensitive); }
if (retVal != null) {
break; private CodeValidationResult testIfConceptIsInList(String theCode, List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive) {
} String code = theCode;
} if (theCaseSensitive == false) {
code = code.toUpperCase();
return retVal; }
}
return testIfConceptIsInListInner(conceptList, theCaseSensitive, code);
}
private CodeValidationResult testIfConceptIsInListInner(List<ConceptDefinitionComponent> conceptList, boolean theCaseSensitive, String code) {
CodeValidationResult retVal = null;
for (ConceptDefinitionComponent next : conceptList) {
String nextCandidate = next.getCode();
if (theCaseSensitive == false) {
nextCandidate = nextCandidate.toUpperCase();
}
if (nextCandidate.equals(code)) {
retVal = new CodeValidationResult(next);
break;
}
// recurse
retVal = testIfConceptIsInList(code, next.getConcept(), theCaseSensitive);
if (retVal != null) {
break;
}
}
return retVal;
}
} }

View File

@ -29,19 +29,18 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) { public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
return false; return false;
} }
/** /**
* Constructor * Constructor
*/ */
public DefaultProfileValidationSupport() { public DefaultProfileValidationSupport() {
super(); super();
} }
public void flush() { public void flush() {
myDefaultValueSets = null; myDefaultValueSets = null;
myCodeSystems = null; myCodeSystems = null;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
@ -93,18 +92,20 @@ public class DefaultProfileValidationSupport implements IValidationSupport {
@Override @Override
public ValueSet fetchCodeSystem(FhirContext theContext, String theSystem) { public ValueSet fetchCodeSystem(FhirContext theContext, String theSystem) {
Map<String, ValueSet> codeSystems = myCodeSystems; synchronized (this) {
if (codeSystems == null) { Map<String, ValueSet> valueSets = myCodeSystems;
codeSystems = new HashMap<String, ValueSet>(); if (valueSets == null) {
valueSets = new HashMap<String, ValueSet>();
loadCodeSystems(theContext, codeSystems, "/org/hl7/fhir/instance/model/valueset/valuesets.xml"); loadCodeSystems(theContext, valueSets, "/org/hl7/fhir/instance/model/valueset/valuesets.xml");
loadCodeSystems(theContext, codeSystems, "/org/hl7/fhir/instance/model/valueset/v2-tables.xml"); loadCodeSystems(theContext, valueSets, "/org/hl7/fhir/instance/model/valueset/v2-tables.xml");
loadCodeSystems(theContext, codeSystems, "/org/hl7/fhir/instance/model/valueset/v3-codesystems.xml"); loadCodeSystems(theContext, valueSets, "/org/hl7/fhir/instance/model/valueset/v3-codesystems.xml");
myCodeSystems = codeSystems; myCodeSystems = valueSets;
}
return valueSets.get(theSystem);
} }
return codeSystems.get(theSystem);
} }
private void loadCodeSystems(FhirContext theContext, Map<String, ValueSet> codeSystems, String file) { private void loadCodeSystems(FhirContext theContext, Map<String, ValueSet> codeSystems, String file) {

View File

@ -154,6 +154,19 @@
been received by the client when the error happens. Thanks to GitHub been received by the client when the error happens. Thanks to GitHub
user maclema for the pull request! user maclema for the pull request!
</action> </action>
<action type="add">
Add a check in JPA server that prevents completely blank tags, profiles, and security labels
from being saved to the database. These were filtered out anyhow when the
result was returned back to the client but they were persisted which
just wasted space.
</action>
<action type="fix" issue="664">
Loading the build-in profile structures (StructureDefinition, ValueSet, etc) is now done in
a synchronized block in order to prevent multiple loads happening if the server processes
multiple validations in parallel threads right after startup. Previously a heavy load could
cause the server to run out of memory and lock up. Thanks to Karl M Davis
for analysis and help fixing this!
</action>
</release> </release>
<release version="2.4" date="2017-04-19"> <release version="2.4" date="2017-04-19">
<action type="add"> <action type="add">