Refactor Context to support multiple terminology services

This commit is contained in:
Grahame Grieve 2024-01-08 21:07:08 +11:00
parent cdfd941205
commit d74b0e2b4a
34 changed files with 763 additions and 180 deletions

View File

@ -16,3 +16,4 @@ major upgrade to validation - use terminology server to perform more logic, and
* R6 release support
* IPS tool development
* Improved errors publishing IGs
* Refactor Context to support multiple terminology services

View File

@ -38,6 +38,7 @@ import java.util.UUID;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_14_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_14_50;
import org.hl7.fhir.convertors.txClient.TerminologyClientFactory;
import org.hl7.fhir.dstu2016may.formats.JsonParser;
import org.hl7.fhir.dstu2016may.formats.XmlParser;
import org.hl7.fhir.dstu2016may.model.Resource;
@ -49,6 +50,7 @@ import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
public class R2016MayToR5Loader extends BaseLoaderR5 {
@ -136,4 +138,9 @@ public class R2016MayToR5Loader extends BaseLoaderR5 {
return "4.3";
}
@Override
public ITerminologyClientFactory txFactory() {
return new TerminologyClientFactory(versionString());
}
}

View File

@ -38,6 +38,7 @@ import java.util.UUID;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_10_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50;
import org.hl7.fhir.convertors.txClient.TerminologyClientFactory;
import org.hl7.fhir.dstu2.formats.JsonParser;
import org.hl7.fhir.dstu2.formats.XmlParser;
import org.hl7.fhir.dstu2.model.Resource;
@ -50,6 +51,7 @@ import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
public class R2ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader {
@ -140,4 +142,9 @@ public class R2ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader
return "1.0";
}
@Override
public ITerminologyClientFactory txFactory() {
return new TerminologyClientFactory(versionString());
}
}

View File

@ -38,6 +38,7 @@ import java.util.UUID;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50;
import org.hl7.fhir.convertors.txClient.TerminologyClientFactory;
import org.hl7.fhir.dstu3.formats.JsonParser;
import org.hl7.fhir.dstu3.formats.XmlParser;
import org.hl7.fhir.dstu3.model.Resource;
@ -50,6 +51,7 @@ import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
public class R3ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader {
@ -135,4 +137,9 @@ public class R3ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader
return "3.0";
}
@Override
public ITerminologyClientFactory txFactory() {
return new TerminologyClientFactory(versionString());
}
}

View File

@ -38,6 +38,7 @@ import java.util.UUID;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.convertors.txClient.TerminologyClientFactory;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.formats.JsonParser;
import org.hl7.fhir.r4.formats.XmlParser;
@ -51,6 +52,7 @@ import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
import org.hl7.fhir.utilities.VersionUtilities;
public class R4BToR5Loader extends BaseLoaderR5 implements IContextResourceLoader {
@ -143,4 +145,9 @@ public class R4BToR5Loader extends BaseLoaderR5 implements IContextResourceLoade
}
@Override
public ITerminologyClientFactory txFactory() {
return new TerminologyClientFactory(versionString());
}
}

View File

@ -38,6 +38,7 @@ import java.util.UUID;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.convertors.txClient.TerminologyClientFactory;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r4.formats.JsonParser;
import org.hl7.fhir.r4.formats.XmlParser;
@ -51,6 +52,7 @@ import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
import org.hl7.fhir.utilities.VersionUtilities;
public class R4ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader {
@ -144,4 +146,9 @@ public class R4ToR5Loader extends BaseLoaderR5 implements IContextResourceLoader
}
@Override
public ITerminologyClientFactory txFactory() {
return new TerminologyClientFactory(versionString());
}
}

View File

@ -6,6 +6,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.hl7.fhir.convertors.txClient.TerminologyClientFactory;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
@ -47,6 +49,7 @@ import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
public class R5ToR5Loader extends BaseLoaderR5 {
@ -128,4 +131,9 @@ public class R5ToR5Loader extends BaseLoaderR5 {
}
@Override
public ITerminologyClientFactory txFactory() {
return new TerminologyClientFactory(versionString());
}
}

View File

@ -6,6 +6,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.hl7.fhir.convertors.txClient.TerminologyClientFactory;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
@ -47,6 +49,7 @@ import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
public class R6ToR5Loader extends BaseLoaderR5 {
@ -120,5 +123,10 @@ public class R6ToR5Loader extends BaseLoaderR5 {
return "5.0";
}
@Override
public ITerminologyClientFactory txFactory() {
return new TerminologyClientFactory(versionString());
}
}

View File

@ -3,36 +3,27 @@ package org.hl7.fhir.convertors.txClient;
import java.net.URISyntaxException;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientR5;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
public class TerminologyClientFactory {
public class TerminologyClientFactory implements ITerminologyClientFactory {
public static ITerminologyClient makeClient(String id, String url, String userAgent, FhirPublication v) throws URISyntaxException {
if (v == null)
return new TerminologyClientR5(id, checkEndsWith("/r4", url), userAgent);
switch (v) {
case DSTU2016May:
return new TerminologyClientR3(id, checkEndsWith("/r3", url), userAgent); // r3 is the least worst match
case DSTU1:
throw new Error("The version " + v + " is not currently supported");
case DSTU2:
return new TerminologyClientR2(id, checkEndsWith("/r2", url), userAgent);
case R4:
return new TerminologyClientR4(id, checkEndsWith("/r4", url), userAgent);
case R4B:
return new TerminologyClientR4(id, checkEndsWith("/r4", url), userAgent);
case R5:
return new TerminologyClientR5(id, checkEndsWith("/r4", url), userAgent); // r4 for now, since the terminology is currently the same
case STU3:
return new TerminologyClientR3(id, checkEndsWith("/r3", url), userAgent);
default:
throw new Error("The version " + v + " is not currently supported");
}
private String v;
public TerminologyClientFactory(FhirPublication v) {
super();
this.v = v == null ? null : v.toCode();
}
public TerminologyClientFactory(String version) {
super();
this.v = version;
}
public static ITerminologyClient makeClient(String id, String url, String userAgent, String v) throws URISyntaxException {
public ITerminologyClient makeClient(String id, String url, String userAgent) throws URISyntaxException {
if (v == null)
return new TerminologyClientR5(id, checkEndsWith("/r4", url), userAgent);
v = VersionUtilities.getMajMin(v);
@ -57,7 +48,7 @@ public class TerminologyClientFactory {
throw new Error("The version " + v + " is not currently supported");
}
private static String checkEndsWith(String term, String url) {
private String checkEndsWith(String term, String url) {
if (url.endsWith(term))
return url;
if (Utilities.isTxFhirOrgServer(url)) {
@ -66,4 +57,9 @@ public class TerminologyClientFactory {
return url;
}
@Override
public String getVersion() {
return v;
}
}

View File

@ -221,4 +221,9 @@ public class TerminologyClientR2 implements ITerminologyClient {
client.setContentLanguage(lang);
return this;
}
@Override
public int getUseCount() {
return client.getUseCount();
}
}

View File

@ -223,4 +223,10 @@ public class TerminologyClientR3 implements ITerminologyClient {
client.setContentLanguage(lang);
return this;
}
@Override
public int getUseCount() {
return client.getUseCount();
}
}

View File

@ -235,4 +235,11 @@ public class TerminologyClientR4 implements ITerminologyClient {
client.setContentLanguage(lang);
return this;
}
@Override
public int getUseCount() {
return client.getUseCount();
}
}

View File

@ -53,14 +53,14 @@ public class TerminologyClientFactoryTest {
@ParameterizedTest
@MethodSource("data")
public void testMakeClient(String url, FhirPublication fhirPublication, String expectedAddress) throws URISyntaxException {
ITerminologyClient terminologyClient = TerminologyClientFactory.makeClient("id", url, "dummyUserAgent", fhirPublication);
ITerminologyClient terminologyClient = new TerminologyClientFactory(fhirPublication).makeClient("id", url, "dummyUserAgent");
assertEquals(expectedAddress, terminologyClient.getAddress());
}
@Test
public void testMakeClientDstu1Fails() throws URISyntaxException {
assertThrows(Error.class, () -> {
ITerminologyClient terminologyClient = TerminologyClientFactory.makeClient("id", "urldoesnotmatter", "dummyUserAgent", FhirPublication.DSTU1);
ITerminologyClient terminologyClient = new TerminologyClientFactory(FhirPublication.DSTU1).makeClient("id", "urldoesnotmatter", "dummyUserAgent");
}
);
}
@ -68,7 +68,7 @@ public class TerminologyClientFactoryTest {
@Test
public void testMakeClientNullFails() throws URISyntaxException {
assertThrows(Error.class, () -> {
ITerminologyClient terminologyClient = TerminologyClientFactory.makeClient("id", "urldoesnotmatter", "dummyUserAgent", FhirPublication.NULL);
ITerminologyClient terminologyClient = new TerminologyClientFactory(FhirPublication.NULL).makeClient("id", "urldoesnotmatter", "dummyUserAgent");
}
);
}

View File

@ -96,6 +96,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
private int maxResultSetSize = -1;// _count
private Conformance conf;
private ClientUtils utils = new ClientUtils();
private int useCount;
// Pass enpoint for client - URI
public FHIRToolingClient(String baseServiceUrl, String userAgent) throws URISyntaxException {
@ -213,6 +214,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T read(Class<T> resourceClass, String id) {// TODO Change this to AddressableResource
recordUse();
ResourceRequest<T> result = null;
try {
result = utils.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),
@ -231,6 +233,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T vread(Class<T> resourceClass, String id, String version) {
recordUse();
ResourceRequest<T> result = null;
try {
result = utils.issueGetResourceRequest(
@ -254,6 +257,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
// fhir/ValueSet?url=http://hl7.org/fhir/ValueSet/clinical-findings&version=0.8
public <T extends Resource> T getCanonical(Class<T> resourceClass, String canonicalURL) {
recordUse();
ResourceRequest<T> result = null;
try {
result = utils.issueGetResourceRequest(
@ -279,6 +283,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Resource update(Resource resource) {
recordUse();
ResourceRequest<Resource> result = null;
try {
List<Header> headers = null;
@ -315,6 +320,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T update(Class<T> resourceClass, T resource, String id) {
recordUse();
ResourceRequest<T> result = null;
try {
List<Header> headers = null;
@ -515,6 +521,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
// }
public <T extends Resource> Parameters operateType(Class<T> resourceClass, String name, Parameters params) {
recordUse();
boolean complex = false;
for (ParametersParameterComponent p : params.getParameter())
complex = complex || !(p.getValue() instanceof PrimitiveType);
@ -548,6 +555,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Bundle transaction(Bundle batch) {
recordUse();
Bundle transactionResult = null;
try {
transactionResult = utils.postBatchRequest(resourceAddress.getBaseServiceUri(),
@ -561,6 +569,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
@SuppressWarnings("unchecked")
public <T extends Resource> OperationOutcome validate(Class<T> resourceClass, T resource, String id) {
recordUse();
ResourceRequest<T> result = null;
try {
result = utils.issuePostRequest(resourceAddress.resolveValidateUri(resourceClass, id),
@ -705,6 +714,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Bundle fetchFeed(String url) {
recordUse();
Bundle feed = null;
try {
feed = utils.issueGetFeedRequest(new URI(url), getPreferredResourceFormat());
@ -715,6 +725,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ValueSet expandValueset(ValueSet source) {
recordUse();
List<Header> headers = null;
ResourceRequest<Resource> result = utils.issuePostRequest(
resourceAddress.resolveOperationUri(ValueSet.class, "expand"),
@ -734,6 +745,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Parameters lookupCode(Map<String, String> params) {
recordUse();
ResourceRequest<Resource> result = utils.issueGetResourceRequest(
resourceAddress.resolveOperationUri(ValueSet.class, "lookup", params), getPreferredResourceFormat(),
timeoutNormal);
@ -751,6 +763,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ValueSet expandValueset(ValueSet source, Parameters expParams, Map<String, String> params) {
recordUse();
List<Header> headers = null;
Parameters p = expParams == null ? new Parameters() : expParams.copy();
p.addParameter().setName("valueSet").setResource(source);
@ -794,6 +807,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ConceptMap initializeClosure(String name) {
recordUse();
Parameters params = new Parameters();
params.addParameter().setName("name").setValue(new StringType(name));
List<Header> headers = null;
@ -815,6 +829,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ConceptMap updateClosure(String name, Coding coding) {
recordUse();
Parameters params = new Parameters();
params.addParameter().setName("name").setValue(new StringType(name));
params.addParameter().setName("concept").setValue(coding);
@ -902,4 +917,11 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
utils.setAcceptLanguage(lang);
}
public int getUseCount() {
return useCount;
}
private void recordUse() {
useCount++;
}
}

View File

@ -79,6 +79,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
private EnumSet<FhirPublication> allowedVersions;
private String acceptLang;
private String contentLang;
private int useCount;
//Pass endpoint for client - URI
public FHIRToolingClient(String baseServiceUrl, String userAgent) throws URISyntaxException {
@ -187,6 +188,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T read(Class<T> resourceClass, String id) {//TODO Change this to AddressableResource
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),
@ -204,6 +206,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T vread(Class<T> resourceClass, String id, String version) {
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndIdAndVersion(resourceClass, id, version),
@ -221,6 +224,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T getCanonical(Class<T> resourceClass, String canonicalURL) {
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndCanonical(resourceClass, canonicalURL),
@ -243,6 +247,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Resource update(Resource resource) {
recordUse();
org.hl7.fhir.dstu3.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()),
@ -270,6 +275,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T update(Class<T> resourceClass, T resource, String id) {
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),
@ -297,6 +303,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> Parameters operateType(Class<T> resourceClass, String name, Parameters params) {
recordUse();
boolean complex = false;
for (ParametersParameterComponent p : params.getParameter())
complex = complex || !(p.getValue() instanceof PrimitiveType);
@ -339,6 +346,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
public Bundle transaction(Bundle batch) {
recordUse();
Bundle transactionResult = null;
try {
transactionResult = client.postBatchRequest(resourceAddress.getBaseServiceUri(), ByteUtils.resourceToByteArray(batch, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(), "transaction", timeoutOperation + (timeoutEntry * batch.getEntry().size()));
@ -350,6 +358,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
@SuppressWarnings("unchecked")
public <T extends Resource> OperationOutcome validate(Class<T> resourceClass, T resource, String id) {
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveValidateUri(resourceClass, id),
@ -395,6 +404,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Bundle fetchFeed(String url) {
recordUse();
Bundle feed = null;
try {
feed = client.issueGetFeedRequest(new URI(url), getPreferredResourceFormat());
@ -405,6 +415,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ValueSet expandValueset(ValueSet source, Parameters expParams) {
recordUse();
Parameters p = expParams == null ? new Parameters() : expParams.copy();
p.addParameter().setName("valueSet").setResource(source);
org.hl7.fhir.dstu3.utils.client.network.ResourceRequest<Resource> result = null;
@ -426,6 +437,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
public Parameters lookupCode(Map<String, String> params) {
recordUse();
org.hl7.fhir.dstu3.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup", params),
@ -443,6 +455,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ValueSet expandValueset(ValueSet source, ExpansionProfile profile, Map<String, String> params) {
recordUse();
Parameters p = new Parameters();
p.addParameter().setName("valueSet").setResource(source);
if (profile != null)
@ -466,6 +479,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ValueSet expandValueset(ValueSet source, Parameters expParams, Map<String, String> params) {
recordUse();
Parameters p = expParams == null ? new Parameters() : expParams.copy();
p.addParameter().setName("valueSet").setResource(source);
for (String n : params.keySet()) {
@ -494,6 +508,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ConceptMap initializeClosure(String name) {
recordUse();
Parameters params = new Parameters();
params.addParameter().setName("name").setValue(new StringType(name));
ResourceRequest<Resource> result = null;
@ -514,6 +529,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ConceptMap updateClosure(String name, Coding coding) {
recordUse();
Parameters params = new Parameters();
params.addParameter().setName("name").setValue(new StringType(name));
params.addParameter().setName("concept").setValue(coding);
@ -629,5 +645,13 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
public void setContentLanguage(String lang) {
this.contentLang = lang;
}
public int getUseCount() {
return useCount;
}
private void recordUse() {
useCount++;
}
}

View File

@ -80,6 +80,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
private String userAgent;
private String acceptLang;
private String contentLang;
private int useCount;
// Pass endpoint for client - URI
public FHIRToolingClient(String baseServiceUrl, String userAgent) throws URISyntaxException {
@ -162,6 +163,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Resource read(String resourceClass, String id) {// TODO Change this to AddressableResource
recordUse();
ResourceRequest<Resource> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),
@ -178,6 +180,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T read(Class<T> resourceClass, String id) {// TODO Change this to AddressableResource
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),
@ -194,6 +197,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T vread(Class<T> resourceClass, String id, String version) {
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issueGetResourceRequest(
@ -211,6 +215,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T getCanonical(Class<T> resourceClass, String canonicalURL) {
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issueGetResourceRequest(
@ -233,6 +238,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Resource update(Resource resource) {
recordUse();
org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePutRequest(
@ -263,6 +269,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T update(Class<T> resourceClass, T resource, String id) {
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),
@ -292,6 +299,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> Parameters operateType(Class<T> resourceClass, String name, Parameters params) throws IOException {
recordUse();
boolean complex = false;
for (ParametersParameterComponent p : params.getParameter())
complex = complex || !(p.getValue() instanceof PrimitiveType);
@ -324,6 +332,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Bundle transaction(Bundle batch) {
recordUse();
Bundle transactionResult = null;
try {
transactionResult = client.postBatchRequest(resourceAddress.getBaseServiceUri(),
@ -337,6 +346,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
@SuppressWarnings("unchecked")
public <T extends Resource> OperationOutcome validate(Class<T> resourceClass, T resource, String id) {
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveValidateUri(resourceClass, id),
@ -383,6 +393,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Bundle fetchFeed(String url) {
recordUse();
Bundle feed = null;
try {
feed = client.issueGetFeedRequest(new URI(url), getPreferredResourceFormat(), timeoutLong);
@ -393,6 +404,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ValueSet expandValueset(String vsUrl, Parameters expParams) {
recordUse();
Map<String, String> parameters = new HashMap<>();
parameters.put("url", vsUrl);
if (expParams != null) {
@ -422,6 +434,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ValueSet expandValueset(ValueSet source, Parameters expParams) {
recordUse();
Parameters p = expParams == null ? new Parameters() : expParams.copy();
p.addParameter().setName("valueSet").setResource(source);
org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null;
@ -440,6 +453,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Parameters lookupCode(Map<String, String> params) {
recordUse();
org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup", params),
@ -455,6 +469,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ValueSet expandValueset(ValueSet source, Parameters expParams, Map<String, String> params) {
recordUse();
Parameters p = expParams == null ? new Parameters() : expParams.copy();
p.addParameter().setName("valueSet").setResource(source);
if (params != null) {
@ -489,6 +504,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ConceptMap initializeClosure(String name) {
recordUse();
Parameters params = new Parameters();
params.addParameter().setName("name").setValue(new StringType(name));
ResourceRequest<Resource> result = null;
@ -508,6 +524,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ConceptMap updateClosure(String name, Coding coding) {
recordUse();
Parameters params = new Parameters();
params.addParameter().setName("name").setValue(new StringType(name));
params.addParameter().setName("concept").setValue(coding);
@ -619,10 +636,12 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Bundle search(String type, String criteria) {
recordUse();
return fetchFeed(Utilities.pathURL(base, type+criteria));
}
public <T extends Resource> T fetchResource(Class<T> resourceClass, String id) {
recordUse();
org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveGetResource(resourceClass, id),
@ -636,6 +655,13 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
return (T) result.getPayload();
}
public int getUseCount() {
return useCount;
}
private void recordUse() {
useCount++;
}
}

View File

@ -132,6 +132,8 @@ import org.hl7.fhir.r5.terminologies.validation.VSCheckerException;
import org.hl7.fhir.r5.terminologies.validation.ValueSetValidator;
import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientR5.TerminologyClientR5Factory;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientContext;
import org.hl7.fhir.r5.utils.PackageHackerR5;
import org.hl7.fhir.r5.utils.ResourceUtilities;
@ -240,7 +242,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
private Object lock = new Object(); // used as a lock for the data that follows
protected String version; // although the internal resources are all R5, the version of FHIR they describe may not be
protected TerminologyClientContext tcc = new TerminologyClientContext();
protected TerminologyClientManager tcc = new TerminologyClientManager(new TerminologyClientR5Factory());
private boolean minimalMemory = false;
private Map<String, Map<String, ResourceProxy>> allResourcesById = new HashMap<String, Map<String, ResourceProxy>>();
@ -786,15 +788,15 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (tcc.getTxcaps() == null) {
try {
logger.logMessage("Terminology server: Check for supported code systems for "+system);
final TerminologyCapabilities capabilityStatement = txCache.hasTerminologyCapabilities() ? txCache.getTerminologyCapabilities() : tcc.getClient().getTerminologyCapabilities();
final TerminologyCapabilities capabilityStatement = txCache.hasTerminologyCapabilities() ? txCache.getTerminologyCapabilities() : tcc.getMasterClient().getTerminologyCapabilities();
txCache.cacheTerminologyCapabilities(capabilityStatement);
setTxCaps(capabilityStatement);
} catch (Exception e) {
if (canRunWithoutTerminology) {
noTerminologyServer = true;
logger.logMessage("==============!! Running without terminology server !! ==============");
if (tcc.getClient() != null) {
logger.logMessage("txServer = "+tcc.getClient().getId());
if (tcc.getMasterClient() != null) {
logger.logMessage("txServer = "+tcc.getMasterClient().getId());
logger.logMessage("Error = "+e.getMessage()+"");
}
logger.logMessage("=====================================================================");
@ -857,7 +859,9 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (res != null) {
return res;
}
Parameters p = constructParameters(vs, hierarchical);
Set<String> systems = findRelevantSystems(vs);
TerminologyClientContext tc = tcc.chooseServer(systems, true);
Parameters p = constructParameters(tc, vs, hierarchical);
for (ConceptSetComponent incl : vs.getCompose().getInclude()) {
codeSystemsUsed.add(incl.getSystem());
}
@ -871,13 +875,13 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
Map<String, String> params = new HashMap<String, String>();
params.put("_limit", Integer.toString(expandCodesLimit ));
params.put("_incomplete", "true");
txLog("$expand on "+txCache.summary(vs));
if (addDependentResources(p, vs)) {
txLog("$expand on "+txCache.summary(vs)+" on "+tc.getAddress());
if (addDependentResources(tc, p, vs)) {
p.addParameter().setName("cache-id").setValue(new IdType(tcc.getCacheId()));
}
try {
ValueSet result = tcc.getClient().expandValueset(vs, p, params);
ValueSet result = tc.getClient().expandValueset(vs, p, params);
res = new ValueSetExpansionOutcome(result).setTxLink(txLog.getLastId());
} catch (Exception e) {
res = new ValueSetExpansionOutcome(e.getMessage() == null ? e.getClass().getName() : e.getMessage(), TerminologyServiceErrorClass.UNKNOWN, true);
@ -972,14 +976,18 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
p.addParameter().setName("cache-id").setValue(new IdType(tcc.getCacheId()));
addDependentResources(p, vs);
Set<String> systems = findRelevantSystems(vs);
TerminologyClientContext tc = tcc.chooseServer(systems, true);
addDependentResources(tc, p, vs);
Map<String, String> params = new HashMap<String, String>();
params.put("_limit", Integer.toString(expandCodesLimit ));
params.put("_incomplete", "true");
txLog("$expand on "+txCache.summary(vs));
txLog("$expand on "+txCache.summary(vs)+" on "+tc.getAddress());
try {
ValueSet result = tcc.getClient().expandValueset(vs, p, params);
ValueSet result = tc.getClient().expandValueset(vs, p, params);
if (result != null) {
if (!result.hasUrl()) {
result.setUrl(vs.getUrl());
@ -1052,7 +1060,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (!t.hasResult()) {
try {
ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs == null ? t.getVsObj() : vs);
vsc.setThrowToServer(options.isUseServer() && tcc.getClient() != null);
vsc.setThrowToServer(options.isUseServer() && tcc.hasClient());
ValidationResult res = vsc.validateCode("Coding", t.getCoding());
if (txCache != null) {
txCache.cacheValidation(t.getCacheToken(), res, TerminologyCache.TRANSIENT);
@ -1082,7 +1090,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
// for those that that failed, we try to validate on the server
Bundle batch = new Bundle();
batch.setType(BundleType.BATCH);
Set<String> systems = new HashSet<>();
Set<String> systems = findRelevantSystems(vs);
for (CodingValidationRequest codingValidationRequest : codes) {
if (!codingValidationRequest.hasResult()) {
Parameters pIn = constructParameters(options, codingValidationRequest, vs == null ? codingValidationRequest.getVsObj() : vs);
@ -1097,16 +1105,19 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
be.setUserData("source", codingValidationRequest);
systems.add(codingValidationRequest.getCoding().getSystem());
findRelevantSystems(systems, codingValidationRequest.getCoding());
}
}
if (batch.getEntry().size() > 0) {
Bundle resp = processBatch(batch, systems);
TerminologyClientContext tc = tcc.chooseServer(systems, false);
Bundle resp = processBatch(tc, batch, systems);
for (int i = 0; i < batch.getEntry().size(); i++) {
CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source");
BundleEntryComponent r = resp.getEntry().get(i);
if (r.getResource() instanceof Parameters) {
t.setResult(processValidationResult((Parameters) r.getResource(), vs != null ? vs.getUrl() : t.getVsObj() != null ? t.getVsObj().getUrl() : null, tcc.getClient().getAddress()));
t.setResult(processValidationResult((Parameters) r.getResource(), vs != null ? vs.getUrl() : t.getVsObj() != null ? t.getVsObj().getUrl() : null, tc.getAddress()));
if (txCache != null) {
txCache.cacheValidation(t.getCacheToken(), t.getResult(), TerminologyCache.PERMANENT);
}
@ -1117,15 +1128,15 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
private Bundle processBatch(Bundle batch, Set<String> systems) {
private Bundle processBatch(TerminologyClientContext tc, Bundle batch, Set<String> systems) {
txLog("$batch validate for "+batch.getEntry().size()+" codes on systems "+systems.toString());
if (tcc.getClient() == null) {
if (tcc == null) {
throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE));
}
if (txLog != null) {
txLog.clearLastId();
}
Bundle resp = tcc.getClient().validateBatch(batch);
Bundle resp = tc.getClient().validateBatch(batch);
if (resp == null) {
throw new FHIRException(formatMessage(I18nConstants.TX_SERVER_NO_BATCH_RESPONSE));
}
@ -1149,14 +1160,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
t.setResult(txCache.getValidation(t.getCacheToken()));
}
}
ValueSet vs = fetchResource(ValueSet.class, vsUrl);
if (options.isUseClient()) {
ValueSet vs = fetchResource(ValueSet.class, vsUrl);
if (vs != null) {
for (CodingValidationRequest t : codes) {
if (!t.hasResult()) {
try {
ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs);
vsc.setThrowToServer(options.isUseServer() && tcc.getClient() != null);
vsc.setThrowToServer(options.isUseServer() && tcc.hasClient());
ValidationResult res = vsc.validateCode("Coding", t.getCoding());
if (txCache != null) {
txCache.cacheValidation(t.getCacheToken(), res, TerminologyCache.TRANSIENT);
@ -1187,7 +1198,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
// for those that that failed, we try to validate on the server
Bundle batch = new Bundle();
batch.setType(BundleType.BATCH);
Set<String> systems = new HashSet<>();
Set<String> systems = vs != null ? findRelevantSystems(vs) : new HashSet<>();
for (CodingValidationRequest codingValidationRequest : codes) {
if (!codingValidationRequest.hasResult()) {
Parameters pIn = constructParameters(options, codingValidationRequest, vsUrl);
@ -1204,14 +1215,16 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
systems.add(codingValidationRequest.getCoding().getSystem());
}
}
TerminologyClientContext tc = tcc.chooseServer(systems, false);
if (batch.getEntry().size() > 0) {
Bundle resp = processBatch(batch, systems);
Bundle resp = processBatch(tc, batch, systems);
for (int i = 0; i < batch.getEntry().size(); i++) {
CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source");
BundleEntryComponent r = resp.getEntry().get(i);
if (r.getResource() instanceof Parameters) {
t.setResult(processValidationResult((Parameters) r.getResource(), vsUrl, tcc.getClient().getAddress()));
t.setResult(processValidationResult((Parameters) r.getResource(), vsUrl, tc.getAddress()));
if (txCache != null) {
txCache.cacheValidation(t.getCacheToken(), t.getResult(), TerminologyCache.PERMANENT);
}
@ -1278,7 +1291,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
try {
ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs, ctxt);
vsc.setUnknownSystems(unknownSystems);
vsc.setThrowToServer(options.isUseServer() && tcc.getClient() != null);
vsc.setThrowToServer(options.isUseServer() && tcc.hasClient());
if (!ValueSetUtilities.isServerSide(code.getSystem())) {
res = vsc.validateCode(path, code);
if (txCache != null && cachingAllowed) {
@ -1307,14 +1320,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
if (localError != null && tcc.getClient() == null) {
if (localError != null && !tcc.hasClient()) {
if (unknownSystems.size() > 0) {
return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, issues).setUnknownSystems(unknownSystems);
} else {
return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.UNKNOWN, issues);
}
}
if (localWarning != null && tcc.getClient() == null) {
if (localWarning != null && !tcc.hasClient()) {
return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localWarning), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues);
}
if (!options.isUseServer()) {
@ -1333,15 +1346,19 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (noTerminologyServer) {
return new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE, issues);
}
Set<String> systems = findRelevantSystems(code, vs);
TerminologyClientContext tc = tcc.chooseServer(systems, false);
String csumm =cachingAllowed && txCache != null ? txCache.summary(code) : null;
if (cachingAllowed && txCache != null) {
txLog("$validate "+csumm+(vs == null ? "" : " for "+ txCache.summary(vs)));
txLog("$validate "+csumm+(vs == null ? "" : " for "+ txCache.summary(vs))+" on "+tc.getAddress());
} else {
txLog("$validate "+csumm+" before cache exists");
txLog("$validate "+csumm+" before cache exists on "+tc.getAddress());
}
try {
Parameters pIn = constructParameters(options, code);
res = validateOnServer(vs, pIn, options);
res = validateOnServer(tc, vs, pIn, options);
} catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage(), null).setTxLink(txLog == null ? null : txLog.getLastId()).setErrorClass(TerminologyServiceErrorClass.SERVER_ERROR);
}
@ -1375,12 +1392,12 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
return new ValueSetValidator(this, new TerminologyOperationContext(this, options), options, vs, expParameters, tcc.getTxcaps());
}
protected Parameters constructParameters(ValueSet vs, boolean hierarchical) {
protected Parameters constructParameters(TerminologyClientContext tcd, ValueSet vs, boolean hierarchical) {
Parameters p = expParameters.copy();
p.setParameter("includeDefinition", false);
p.setParameter("excludeNested", !hierarchical);
addDependentResources(p, vs);
addDependentResources(tcd, p, vs);
p.addParameter().setName("cache-id").setValue(new IdType(tcc.getCacheId()));
return p;
}
@ -1477,7 +1494,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
try {
ValueSetValidator vsc = constructValueSetCheckerSimple(options, vs);
vsc.setUnknownSystems(unknownSystems);
vsc.setThrowToServer(options.isUseServer() && tcc.getClient() != null);
vsc.setThrowToServer(options.isUseServer() && tcc.hasClient());
res = vsc.validateCode("CodeableConcept", code);
if (cachingAllowed) {
txCache.cacheValidation(cacheToken, res, TerminologyCache.TRANSIENT);
@ -1503,14 +1520,14 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
if (localError != null && tcc.getClient() == null) {
if (localError != null && !tcc.hasClient()) {
if (unknownSystems.size() > 0) {
return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED, issues).setUnknownSystems(unknownSystems);
} else {
return new ValidationResult(IssueSeverity.ERROR, localError, TerminologyServiceErrorClass.UNKNOWN, issues);
}
}
if (localWarning != null && tcc.getClient() == null) {
if (localWarning != null && !tcc.hasClient()) {
return new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER, localWarning), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS, issues);
}
@ -1522,10 +1539,13 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (noTerminologyServer) {
return new ValidationResult(IssueSeverity.ERROR, "Error validating code: running without terminology services", TerminologyServiceErrorClass.NOSERVICE, null);
}
txLog("$validate "+txCache.summary(code)+" for "+ txCache.summary(vs));
Set<String> systems = findRelevantSystems(code, vs);
TerminologyClientContext tc = tcc.chooseServer(systems, false);
txLog("$validate "+txCache.summary(code)+" for "+ txCache.summary(vs)+" on "+tc.getAddress());
try {
Parameters pIn = constructParameters(options, code);
res = validateOnServer(vs, pIn, options);
res = validateOnServer(tc, vs, pIn, options);
} catch (Exception e) {
res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage(), null).setTxLink(txLog == null ? null : txLog.getLastId());
}
@ -1535,7 +1555,74 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
return res;
}
protected ValidationResult validateOnServer(ValueSet vs, Parameters pin, ValidationOptions options) throws FHIRException {
private Set<String> findRelevantSystems(ValueSet vs) {
Set<String> set = new HashSet<>();
if (vs != null) {
findRelevantSystems(set, vs);
}
return set;
}
private Set<String> findRelevantSystems(CodeableConcept code, ValueSet vs) {
Set<String> set = new HashSet<>();
if (vs != null) {
findRelevantSystems(set, vs);
}
for (Coding c : code.getCoding()) {
findRelevantSystems(set, c);
}
return set;
}
private Set<String> findRelevantSystems(Coding code, ValueSet vs) {
Set<String> set = new HashSet<>();
if (vs != null) {
findRelevantSystems(set, vs);
}
if (code != null) {
findRelevantSystems(set, code);
}
return set;
}
private void findRelevantSystems(Set<String> set, ValueSet vs) {
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
findRelevantSystems(set, inc);
}
for (ConceptSetComponent inc : vs.getCompose().getExclude()) {
findRelevantSystems(set, inc);
}
}
private void findRelevantSystems(Set<String> set, ConceptSetComponent inc) {
if (inc.hasSystem()) {
if (inc.hasVersion()) {
set.add(inc.getSystem()+"|"+inc.getVersion());
} else {
set.add(inc.getSystem());
}
}
for (CanonicalType u : inc.getValueSet()) {
ValueSet vs = fetchResource(ValueSet.class, u.getValue());
if (vs != null) {
findRelevantSystems(set, vs);
} else {
set.add(TerminologyClientManager.UNRESOLVED_VALUESET);
}
}
}
private void findRelevantSystems(Set<String> set, Coding c) {
if (c.hasSystem()) {
if (c.hasVersion()) {
set.add(c.getSystem()+"|"+c.getVersion());
} else {
set.add(c.getSystem());
}
}
}
protected ValidationResult validateOnServer(TerminologyClientContext tc, ValueSet vs, Parameters pin, ValidationOptions options) throws FHIRException {
if (vs != null) {
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
@ -1546,38 +1633,38 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
addServerValidationParameters(vs, pin, options);
addServerValidationParameters(tc, vs, pin, options);
if (txLog != null) {
txLog.clearLastId();
}
if (tcc.getClient() == null) {
if (tc == null) {
throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE));
}
Parameters pOut;
if (vs == null) {
pOut = tcc.getClient().validateCS(pin);
pOut = tc.getClient().validateCS(pin);
} else {
pOut = tcc.getClient().validateVS(pin);
pOut = tc.getClient().validateVS(pin);
}
return processValidationResult(pOut, vs == null ? null : vs.getUrl(), tcc.getClient().getAddress());
return processValidationResult(pOut, vs == null ? null : vs.getUrl(), tc.getClient().getAddress());
}
protected void addServerValidationParameters(ValueSet vs, Parameters pin, ValidationOptions options) {
protected void addServerValidationParameters(TerminologyClientContext tcd, ValueSet vs, Parameters pin, ValidationOptions options) {
boolean cache = false;
if (vs != null) {
if (tcc.isTxCaching() && tcc.getCacheId() != null && vs.getUrl() != null && tcc.getCached().contains(vs.getUrl()+"|"+ vs.getVersion())) {
if (tcc.isTxCaching() && tcc.getCacheId() != null && vs.getUrl() != null && tcd.getCached().contains(vs.getUrl()+"|"+ vs.getVersion())) {
pin.addParameter().setName("url").setValue(new UriType(vs.getUrl()+(vs.hasVersion() ? "|"+ vs.getVersion() : "")));
} else if (options.getVsAsUrl()){
pin.addParameter().setName("url").setValue(new UriType(vs.getUrl()));
} else {
pin.addParameter().setName("valueSet").setResource(vs);
if (vs.getUrl() != null) {
tcc.getCached().add(vs.getUrl()+"|"+ vs.getVersion());
tcd.getCached().add(vs.getUrl()+"|"+ vs.getVersion());
}
}
cache = true;
addDependentResources(pin, vs);
addDependentResources(tcd, pin, vs);
}
pin.addParameter().setName("cache-id").setValue(new IdType(tcc.getCacheId()));
for (ParametersParameterComponent pp : pin.getParameter()) {
@ -1595,40 +1682,40 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
private boolean addDependentResources(Parameters pin, ValueSet vs) {
private boolean addDependentResources(TerminologyClientContext tc, Parameters pin, ValueSet vs) {
boolean cache = false;
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
cache = addDependentResources(pin, inc, vs) || cache;
cache = addDependentResources(tc, pin, inc, vs) || cache;
}
for (ConceptSetComponent inc : vs.getCompose().getExclude()) {
cache = addDependentResources(pin, inc, vs) || cache;
cache = addDependentResources(tc, pin, inc, vs) || cache;
}
return cache;
}
private boolean addDependentResources(Parameters pin, ConceptSetComponent inc, Resource src) {
private boolean addDependentResources(TerminologyClientContext tc, Parameters pin, ConceptSetComponent inc, Resource src) {
boolean cache = false;
for (CanonicalType c : inc.getValueSet()) {
ValueSet vs = fetchResource(ValueSet.class, c.getValue(), src);
if (vs != null && !hasCanonicalResource(pin, "tx-resource", vs.getVUrl())) {
cache = checkAddToParams(pin, vs) || cache;
addDependentResources(pin, vs);
cache = checkAddToParams(tc, pin, vs) || cache;
addDependentResources(tc, pin, vs);
}
}
CodeSystem cs = fetchResource(CodeSystem.class, inc.getSystem(), src);
if (cs != null && !hasCanonicalResource(pin, "tx-resource", cs.getVUrl()) && (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == CodeSystemContentMode.FRAGMENT)) {
cache = checkAddToParams(pin, cs) || cache;
cache = checkAddToParams(tc, pin, cs) || cache;
// todo: supplements
}
return cache;
}
private boolean checkAddToParams(Parameters pin, CanonicalResource cr) {
private boolean checkAddToParams(TerminologyClientContext tc, Parameters pin, CanonicalResource cr) {
boolean cache = false;
boolean addToParams = false;
if (tcc.usingCache()) {
if (!tcc.alreadyCached(cr)) {
tcc.addToCache(cr);
if (!tc.alreadyCached(cr)) {
tc.addToCache(cr);
if (logger.isDebugLogging()) {
logger.logMessage("add to cache: "+cr.getVUrl());
}
@ -1782,6 +1869,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
Utilities.createDirectory(cachePath);
}
txCache = new TerminologyCache(lock, cachePath);
tcc.setCache(txCache);
}
public void clearTSCache(String url) throws Exception {
@ -1814,7 +1902,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
@Override
public boolean isNoTerminologyServer() {
return noTerminologyServer || tcc.getClient() == null;
return noTerminologyServer || !tcc.hasClient();
}
public void setNoTerminologyServer(boolean noTerminologyServer) {
@ -2860,6 +2948,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
}
}
codeSystems.setVersion(version);
valueSets.setVersion(version);
maps.setVersion(version);
@ -2890,18 +2979,16 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
public int getClientRetryCount() {
return tcc.getClient() == null ? 0 : tcc.getClient().getRetryCount();
return tcc.getRetryCount();
}
public IWorkerContext setClientRetryCount(int value) {
if (tcc.getClient() != null) {
tcc.getClient().setRetryCount(value);
}
tcc.setRetryCount(value);
return this;
}
public ITerminologyClient getTxClient() {
return tcc.getClient();
public TerminologyClientManager getTxClientManager() {
return tcc;
}
public String getCacheId() {
@ -2959,8 +3046,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
protected void setUserAgent(String userAgent) {
this.userAgent = userAgent;
if (tcc.getClient() != null)
tcc.getClient().setUserAgent(userAgent);
tcc.setUserAgent(userAgent);
}

View File

@ -8,6 +8,7 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
import org.hl7.fhir.utilities.npm.NpmPackage;
import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformation;
@ -89,6 +90,11 @@ public interface IContextResourceLoader {
*/
IContextResourceLoader setLoadProfiles(boolean value);
/**
* @return the terminology client factory
*/
ITerminologyClientFactory txFactory();
/**
* Called during the loading process - the loader can decide which resources to load.
* At this point, only the .index.json is being read

View File

@ -68,6 +68,9 @@ import org.hl7.fhir.r5.profilemodel.PEBuilder;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.terminologies.JurisdictionUtilities;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientR5;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.R5Hacker;
import org.hl7.fhir.r5.utils.XVerExtensionManager;
@ -248,6 +251,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
public SimpleWorkerContext fromPackage(NpmPackage pi) throws IOException, FHIRException {
SimpleWorkerContext context = getSimpleWorkerContextInstance();
context.setAllowLoadingDuplicates(allowLoadingDuplicates);
context.tcc = new TerminologyClientManager(TerminologyClientR5.factory());
context.loadFromPackage(pi, null);
return build(context);
}
@ -256,6 +260,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
SimpleWorkerContext context = getSimpleWorkerContextInstance();
context.setAllowLoadingDuplicates(allowLoadingDuplicates);
context.version = pi.getNpm().asString("version");
context.tcc.setFactory(loader.txFactory());
context.loadFromPackage(pi, loader);
context.finishLoading(genSnapshots);
return build(context);
@ -327,22 +332,23 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
loadBytes(name, stream);
}
public String connectToTSServer(ITerminologyClient client, String log) {
public String connectToTSServer(ITerminologyClientFactory factory, ITerminologyClient client, String log) {
try {
txLog("Connect to "+client.getAddress());
tcc.setClient(client);
tcc.setFactory(factory);
tcc.setMasterClient(client);
if (log != null && (log.endsWith(".htm") || log.endsWith(".html"))) {
txLog = new HTMLClientLogger(log);
} else {
txLog = new TextClientLogger(log);
}
tcc.getClient().setLogger(txLog);
tcc.getClient().setUserAgent(userAgent);
tcc.setLogger(txLog);
tcc.setUserAgent(userAgent);
final CapabilityStatement capabilitiesStatementQuick = txCache.hasCapabilityStatement() ? txCache.getCapabilityStatement() : tcc.getClient().getCapabilitiesStatementQuick();
final CapabilityStatement capabilitiesStatementQuick = txCache.hasCapabilityStatement() ? txCache.getCapabilityStatement() : tcc.getMasterClient().getCapabilitiesStatementQuick();
txCache.cacheCapabilityStatement(capabilitiesStatementQuick);
final TerminologyCapabilities capabilityStatement = txCache.hasTerminologyCapabilities() ? txCache.getTerminologyCapabilities() : tcc.getClient().getTerminologyCapabilities();
final TerminologyCapabilities capabilityStatement = txCache.hasTerminologyCapabilities() ? txCache.getTerminologyCapabilities() : tcc.getMasterClient().getTerminologyCapabilities();
txCache.cacheTerminologyCapabilities(capabilityStatement);
setTxCaps(capabilityStatement);
@ -532,6 +538,9 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
version = "5.0.0";
}
}
if (loader != null) {
tcc.setFactory(loader.txFactory());
}
return t;
}

View File

@ -68,4 +68,5 @@ public interface ITerminologyClient {
ITerminologyClient setAcceptLanguage(String lang);
ITerminologyClient setContentLanguage(String lang);
String getUserAgent();
int getUseCount();
}

View File

@ -1,65 +1,70 @@
package org.hl7.fhir.r5.terminologies.client;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientContext.TerminologyClientContextUseCount;
public class TerminologyClientContext {
private String cacheId;
private boolean isTxCaching;
private int serverQueryCount = 0;
private final Set<String> cached = new HashSet<>();
protected String server;
public class TerminologyClientContextUseCount {
private int expands;
private int validates;
public int getExpands() {
return expands;
}
public void setExpands(int expands) {
this.expands = expands;
}
public int getValidates() {
return validates;
}
public void setValidates(int validates) {
this.validates = validates;
}
}
private ITerminologyClient client;
Map<String, TerminologyClientContextUseCount> useCounts = new HashMap<>();
private TerminologyCapabilities txcaps;
private final Set<String> cached = new HashSet<>();
private boolean master;
public String getCacheId() {
return cacheId;
}
public void setCacheId(String cacheId) {
this.cacheId = cacheId;
}
public boolean isTxCaching() {
return isTxCaching;
}
public void setTxCaching(boolean isTxCaching) {
this.isTxCaching = isTxCaching;
}
public int getServerQueryCount() {
return serverQueryCount;
}
public void setServerQueryCount(int serverQueryCount) {
this.serverQueryCount = serverQueryCount;
}
public Set<String> getCached() {
return cached;
protected TerminologyClientContext(ITerminologyClient client, boolean master) {
super();
this.client = client;
this.master = master;
}
public String getServer() {
return server;
public Map<String, TerminologyClientContextUseCount> getUseCounts() {
return useCounts;
}
public void setServer(String server) {
this.server = server;
public boolean isMaster() {
return master;
}
public ITerminologyClient getClient() {
return client;
}
public void setClient(ITerminologyClient client) {
this.client = client;
public void seeUse(String s, boolean expand) {
TerminologyClientContextUseCount uc = useCounts.get(s);
if (uc == null) {
uc = new TerminologyClientContextUseCount();
useCounts.put(s,uc);
}
if (expand) {
uc.expands++;
} else {
uc.validates++;
}
}
public TerminologyCapabilities getTxcaps() {
return txcaps;
}
@ -68,18 +73,8 @@ public class TerminologyClientContext {
this.txcaps = txcaps;
}
public void copy(TerminologyClientContext other) {
cacheId = other.cacheId;
isTxCaching = other.isTxCaching;
cached.addAll(other.cached);
// tsServer = other.tsServer;
client = other.client;
txcaps = other.txcaps;
}
public boolean usingCache() {
return isTxCaching && cacheId != null;
public Set<String> getCached() {
return cached;
}
public boolean alreadyCached(CanonicalResource cr) {
@ -90,6 +85,12 @@ public class TerminologyClientContext {
cached.add(cr.getVUrl());
}
public String getAddress() {
return client.getAddress();
}
public int getUseCount() {
return getClient().getUseCount();
}
}

View File

@ -0,0 +1,266 @@
package org.hl7.fhir.r5.terminologies.client;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyCache;
import org.hl7.fhir.utilities.ToolingClientLogger;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.json.JsonException;
import org.hl7.fhir.utilities.json.model.JsonObject;
import org.hl7.fhir.utilities.json.parser.JsonParser;
public class TerminologyClientManager {
public ITerminologyClientFactory getFactory() {
return factory;
}
public interface ITerminologyClientFactory {
ITerminologyClient makeClient(String id, String url, String userAgent) throws URISyntaxException;
String getVersion();
}
public static final String UNRESOLVED_VALUESET = "--unknown--";
private ITerminologyClientFactory factory;
private String cacheId;
private boolean isTxCaching;
private List<TerminologyClientContext> serverList = new ArrayList<>(); // clients by server address
private Map<String, TerminologyClientContext> serverMap = new HashMap<>(); // clients by server address
private Map<String, String> resMap = new HashMap<>(); // client resolution list
private List<String> internalErrors = new ArrayList<>();
private TerminologyCache cache;
private File cacheFile;
private String monitorServiceURL;
public TerminologyClientManager(ITerminologyClientFactory factory) {
super();
this.factory = factory;
}
public String getCacheId() {
return cacheId;
}
public void setCacheId(String cacheId) {
this.cacheId = cacheId;
}
public boolean isTxCaching() {
return isTxCaching;
}
public void setTxCaching(boolean isTxCaching) {
this.isTxCaching = isTxCaching;
}
public void copy(TerminologyClientManager other) {
cacheId = other.cacheId;
isTxCaching = other.isTxCaching;
serverList.addAll(other.serverList);
serverMap.putAll(other.serverMap);
resMap.putAll(other.resMap);
}
public boolean usingCache() {
return isTxCaching && cacheId != null;
}
public TerminologyClientContext chooseServer(Set<String> systems, boolean expand) throws TerminologyServiceException {
if (serverList.isEmpty()) {
return null;
}
if (systems.contains(UNRESOLVED_VALUESET)) {
return serverList.get(0);
}
Set<TerminologyClientContext> clients = new HashSet<>();
for (String s : systems) {
clients.add(findServerForSystem(s, expand));
}
if (clients.size() == 1) {
return clients.iterator().next();
} else {
System.out.println("systems: "+systems.toString());
return serverList.get(0);
}
}
private TerminologyClientContext findServerForSystem(String s, boolean expand) throws TerminologyServiceException {
String server = resMap.get(s);
if (server == null) {
server = decideWhichServer(s);
resMap.put(s, server);
save();
}
TerminologyClientContext client = serverMap.get(server);
if (client == null) {
try {
client = new TerminologyClientContext(factory.makeClient("id"+(serverList.size()+1), server, getMasterClient().getUserAgent()), false);
} catch (URISyntaxException e) {
throw new TerminologyServiceException(e);
}
serverList.add(client);
serverMap.put(server, client);
}
client.seeUse(s, expand);
return client;
}
private String decideWhichServer(String url) {
String request = Utilities.pathURL(monitorServiceURL, "resolve?fhirVersion="+factory.getVersion()+"&url="+url);
try {
JsonObject json = JsonParser.parseObjectFromUrl(request);
for (JsonObject item : json.getJsonObjects("authoritative")) {
return item.asString("url");
}
for (JsonObject item : json.getJsonObjects("candidate")) {
return item.asString("url");
}
} catch (Exception e) {
String msg = "Error resolving system "+url+": "+e.getMessage()+" ("+request+")";
if (!internalErrors.contains(msg)) {
internalErrors.add(msg);
}
e.printStackTrace();
}
return getMasterClient().getAddress();
}
public List<TerminologyClientContext> serverList() {
return serverList;
}
public boolean hasClient() {
return !serverList.isEmpty();
}
public int getRetryCount() {
return hasClient() ? getMasterClient().getRetryCount() : 0;
}
public void setRetryCount(int value) {
if (hasClient()) {
getMasterClient().setRetryCount(value);
}
}
public void setUserAgent(String value) {
if (hasClient()) {
getMasterClient().setUserAgent(value);
}
}
public void setLogger(ToolingClientLogger txLog) {
if (hasClient()) {
getMasterClient().setLogger(txLog);
}
}
public void setMasterClient(ITerminologyClient client) {
TerminologyClientContext details = new TerminologyClientContext(client, true);
serverList.clear();
serverList.add(details);
serverMap.put(client.getAddress(), details);
monitorServiceURL = Utilities.pathURL(Utilities.getDirectoryForURL(client.getAddress()), "tx-reg");
}
public TerminologyClientContext getMaster() {
return serverList.isEmpty() ? null : serverList.get(0);
}
public ITerminologyClient getMasterClient() {
return serverList.isEmpty() ? null : serverList.get(0).getClient();
}
public Map<String, TerminologyClientContext> serverMap() {
Map<String, TerminologyClientContext> map = new HashMap<>();
for (TerminologyClientContext t : serverList) {
map.put(t.getClient().getAddress(), t);
}
return map;
}
public TerminologyCapabilities getTxcaps() {
return hasClient() ? serverList.get(0).getTxcaps() : null;
}
public void setTxcaps(TerminologyCapabilities txCaps) {
serverList.get(0).setTxcaps(txCaps);
}
public void setFactory(ITerminologyClientFactory factory) {
this.factory = factory;
}
public void setCache(TerminologyCache cache) {
this.cache = cache;
this.cacheFile = null;
if (cache != null) {
try {
cacheFile = new File(Utilities.path(cache.getFolder(), "system-map.json"));
if (cacheFile.exists()) {
JsonObject json = JsonParser.parseObject(cacheFile);
for (JsonObject pair : json.getJsonObjects("systems")) {
resMap.put(pair.asString("system"), pair.asString("server"));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void save() {
if (cacheFile != null) {
JsonObject json = new JsonObject();
for (String s : Utilities.sorted(resMap.keySet())) {
JsonObject si = new JsonObject();
json.forceArray("systems").add(si);
si.add("system", s);
si.add("server", resMap.get(s));
}
try {
JsonParser.compose(json, cacheFile, true);
} catch (IOException e) {
}
}
}
public List<String> getInternalErrors() {
return internalErrors;
}
public List<TerminologyClientContext> getServerList() {
return serverList;
}
public Map<String, TerminologyClientContext> getServerMap() {
return serverMap;
}
public String getMonitorServiceURL() {
return monitorServiceURL;
}
}

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.convertors.txClient;
package org.hl7.fhir.r5.terminologies.client;
import java.net.URISyntaxException;
import java.util.EnumSet;
@ -44,6 +44,8 @@ import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.TerminologyCapabilities;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.terminologies.client.ITerminologyClient;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientR5.TerminologyClientR5Factory;
import org.hl7.fhir.r5.utils.client.FHIRToolingClient;
import org.hl7.fhir.r5.utils.client.network.ClientHeaders;
import org.hl7.fhir.utilities.FhirPublication;
@ -52,6 +54,28 @@ import org.hl7.fhir.utilities.Utilities;
public class TerminologyClientR5 implements ITerminologyClient {
public static class TerminologyClientR5Factory implements ITerminologyClientFactory {
@Override
public ITerminologyClient makeClient(String id, String url, String userAgent) throws URISyntaxException {
return new TerminologyClientR5(id, checkEndsWith("/r4", url), userAgent);
}
private String checkEndsWith(String term, String url) {
if (url.endsWith(term))
return url;
if (Utilities.isTxFhirOrgServer(url)) {
return Utilities.pathURL(url, term);
}
return url;
}
@Override
public String getVersion() {
return "5.0.0";
}
}
private final FHIRToolingClient client;
private ClientHeaders clientHeaders;
private String id;
@ -211,4 +235,14 @@ public class TerminologyClientR5 implements ITerminologyClient {
client.setContentLanguage(lang);
return this;
}
@Override
public int getUseCount() {
return client.getUseCount();
}
public static ITerminologyClientFactory factory() {
return new TerminologyClientR5Factory();
}
}

View File

@ -1,6 +1,6 @@
package org.hl7.fhir.r5.test.utils;
import java.io.IOException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
@ -12,6 +12,8 @@ import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientManager.ITerminologyClientFactory;
import org.hl7.fhir.r5.terminologies.client.TerminologyClientR5.TerminologyClientR5Factory;
import org.hl7.fhir.utilities.npm.NpmPackage;
import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformation;
@ -73,4 +75,9 @@ public class TestPackageLoader implements IContextResourceLoader {
return true;
}
@Override
public ITerminologyClientFactory txFactory() {
return new TerminologyClientR5Factory();
}
}

View File

@ -106,6 +106,9 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
private String acceptLang;
private String contentLang;
private int useCount;
//Pass endpoint for client - URI
public FHIRToolingClient(String baseServiceUrl, String userAgent) throws URISyntaxException {
preferredResourceFormat = ResourceFormat.RESOURCE_JSON;
@ -206,6 +209,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Resource read(String resourceClass, String id) {// TODO Change this to AddressableResource
recordUse();
ResourceRequest<Resource> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),
@ -223,6 +227,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
public <T extends Resource> T read(Class<T> resourceClass, String id) {//TODO Change this to AddressableResource
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),
@ -240,6 +245,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T vread(Class<T> resourceClass, String id, String version) {
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndIdAndVersion(resourceClass, id, version),
@ -257,6 +263,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T getCanonical(Class<T> resourceClass, String canonicalURL) {
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveGetUriFromResourceClassAndCanonical(resourceClass, canonicalURL),
@ -279,6 +286,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Resource update(Resource resource) {
recordUse();
org.hl7.fhir.r5.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()),
@ -306,6 +314,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> T update(Class<T> resourceClass, T resource, String id) {
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),
@ -333,6 +342,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public <T extends Resource> Parameters operateType(Class<T> resourceClass, String name, Parameters params) {
recordUse();
boolean complex = false;
for (ParametersParameterComponent p : params.getParameter())
complex = complex || !(p.getValue() instanceof PrimitiveType);
@ -368,6 +378,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Bundle transaction(Bundle batch) {
recordUse();
Bundle transactionResult = null;
try {
transactionResult = client.postBatchRequest(resourceAddress.getBaseServiceUri(), ByteUtils.resourceToByteArray(batch, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(),
@ -381,6 +392,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
@SuppressWarnings("unchecked")
public <T extends Resource> OperationOutcome validate(Class<T> resourceClass, T resource, String id) {
recordUse();
ResourceRequest<T> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveValidateUri(resourceClass, id),
@ -426,6 +438,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Bundle fetchFeed(String url) {
recordUse();
Bundle feed = null;
try {
feed = client.issueGetFeedRequest(new URI(url), getPreferredResourceFormat());
@ -436,6 +449,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ValueSet expandValueset(ValueSet source, Parameters expParams) {
recordUse();
Parameters p = expParams == null ? new Parameters() : expParams.copy();
p.addParameter().setName("valueSet").setResource(source);
org.hl7.fhir.r5.utils.client.network.ResourceRequest<Resource> result = null;
@ -457,6 +471,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
public Parameters lookupCode(Map<String, String> params) {
recordUse();
org.hl7.fhir.r5.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup", params),
@ -474,6 +489,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ValueSet expandValueset(ValueSet source, Parameters expParams, Map<String, String> params) {
recordUse();
Parameters p = expParams == null ? new Parameters() : expParams.copy();
if (source != null) {
p.addParameter().setName("valueSet").setResource(source);
@ -507,6 +523,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ConceptMap initializeClosure(String name) {
recordUse();
Parameters params = new Parameters();
params.addParameter().setName("name").setValue(new StringType(name));
ResourceRequest<Resource> result = null;
@ -527,6 +544,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public ConceptMap updateClosure(String name, Coding coding) {
recordUse();
Parameters params = new Parameters();
params.addParameter().setName("name").setValue(new StringType(name));
params.addParameter().setName("concept").setValue(coding);
@ -654,10 +672,12 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
public Bundle search(String type, String criteria) {
recordUse();
return fetchFeed(Utilities.pathURL(base, type+criteria));
}
public <T extends Resource> T fetchResource(Class<T> resourceClass, String id) {
recordUse();
org.hl7.fhir.r5.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issueGetResourceRequest(resourceAddress.resolveGetResource(resourceClass, id),
@ -671,6 +691,14 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
}
return (T) result.getPayload();
}
private void recordUse() {
useCount++;
}
public int getUseCount() {
return useCount;
}
}

View File

@ -94,7 +94,7 @@ public class BaseWorkerContextTests {
public void testAddServerValidationParametersDisplayWarning() throws IOException {
BaseWorkerContext baseWorkerContext = getBaseWorkerContext();
Parameters pin = new Parameters();
baseWorkerContext.addServerValidationParameters(new ValueSet(), pin, new ValidationOptions(FhirPublication.fromCode(baseWorkerContext.getVersion())).setDisplayWarningMode(true));
baseWorkerContext.addServerValidationParameters(baseWorkerContext.getTxClientManager().getMaster(), new ValueSet(), pin, new ValidationOptions(FhirPublication.fromCode(baseWorkerContext.getVersion())).setDisplayWarningMode(true));
assertEquals("lenient-display-validation", pin.getParameter("mode").getValue().primitiveValue());
}
@ -102,7 +102,7 @@ public class BaseWorkerContextTests {
public void testAddServerValidationParametersVsAsUrl() throws IOException {
BaseWorkerContext baseWorkerContext = getBaseWorkerContext();
Parameters pin = new Parameters();
baseWorkerContext.addServerValidationParameters(new ValueSet().setUrl("http://dummy.org/vs"), pin, new ValidationOptions(FhirPublication.fromCode(baseWorkerContext.getVersion())).setVsAsUrl(true));
baseWorkerContext.addServerValidationParameters(baseWorkerContext.getTxClientManager().getMaster(), new ValueSet().setUrl("http://dummy.org/vs"), pin, new ValidationOptions(FhirPublication.fromCode(baseWorkerContext.getVersion())).setVsAsUrl(true));
assertEquals("uri", pin.getParameter("url").getValue().fhirType());
assertEquals("http://dummy.org/vs", pin.getParameter("url").getValue().primitiveValue());
}
@ -112,7 +112,7 @@ public class BaseWorkerContextTests {
BaseWorkerContext baseWorkerContext = getBaseWorkerContext();
Parameters pin = new Parameters();
baseWorkerContext.addServerValidationParameters(new ValueSet(), pin, new ValidationOptions(FhirPublication.fromCode(baseWorkerContext.getVersion())));
baseWorkerContext.addServerValidationParameters(baseWorkerContext.getTxClientManager().getMaster(), new ValueSet(), pin, new ValidationOptions(FhirPublication.fromCode(baseWorkerContext.getVersion())));
assertNull(pin.getParameter("mode"));
}
}

View File

@ -93,7 +93,7 @@ public class SimpleWorkerContextTests {
public void beforeEach() {
context.txCache = terminologyCache;
context.expParameters = expParameters;
context.tcc.setClient(terminologyClient);
context.tcc.setMasterClient(terminologyClient);
context.txLog = txLog;
}
@ -189,7 +189,7 @@ public class SimpleWorkerContextTests {
Mockito.doReturn(cacheToken).when(terminologyCache).generateValidationToken(validationOptions, coding, valueSet, expParameters);
Mockito.doReturn(pIn).when(context).constructParameters(validationOptions, coding);
Mockito.doReturn(expectedValidationResult).when(context).validateOnServer(valueSet, pIn, validationOptions);
Mockito.doReturn(expectedValidationResult).when(context).validateOnServer(context.getTxClientManager().getMaster(), valueSet, pIn, validationOptions);
ValidationContextCarrier ctxt = mock(ValidationContextCarrier.class);
@ -233,7 +233,7 @@ public class SimpleWorkerContextTests {
Mockito.verify(valueSetCheckerSimple).validateCode("CodeableConcept", codeableConcept);
Mockito.verify(terminologyCache).cacheValidation(cacheToken, expectedValidationResult, false);
Mockito.verify(context, times(0)).validateOnServer(any(), any(), any());
Mockito.verify(context, times(0)).validateOnServer(context.getTxClientManager().getMaster(), any(), any(), any());
}
@ -246,7 +246,7 @@ public class SimpleWorkerContextTests {
ValidationOptions validationOptions = CacheTestUtils.validationOptions.withNoClient();
Mockito.doReturn(pIn).when(context).constructParameters(validationOptions, codeableConcept);
Mockito.doReturn(expectedValidationResult).when(context).validateOnServer(valueSet, pIn, validationOptions);
Mockito.doReturn(expectedValidationResult).when(context).validateOnServer(context.getTxClientManager().getMaster(), valueSet, pIn, validationOptions);
Mockito.doReturn(cacheToken).when(terminologyCache).generateValidationToken(validationOptions, codeableConcept, valueSet, expParameters);
@ -256,7 +256,7 @@ public class SimpleWorkerContextTests {
Mockito.verify(valueSetCheckerSimple, times(0)).validateCode("CodeableConcept", codeableConcept);
Mockito.verify(terminologyCache).cacheValidation(cacheToken, expectedValidationResult, true);
Mockito.verify(context).validateOnServer(valueSet, pIn, validationOptions);
Mockito.verify(context).validateOnServer(context.getTxClientManager().getMaster(),valueSet, pIn, validationOptions);
}
@Test
@ -295,7 +295,8 @@ public class SimpleWorkerContextTests {
Mockito.doReturn(cacheToken).when(terminologyCache).generateExpandToken(argThat(new ValueSetMatcher(vs)),eq(true));
Mockito.doReturn(expParameters).when(context).constructParameters(argThat(new ValueSetMatcher(vs)), eq(true));
// #FIXME
// Mockito.doReturn(expParameters).when(context).constructParameters(argThat(new ValueSetMatcher(vs)), eq(true));
ValueSet expectedValueSet = new ValueSet();
@ -408,7 +409,7 @@ public class SimpleWorkerContextTests {
Mockito.doReturn(terminologyCapabilities).when(terminologyCache).getTerminologyCapabilities();
Mockito.doReturn(capabilitiesStatement).when(terminologyCache).getCapabilityStatement();
String actual = context.connectToTSServer(terminologyClient, null);
String actual = context.connectToTSServer(null, terminologyClient, null);
assertEquals("dummyVersion", actual);
@ -430,7 +431,7 @@ public class SimpleWorkerContextTests {
Mockito.doReturn(terminologyCapabilities).when(terminologyClient).getTerminologyCapabilities();
Mockito.doReturn(capabilitiesStatement).when(terminologyClient).getCapabilitiesStatementQuick();
String actual = context.connectToTSServer(terminologyClient, null);
String actual = context.connectToTSServer(null, terminologyClient, null);
assertEquals("dummyVersion", actual);

View File

@ -750,6 +750,10 @@ public class Utilities {
return f.getParent();
}
public static String getDirectoryForURL(String url) {
return url.contains("/") && url.lastIndexOf("/") > 10 ? url.substring(0, url.lastIndexOf("/")) : url;
}
public static String appendPeriod(String s) {
if (Utilities.noString(s))
return s;

View File

@ -672,7 +672,8 @@ public class JsonParser {
private static byte[] fetch(String source) throws IOException {
SimpleHTTPClient fetcher = new SimpleHTTPClient();
fetcher.addHeader("Accept", "application/json, application/fhir+json");
HTTPResult res = fetcher.get(source+"?nocache=" + System.currentTimeMillis());
String murl = source.contains("?") ? source+"&nocache=" + System.currentTimeMillis() : source+"?nocache=" + System.currentTimeMillis();
HTTPResult res = fetcher.get(murl);
res.checkThrowException();
return res.getContent();
}

View File

@ -523,7 +523,8 @@ public class ValidationEngine implements IValidatorResourceFetcher, IValidationP
return "n/a: No Terminology Server";
} else {
try {
return context.connectToTSServer(TerminologyClientFactory.makeClient("Tx-Server", url, context.getUserAgent(), version), log);
TerminologyClientFactory factory = new TerminologyClientFactory(version);
return context.connectToTSServer(factory, factory.makeClient("Tx-Server", url, context.getUserAgent()), log);
} catch (Exception e) {
if (context.isCanRunWithoutTerminology()) {
return "n/a: Running without Terminology Server (error: " + e.getMessage() + ")";

View File

@ -289,7 +289,7 @@ public class StandAloneValidatorFetcher implements IValidatorResourceFetcher, IV
@Nonnull
protected ITerminologyClient getTerminologyClient(String root) throws URISyntaxException {
return TerminologyClientFactory.makeClient("source", root, Common.getValidatorUserAgent(), context.getVersion());
return new TerminologyClientFactory(context.getVersion()).makeClient("source", root, Common.getValidatorUserAgent());
}
private String getRoot(String[] p, String url) {

View File

@ -139,7 +139,7 @@ public class TxTester {
private ITerminologyClient connectToServer() throws URISyntaxException {
System.out.println("Connect to "+server);
return TerminologyClientFactory.makeClient("Test-Server", server, "Tools/Java", FhirPublication.R4);
return new TerminologyClientFactory(FhirPublication.R4).makeClient("Test-Server", server, "Tools/Java");
}

View File

@ -32,7 +32,7 @@ public class ValidationEngineTests {
System.out.println("TestCurrentXml: Validate patient-example.xml in Current version");
ValidationEngine ve = TestUtilities.getValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, FhirPublication.R4, "4.0.1");
CacheVerificationLogger logger = new CacheVerificationLogger();
ve.getContext().getTxClient().setLogger(logger);
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient-example.xml"), null);
int e = errors(op);
int w = warnings(op);
@ -55,7 +55,7 @@ public class ValidationEngineTests {
System.out.println("TestCurrentJson: Validate patient-example.json in Current version");
ValidationEngine ve = TestUtilities.getValidationEngine("hl7.fhir.r4.core#4.0.1", DEF_TX, FhirPublication.R4, "4.0.1");
CacheVerificationLogger logger = new CacheVerificationLogger();
ve.getContext().getTxClient().setLogger(logger);
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
OperationOutcome op = ve.validate(FhirFormat.JSON, TestingUtilities.loadTestResourceStream("validator", "patient-example.json"), null);
int e = errors(op);
int w = warnings(op);
@ -74,7 +74,7 @@ public class ValidationEngineTests {
System.out.println("TestCurrentXml: Validate patient-example.xml in Current version");
ValidationEngine ve = TestUtilities.getValidationEngine("hl7.fhir.r4b.core#4.3.0", DEF_TX, FhirPublication.R4, "4.3.0");
CacheVerificationLogger logger = new CacheVerificationLogger();
ve.getContext().getTxClient().setLogger(logger);
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient-example.xml"), null);
int e = errors(op);
int w = warnings(op);
@ -97,7 +97,7 @@ public class ValidationEngineTests {
System.out.println("TestCurrentJson: Validate patient-example.json in Current version");
ValidationEngine ve = TestUtilities.getValidationEngine("hl7.fhir.r4b.core#4.3.0", DEF_TX, FhirPublication.R4, "4.3.0");
CacheVerificationLogger logger = new CacheVerificationLogger();
ve.getContext().getTxClient().setLogger(logger);
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
OperationOutcome op = ve.validate(FhirFormat.JSON, TestingUtilities.loadTestResourceStream("validator", "patient-example.json"), null);
int e = errors(op);
int w = warnings(op);
@ -126,7 +126,7 @@ public class ValidationEngineTests {
System.out.println("Test140: Validate patient-example.xml in v1.4.0 version");
ValidationEngine ve = TestUtilities.getValidationEngine("hl7.fhir.r2b.core#1.4.0", DEF_TX, FhirPublication.DSTU2016May, "1.4.0");
CacheVerificationLogger logger = new CacheVerificationLogger();
ve.getContext().getTxClient().setLogger(logger);
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient140.xml"), null);
if (!TestUtilities.silent)
for (OperationOutcomeIssueComponent iss : op.getIssue()) {
@ -154,7 +154,7 @@ public class ValidationEngineTests {
ValidationEngine ve = TestUtilities.getValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, FhirPublication.DSTU2, "1.0.2");
ve.setNoInvariantChecks(true);
CacheVerificationLogger logger = new CacheVerificationLogger();
ve.getContext().getTxClient().setLogger(logger);
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "patient102.xml"), null);
if (!TestUtilities.silent)
for (OperationOutcomeIssueComponent iss : op.getIssue()) {
@ -182,7 +182,7 @@ public class ValidationEngineTests {
ValidationEngine ve = TestUtilities.getValidationEngine("hl7.fhir.r2.core#1.0.2", DEF_TX, FhirPublication.DSTU2, "1.0.2");
ve.setNoInvariantChecks(true);
CacheVerificationLogger logger = new CacheVerificationLogger();
ve.getContext().getTxClient().setLogger(logger);
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
OperationOutcome op = ve.validate(FhirFormat.JSON, TestingUtilities.loadTestResourceStream("validator", "observation102.json"), null);
if (!TestUtilities.silent)
for (OperationOutcomeIssueComponent iss : op.getIssue()) {
@ -206,7 +206,7 @@ public class ValidationEngineTests {
System.out.println("Test301: Validate observation301.xml against Core");
ValidationEngine ve = TestUtilities.getValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, FhirPublication.STU3, "3.0.2");
CacheVerificationLogger logger = new CacheVerificationLogger();
ve.getContext().getTxClient().setLogger(logger);
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
if (!TestUtilities.silent)
System.out.println(" .. load USCore");
OperationOutcome op = ve.validate(FhirFormat.XML, TestingUtilities.loadTestResourceStream("validator", "observation301.xml"), null);
@ -228,7 +228,7 @@ public class ValidationEngineTests {
System.out.println("Test301USCore: Validate patient300.xml against US-Core");
ValidationEngine ve = TestUtilities.getValidationEngine("hl7.fhir.r3.core#3.0.2", DEF_TX, FhirPublication.STU3, "3.0.2");
CacheVerificationLogger logger = new CacheVerificationLogger();
ve.getContext().getTxClient().setLogger(logger);
ve.getContext().getTxClientManager().getMasterClient().setLogger(logger);
IgLoader igLoader = new IgLoader(ve.getPcm(), ve.getContext(), ve.getVersion(), true);
if (!TestUtilities.silent)
System.out.println(" .. load USCore");

View File

@ -199,7 +199,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
currentVersion = version;
}
vCurr = CLONE ? new ValidationEngine(currentEngine) : currentEngine;
vCurr.getContext().getTxClient().setLogger(logger);
vCurr.getContext().getTxClientManager().getMasterClient().setLogger(logger);
igLoader = new IgLoader(vCurr.getPcm(), vCurr.getContext(), vCurr.getVersion(), true);
if (content.has("close-up")) {