Split out delivery and processing channel for subscriptions

This commit is contained in:
James 2017-08-19 12:14:48 -04:00
parent 7764484f44
commit e3a28e2ff5
16 changed files with 32481 additions and 35599 deletions

View File

@ -59,6 +59,7 @@ public abstract class BaseSubscriptionInterceptor extends ServerOperationInterce
static final String SUBSCRIPTION_HEADER = "Subscription.channel.header"; static final String SUBSCRIPTION_HEADER = "Subscription.channel.header";
private static final Integer MAX_SUBSCRIPTION_RESULTS = 1000; private static final Integer MAX_SUBSCRIPTION_RESULTS = 1000;
private SubscribableChannel myProcessingChannel; private SubscribableChannel myProcessingChannel;
private SubscribableChannel myDeliveryChannel;
private ExecutorService myExecutor; private ExecutorService myExecutor;
private boolean myAutoActivateSubscriptions = true; private boolean myAutoActivateSubscriptions = true;
private int myExecutorThreadCount = 1; private int myExecutorThreadCount = 1;
@ -70,6 +71,14 @@ public abstract class BaseSubscriptionInterceptor extends ServerOperationInterce
public abstract Subscription.SubscriptionChannelType getChannelType(); public abstract Subscription.SubscriptionChannelType getChannelType();
public SubscribableChannel getDeliveryChannel() {
return myDeliveryChannel;
}
public void setDeliveryChannel(SubscribableChannel theDeliveryChannel) {
myDeliveryChannel = theDeliveryChannel;
}
public BlockingQueue<Runnable> getExecutorQueueForUnitTests() { public BlockingQueue<Runnable> getExecutorQueueForUnitTests() {
return myExecutorQueue; return myExecutorQueue;
} }
@ -144,19 +153,22 @@ public abstract class BaseSubscriptionInterceptor extends ServerOperationInterce
rejectedExecutionHandler); rejectedExecutionHandler);
if (myProcessingChannel == null) { if (getProcessingChannel() == null) {
myProcessingChannel = new ExecutorSubscribableChannel(myExecutor); setProcessingChannel(new ExecutorSubscribableChannel(myExecutor));
}
if (getDeliveryChannel() == null) {
setDeliveryChannel(new ExecutorSubscribableChannel(myExecutor));
} }
if (myAutoActivateSubscriptions) { if (myAutoActivateSubscriptions) {
if (mySubscriptionActivatingSubscriber == null) { if (mySubscriptionActivatingSubscriber == null) {
mySubscriptionActivatingSubscriber = new SubscriptionActivatingSubscriber(getSubscriptionDao(), myIdToSubscription, getChannelType(), myProcessingChannel); mySubscriptionActivatingSubscriber = new SubscriptionActivatingSubscriber(getSubscriptionDao(), myIdToSubscription, getChannelType(), this);
} }
getProcessingChannel().subscribe(mySubscriptionActivatingSubscriber); getProcessingChannel().subscribe(mySubscriptionActivatingSubscriber);
} }
if (mySubscriptionCheckingSubscriber == null) { if (mySubscriptionCheckingSubscriber == null) {
mySubscriptionCheckingSubscriber = new SubscriptionCheckingSubscriber(getSubscriptionDao(), myIdToSubscription, getChannelType(), myProcessingChannel); mySubscriptionCheckingSubscriber = new SubscriptionCheckingSubscriber(getSubscriptionDao(), myIdToSubscription, getChannelType(), this);
} }
getProcessingChannel().subscribe(mySubscriptionCheckingSubscriber); getProcessingChannel().subscribe(mySubscriptionCheckingSubscriber);

View File

@ -26,14 +26,14 @@ public abstract class BaseSubscriptionRestHookInterceptor extends BaseSubscripti
@Override @Override
protected void registerDeliverySubscriber() { protected void registerDeliverySubscriber() {
if (mySubscriptionDeliverySubscriber == null) { if (mySubscriptionDeliverySubscriber == null) {
mySubscriptionDeliverySubscriber = new SubscriptionDeliveringRestHookSubscriber(getSubscriptionDao(), getIdToSubscription(), getChannelType(), getProcessingChannel()); mySubscriptionDeliverySubscriber = new SubscriptionDeliveringRestHookSubscriber(getSubscriptionDao(), getIdToSubscription(), getChannelType(), this);
} }
getProcessingChannel().subscribe(mySubscriptionDeliverySubscriber); getDeliveryChannel().subscribe(mySubscriptionDeliverySubscriber);
} }
@Override @Override
protected void unregisterDeliverySubscriber() { protected void unregisterDeliverySubscriber() {
getProcessingChannel().unsubscribe(mySubscriptionDeliverySubscriber); getDeliveryChannel().unsubscribe(mySubscriptionDeliverySubscriber);
} }
} }

View File

@ -26,7 +26,6 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.Subscription; import org.hl7.fhir.r4.model.Subscription;
import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.SubscribableChannel;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -35,16 +34,16 @@ public abstract class BaseSubscriptionSubscriber implements MessageHandler {
private final IFhirResourceDao mySubscriptionDao; private final IFhirResourceDao mySubscriptionDao;
private final ConcurrentHashMap<String, IBaseResource> myIdToSubscription; private final ConcurrentHashMap<String, IBaseResource> myIdToSubscription;
private final Subscription.SubscriptionChannelType myChannelType; private final Subscription.SubscriptionChannelType myChannelType;
private final SubscribableChannel myProcessingChannel; private final BaseSubscriptionInterceptor mySubscriptionInterceptor;
/** /**
* Constructor * Constructor
*/ */
public BaseSubscriptionSubscriber(IFhirResourceDao<? extends IBaseResource> theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType, SubscribableChannel theProcessingChannel) { public BaseSubscriptionSubscriber(IFhirResourceDao<? extends IBaseResource> theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType, BaseSubscriptionInterceptor theSubscriptionInterceptor) {
mySubscriptionDao = theSubscriptionDao; mySubscriptionDao = theSubscriptionDao;
myIdToSubscription = theIdToSubscription; myIdToSubscription = theIdToSubscription;
myChannelType = theChannelType; myChannelType = theChannelType;
myProcessingChannel = theProcessingChannel; mySubscriptionInterceptor = theSubscriptionInterceptor;
} }
public Subscription.SubscriptionChannelType getChannelType() { public Subscription.SubscriptionChannelType getChannelType() {
@ -59,14 +58,14 @@ public abstract class BaseSubscriptionSubscriber implements MessageHandler {
return myIdToSubscription; return myIdToSubscription;
} }
public SubscribableChannel getProcessingChannel() {
return myProcessingChannel;
}
public IFhirResourceDao getSubscriptionDao() { public IFhirResourceDao getSubscriptionDao() {
return mySubscriptionDao; return mySubscriptionDao;
} }
public BaseSubscriptionInterceptor getSubscriptionInterceptor() {
return mySubscriptionInterceptor;
}
/** /**
* Does this subscription type (e.g. rest hook, websocket, etc) apply to this interceptor? * Does this subscription type (e.g. rest hook, websocket, etc) apply to this interceptor?
*/ */

View File

@ -44,14 +44,14 @@ public abstract class BaseSubscriptionWebsocketInterceptor extends BaseSubscript
@Override @Override
protected void registerDeliverySubscriber() { protected void registerDeliverySubscriber() {
if (mySubscriptionDeliverySubscriber == null) { if (mySubscriptionDeliverySubscriber == null) {
mySubscriptionDeliverySubscriber = new SubscriptionDeliveringWebsocketSubscriber(getSubscriptionDao(), getIdToSubscription(), getChannelType(), getProcessingChannel(), myTxManager, mySubscriptionFlaggedResourceDataDao, mySubscriptionTableDao, myResourceTableDao); mySubscriptionDeliverySubscriber = new SubscriptionDeliveringWebsocketSubscriber(getSubscriptionDao(), getIdToSubscription(), getChannelType(), this, myTxManager, mySubscriptionFlaggedResourceDataDao, mySubscriptionTableDao, myResourceTableDao);
} }
getProcessingChannel().subscribe(mySubscriptionDeliverySubscriber); getDeliveryChannel().subscribe(mySubscriptionDeliverySubscriber);
} }
@Override @Override
protected void unregisterDeliverySubscriber() { protected void unregisterDeliverySubscriber() {
getProcessingChannel().unsubscribe(mySubscriptionDeliverySubscriber); getDeliveryChannel().unsubscribe(mySubscriptionDeliverySubscriber);
} }
} }

View File

@ -41,8 +41,8 @@ public class SubscriptionActivatingSubscriber extends BaseSubscriptionSubscriber
/** /**
* Constructor * Constructor
*/ */
public SubscriptionActivatingSubscriber(IFhirResourceDao<? extends IBaseResource> theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType, SubscribableChannel theProcessingChannel) { public SubscriptionActivatingSubscriber(IFhirResourceDao<? extends IBaseResource> theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType, BaseSubscriptionInterceptor theSubscriptionInterceptor) {
super(theSubscriptionDao, theIdToSubscription, theChannelType, theProcessingChannel); super(theSubscriptionDao, theIdToSubscription, theChannelType, theSubscriptionInterceptor);
} }
private void activateAndRegisterSubscriptionIfRequired(ResourceModifiedMessage theMsg) { private void activateAndRegisterSubscriptionIfRequired(ResourceModifiedMessage theMsg) {

View File

@ -45,8 +45,8 @@ import java.util.concurrent.ConcurrentHashMap;
public class SubscriptionCheckingSubscriber extends BaseSubscriptionSubscriber { public class SubscriptionCheckingSubscriber extends BaseSubscriptionSubscriber {
private Logger ourLog = LoggerFactory.getLogger(SubscriptionCheckingSubscriber.class); private Logger ourLog = LoggerFactory.getLogger(SubscriptionCheckingSubscriber.class);
public SubscriptionCheckingSubscriber(IFhirResourceDao theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType, SubscribableChannel theProcessingChannel) { public SubscriptionCheckingSubscriber(IFhirResourceDao theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType,BaseSubscriptionInterceptor theSubscriptionInterceptor) {
super(theSubscriptionDao, theIdToSubscription, theChannelType, theProcessingChannel); super(theSubscriptionDao, theIdToSubscription, theChannelType, theSubscriptionInterceptor);
} }
@Override @Override
@ -112,7 +112,7 @@ public class SubscriptionCheckingSubscriber extends BaseSubscriptionSubscriber {
deliveryMsg.setOperationType(msg.getOperationType()); deliveryMsg.setOperationType(msg.getOperationType());
deliveryMsg.setPayloadId(msg.getId()); deliveryMsg.setPayloadId(msg.getId());
getProcessingChannel().send(new GenericMessage<>(deliveryMsg)); getSubscriptionInterceptor().getDeliveryChannel().send(new GenericMessage<>(deliveryMsg));
} }
} }

View File

@ -44,8 +44,8 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionSubscriber { public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionSubscriber {
private Logger ourLog = LoggerFactory.getLogger(SubscriptionDeliveringRestHookSubscriber.class); private Logger ourLog = LoggerFactory.getLogger(SubscriptionDeliveringRestHookSubscriber.class);
public SubscriptionDeliveringRestHookSubscriber(IFhirResourceDao theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType, SubscribableChannel theProcessingChannel) { public SubscriptionDeliveringRestHookSubscriber(IFhirResourceDao theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType, BaseSubscriptionInterceptor theSubscriptionInterceptor) {
super(theSubscriptionDao, theIdToSubscription, theChannelType, theProcessingChannel); super(theSubscriptionDao, theIdToSubscription, theChannelType, theSubscriptionInterceptor);
} }
@Override @Override

View File

@ -58,8 +58,8 @@ public class SubscriptionDeliveringWebsocketSubscriber extends BaseSubscriptionS
private final IResourceTableDao myResourceTableDao; private final IResourceTableDao myResourceTableDao;
private Logger ourLog = LoggerFactory.getLogger(SubscriptionDeliveringWebsocketSubscriber.class); private Logger ourLog = LoggerFactory.getLogger(SubscriptionDeliveringWebsocketSubscriber.class);
public SubscriptionDeliveringWebsocketSubscriber(IFhirResourceDao theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType, SubscribableChannel theProcessingChannel, PlatformTransactionManager theTxManager, ISubscriptionFlaggedResourceDataDao theSubscriptionFlaggedResourceDataDao, ISubscriptionTableDao theSubscriptionTableDao, IResourceTableDao theResourceTableDao) { public SubscriptionDeliveringWebsocketSubscriber(IFhirResourceDao theSubscriptionDao, ConcurrentHashMap<String, IBaseResource> theIdToSubscription, Subscription.SubscriptionChannelType theChannelType, BaseSubscriptionInterceptor theSubscriptionInterceptor, PlatformTransactionManager theTxManager, ISubscriptionFlaggedResourceDataDao theSubscriptionFlaggedResourceDataDao, ISubscriptionTableDao theSubscriptionTableDao, IResourceTableDao theResourceTableDao) {
super(theSubscriptionDao, theIdToSubscription, theChannelType, theProcessingChannel); super(theSubscriptionDao, theIdToSubscription, theChannelType, theSubscriptionInterceptor);
myTxManager = theTxManager; myTxManager = theTxManager;
mySubscriptionFlaggedResourceDao = theSubscriptionFlaggedResourceDataDao; mySubscriptionFlaggedResourceDao = theSubscriptionFlaggedResourceDataDao;

View File

@ -1,7 +1,7 @@
<Bundle xmlns="http://hl7.org/fhir"> <Bundle xmlns="http://hl7.org/fhir">
<id value="v2-valuesets"/> <id value="v2-valuesets"/>
<meta> <meta>
<lastUpdated value="2015-11-11T10:54:01.813-05:00"/> <lastUpdated value="2015-10-24T07:41:03.495+11:00"/>
</meta> </meta>
<type value="collection"/> <type value="collection"/>
<entry> <entry>
@ -34461,7 +34461,7 @@
<display value="MMR"/> <display value="MMR"/>
<designation> <designation>
<language value="nl"/> <language value="nl"/>
<value value="BMR - bof, mazelen, rodehond"/> <value value="BMR - mazelen, bof, rodehond"/>
</designation> </designation>
</concept> </concept>
<concept> <concept>
@ -34901,7 +34901,7 @@
<display value="DTaP"/> <display value="DTaP"/>
<designation> <designation>
<language value="nl"/> <language value="nl"/>
<value value="DaKT -difterie-acellulaire kinkhoest-tetanus"/> <value value="DaKT"/>
</designation> </designation>
</concept> </concept>
<concept> <concept>
@ -35396,7 +35396,7 @@
<display value="leprosy"/> <display value="leprosy"/>
<designation> <designation>
<language value="nl"/> <language value="nl"/>
<value value="lepra"/> <value value="leprosy"/>
</designation> </designation>
</concept> </concept>
<concept> <concept>
@ -35715,7 +35715,7 @@
<display value="MMRV"/> <display value="MMRV"/>
<designation> <designation>
<language value="nl"/> <language value="nl"/>
<value value="BMRV - bof, mazelen, rodehond en varicella"/> <value value="BMRV - mazelen, bof, rodehond en varicella"/>
</designation> </designation>
</concept> </concept>
<concept> <concept>

View File

@ -1,15 +1,10 @@
package org.hl7.fhir.instance.hapi.validation; package org.hl7.fhir.instance.hapi.validation;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import org.hl7.fhir.instance.model.Bundle; import org.hl7.fhir.instance.model.Bundle;
import org.hl7.fhir.instance.model.Bundle.BundleEntryComponent; import org.hl7.fhir.instance.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.instance.model.CodeType;
import org.hl7.fhir.instance.model.IdType; import org.hl7.fhir.instance.model.IdType;
import org.hl7.fhir.instance.model.ValueSet; import org.hl7.fhir.instance.model.ValueSet;
import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.instance.model.ValueSet.ConceptSetComponent;
@ -17,123 +12,138 @@ import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import ca.uhn.fhir.context.FhirContext; import java.io.InputStream;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class DefaultProfileValidationSupport implements IValidationSupport { public class DefaultProfileValidationSupport implements IValidationSupport {
private Map<String, ValueSet> myDefaultValueSets; private Map<String, ValueSet> myDefaultValueSets;
private Map<String, ValueSet> myCodeSystems; private Map<String, ValueSet> myCodeSystems;
@Override /**
public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) { * Constructor
return false; */
} public DefaultProfileValidationSupport() {
super();
}
/** @Override
* Constructor public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {
*/ return null;
public DefaultProfileValidationSupport() { }
super();
}
public void flush() { @Override
myDefaultValueSets = null; public ValueSet fetchCodeSystem(FhirContext theContext, String theSystem) {
myCodeSystems = null; synchronized (this) {
} Map<String, ValueSet> valueSets = myCodeSystems;
if (valueSets == null) {
valueSets = new HashMap<String, ValueSet>();
@SuppressWarnings("unchecked") loadCodeSystems(theContext, valueSets, "/org/hl7/fhir/instance/model/valueset/valuesets.xml");
@Override loadCodeSystems(theContext, valueSets, "/org/hl7/fhir/instance/model/valueset/v2-tables.xml");
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) { loadCodeSystems(theContext, valueSets, "/org/hl7/fhir/instance/model/valueset/v3-codesystems.xml");
if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
return (T) FhirInstanceValidator.loadProfileOrReturnNull(null, theContext, theUri.substring("http://hl7.org/fhir/StructureDefinition/".length()));
}
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>(); myCodeSystems = valueSets;
}
FhirContext ctx = FhirInstanceValidator.getHl7OrgDstu2Ctx(theContext); return valueSets.get(theSystem);
Bundle bundle = ctx.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; @SuppressWarnings("unchecked")
} @Override
public <T extends IBaseResource> T fetchResource(FhirContext theContext, Class<T> theClass, String theUri) {
if (theUri.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
return (T) FhirInstanceValidator.loadProfileOrReturnNull(null, theContext, theUri.substring("http://hl7.org/fhir/StructureDefinition/".length()));
}
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);
}
return (T) defaultValueSets.get(theUri); defaultValueSets = new HashMap<String, ValueSet>();
}
return null; FhirContext ctx = FhirInstanceValidator.getHl7OrgDstu2Ctx(theContext);
} Bundle bundle = ctx.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());
}
@Override myDefaultValueSets = defaultValueSets;
public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) { }
return new CodeValidationResult(IssueSeverity.INFORMATION, "Unknown code: " + theCodeSystem + " / " + theCode);
}
@Override return (T) defaultValueSets.get(theUri);
public ValueSet fetchCodeSystem(FhirContext theContext, String theSystem) { }
synchronized (this) {
Map<String, ValueSet> valueSets = myCodeSystems;
if (valueSets == null) {
valueSets = new HashMap<String, ValueSet>();
loadCodeSystems(theContext, valueSets, "/org/hl7/fhir/instance/model/valueset/valuesets.xml"); return null;
loadCodeSystems(theContext, valueSets, "/org/hl7/fhir/instance/model/valueset/v2-tables.xml"); }
loadCodeSystems(theContext, valueSets, "/org/hl7/fhir/instance/model/valueset/v3-codesystems.xml");
myCodeSystems = valueSets; public void flush() {
} myDefaultValueSets = null;
myCodeSystems = null;
}
return valueSets.get(theSystem); @Override
} public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) {
} return false;
}
private void loadCodeSystems(FhirContext theContext, Map<String, ValueSet> codeSystems, String file) { private void loadCodeSystems(FhirContext theContext, Map<String, ValueSet> codeSystems, String file) {
InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(file); InputStream valuesetText = DefaultProfileValidationSupport.class.getResourceAsStream(file);
if (valuesetText != null) { if (valuesetText != null) {
InputStreamReader reader; InputStreamReader reader;
try { try {
reader = new InputStreamReader(valuesetText, "UTF-8"); reader = new InputStreamReader(valuesetText, "UTF-8");
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
// Shouldn't happen! // Shouldn't happen!
throw new InternalErrorException("UTF-8 encoding not supported on this platform", e); throw new InternalErrorException("UTF-8 encoding not supported on this platform", e);
} }
FhirContext ctx = FhirInstanceValidator.getHl7OrgDstu2Ctx(theContext); FhirContext ctx = FhirInstanceValidator.getHl7OrgDstu2Ctx(theContext);
Bundle bundle = ctx.newXmlParser().parseResource(Bundle.class, reader); Bundle bundle = ctx.newXmlParser().parseResource(Bundle.class, reader);
for (BundleEntryComponent next : bundle.getEntry()) { for (BundleEntryComponent next : bundle.getEntry()) {
ValueSet nextValueSet = (ValueSet) next.getResource(); ValueSet nextValueSet = (ValueSet) next.getResource();
String system = nextValueSet.getCodeSystem().getSystem(); String system = nextValueSet.getCodeSystem().getSystem();
if (isNotBlank(system)) { if (isNotBlank(system)) {
codeSystems.put(system, nextValueSet); codeSystems.put(system, nextValueSet);
} }
} }
} }
} }
@Override @Override
public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { public CodeValidationResult validateCode(FhirContext theContext, String theCodeSystem, String theCode, String theDisplay) {
return null; ValueSet vs = fetchCodeSystem(theContext, theCodeSystem);
} if (vs != null) {
for (ValueSet.ConceptDefinitionComponent nextConcept : vs.getCodeSystem().getConcept()) {
if (nextConcept.getCode().equals(theCode)){
ValueSet.ConceptDefinitionComponent component = new ValueSet.ConceptDefinitionComponent(new CodeType(theCode));
return new CodeValidationResult(component);
}
}
}
return new CodeValidationResult(IssueSeverity.WARNING, "Unknown code: " + theCodeSystem + " / " + theCode);
}
} }

View File

@ -1,8 +1,6 @@
package org.hl7.fhir.instance.hapi.validation; package org.hl7.fhir.instance.hapi.validation;
import java.util.List; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.DataFormatException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.formats.IParser; import org.hl7.fhir.instance.formats.IParser;
import org.hl7.fhir.instance.formats.ParserType; import org.hl7.fhir.instance.formats.ParserType;
@ -15,10 +13,14 @@ import org.hl7.fhir.instance.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.instance.terminologies.ValueSetExpander; import org.hl7.fhir.instance.terminologies.ValueSetExpander;
import org.hl7.fhir.instance.terminologies.ValueSetExpanderFactory; import org.hl7.fhir.instance.terminologies.ValueSetExpanderFactory;
import org.hl7.fhir.instance.terminologies.ValueSetExpanderSimple; import org.hl7.fhir.instance.terminologies.ValueSetExpanderSimple;
import org.hl7.fhir.instance.utils.*; import org.hl7.fhir.instance.utils.INarrativeGenerator;
import org.hl7.fhir.instance.utils.IResourceValidator;
import org.hl7.fhir.instance.utils.IWorkerContext;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import ca.uhn.fhir.context.FhirContext; import java.util.List;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public final class HapiWorkerContext implements IWorkerContext, ValueSetExpanderFactory, ValueSetExpander { public final class HapiWorkerContext implements IWorkerContext, ValueSetExpanderFactory, ValueSetExpander {
private final FhirContext myCtx; private final FhirContext myCtx;
@ -174,21 +176,6 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
@Override @Override
public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) { public ValidationResult validateCode(String theSystem, String theCode, String theDisplay, ValueSet theVs) {
/*
* For some reason the built-in valueset is empty
*/
if (theVs.getIdElement().getValue().equals("http://hl7.org/fhir/ValueSet/defined-types")) {
// try {
// myCtx.getResourceDefinition(theCode);
// return new ValidationResult(new ConceptDefinitionComponent(new CodeType(theCode)));
// } catch (DataFormatException e){
// if (myCtx.getElementDefinition(theCode) != null) {
// return new ValidationResult(new ConceptDefinitionComponent(new CodeType(theCode)));
// }
// }
return new ValidationResult(new ConceptDefinitionComponent(new CodeType(theCode)));
}
if (theSystem == null || StringUtils.equals(theSystem, theVs.getCodeSystem().getSystem())) { if (theSystem == null || StringUtils.equals(theSystem, theVs.getCodeSystem().getSystem())) {
for (ConceptDefinitionComponent next : theVs.getCodeSystem().getConcept()) { for (ConceptDefinitionComponent next : theVs.getCodeSystem().getConcept()) {
ValidationResult retVal = validateCodeSystem(theCode, next); ValidationResult retVal = validateCodeSystem(theCode, next);
@ -199,7 +186,13 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
} }
for (ConceptSetComponent nextComposeConceptSet : theVs.getCompose().getInclude()) { for (ConceptSetComponent nextComposeConceptSet : theVs.getCompose().getInclude()) {
if (StringUtils.equals(theSystem, nextComposeConceptSet.getSystem())) {
String nextSystem = theSystem;
if (nextSystem == null && isNotBlank(nextComposeConceptSet.getSystem())) {
nextSystem = nextComposeConceptSet.getSystem();
}
if (StringUtils.equals(nextSystem, nextComposeConceptSet.getSystem())) {
for (ConceptReferenceComponent nextComposeCode : nextComposeConceptSet.getConcept()) { for (ConceptReferenceComponent nextComposeCode : nextComposeConceptSet.getConcept()) {
ConceptDefinitionComponent conceptDef = new ConceptDefinitionComponent(); ConceptDefinitionComponent conceptDef = new ConceptDefinitionComponent();
conceptDef.setCode(nextComposeCode.getCode()); conceptDef.setCode(nextComposeCode.getCode());
@ -209,8 +202,16 @@ public final class HapiWorkerContext implements IWorkerContext, ValueSetExpander
return retVal; return retVal;
} }
} }
if (nextComposeConceptSet.getConcept().isEmpty()){
ValidationResult result = validateCode(nextSystem, theCode, null);
if (result.isOk()){
return result;
}
}
} }
} }
return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + theSystem + "]"); return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" + theSystem + "]");
} }

View File

@ -13,17 +13,28 @@ public class HapiWorkerContextTest {
@Test @Test
public void testIdTypes(){ public void testIdTypes(){
HapiWorkerContext hwc = new HapiWorkerContext(FhirContext.forDstu2(), new DefaultProfileValidationSupport()); DefaultProfileValidationSupport validationSupport = new DefaultProfileValidationSupport();
ValueSet vs = new ValueSet(); FhirContext ctx = FhirContext.forDstu2();
vs.setId("http://hl7.org/fhir/ValueSet/defined-types"); HapiWorkerContext hwc = new HapiWorkerContext(ctx, validationSupport);
ValueSet vs = validationSupport.fetchResource(ctx, ValueSet.class, "http://hl7.org/fhir/ValueSet/defined-types");
IWorkerContext.ValidationResult outcome; IWorkerContext.ValidationResult outcome;
outcome = hwc.validateCode("http://hl7.org/fhir/resource-types", "Patient", null);
assertTrue(outcome.isOk());
outcome = hwc.validateCode("http://hl7.org/fhir/resource-types", "Patient", null, vs);
assertTrue(outcome.isOk());
outcome = hwc.validateCode(null, "Patient", null, vs); outcome = hwc.validateCode(null, "Patient", null, vs);
assertTrue(outcome.isOk()); assertTrue(outcome.isOk());
outcome = hwc.validateCode(null, "id", null, vs); outcome = hwc.validateCode(null, "id", null, vs);
assertTrue(outcome.isOk()); assertTrue(outcome.isOk());
outcome = hwc.validateCode(null, "foo", null, vs);
assertFalse(outcome.isOk());
} }

View File

@ -151,10 +151,14 @@ public class ResourceMinimizerMojo extends AbstractMojo {
} }
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
FhirContext ctxDstu2 = FhirContext.forDstu2(); FhirContext ctxDstu2;
FhirContext ctxDstu2_1 = FhirContext.forDstu2_1(); FhirContext ctxDstu2_1;
FhirContext ctxDstu3 = FhirContext.forDstu3(); FhirContext ctxDstu3;
FhirContext ctxR4 = FhirContext.forR4(); FhirContext ctxR4;
ctxDstu2 = FhirContext.forDstu2();
ctxDstu2_1 = FhirContext.forDstu2_1();
ctxDstu3 = FhirContext.forDstu3();
ctxR4 = FhirContext.forR4();
LoggerContext loggerContext = ((ch.qos.logback.classic.Logger) ourLog).getLoggerContext(); LoggerContext loggerContext = ((ch.qos.logback.classic.Logger) ourLog).getLoggerContext();
URL mainURL = ConfigurationWatchListUtil.getMainWatchURL(loggerContext); URL mainURL = ConfigurationWatchListUtil.getMainWatchURL(loggerContext);

View File

@ -319,6 +319,10 @@
<action type="add"> <action type="add">
REST Hook subscriptions now honour the Subscription.channel.header field REST Hook subscriptions now honour the Subscription.channel.header field
</action> </action>
<action type="add">
DSTU2 validator has been enhanced to do a better job handling
ValueSets with expansions pointing to other ValueSets
</action>
</release> </release>
<release version="2.5" date="2017-06-08"> <release version="2.5" date="2017-06-08">
<action type="fix"> <action type="fix">